108 lines
5.4 KiB
TypeScript
108 lines
5.4 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
|
|
interface DailyCheckinButtonProps {
|
|
initialStreak?: number;
|
|
hasCheckedInToday?: boolean;
|
|
onCheckin: () => Promise<{ success: boolean; points: number; streak: number }>;
|
|
}
|
|
|
|
export default function DailyCheckinButton({
|
|
initialStreak = 0,
|
|
hasCheckedInToday = false,
|
|
onCheckin
|
|
}: DailyCheckinButtonProps) {
|
|
const [streak, setStreak] = useState(initialStreak);
|
|
const [checkedIn, setCheckedIn] = useState(hasCheckedInToday);
|
|
const [loading, setLoading] = useState(false);
|
|
const [showSuccess, setShowSuccess] = useState(false);
|
|
const [earnedPoints, setEarnedPoints] = useState(0);
|
|
|
|
const handleCheckin = async () => {
|
|
if (checkedIn || loading) return;
|
|
|
|
setLoading(true);
|
|
try {
|
|
const result = await onCheckin();
|
|
if (result.success) {
|
|
setCheckedIn(true);
|
|
setStreak(result.streak);
|
|
setEarnedPoints(result.points);
|
|
setShowSuccess(true);
|
|
setTimeout(() => setShowSuccess(false), 3000);
|
|
}
|
|
} catch (error) {
|
|
console.error("Check-in failed:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="relative">
|
|
<div className="bg-gradient-to-br from-gray-800 to-gray-900 border-2 border-gray-700 rounded-2xl p-8 text-center">
|
|
<div className="mb-6">
|
|
<div className="inline-flex items-center gap-2 bg-orange-500/20 border border-orange-500/30 rounded-full px-4 py-2 mb-4">
|
|
<svg className="w-5 h-5 text-orange-400" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fillRule="evenodd" d="M12.395 2.553a1 1 0 00-1.45-.385c-.345.23-.614.558-.822.88-.214.33-.403.713-.57 1.116-.334.804-.614 1.768-.84 2.734a31.365 31.365 0 00-.613 3.58 2.64 2.64 0 01-.945-1.067c-.328-.68-.398-1.534-.398-2.654A1 1 0 005.05 6.05 6.981 6.981 0 003 11a7 7 0 1011.95-4.95c-.592-.591-.98-.985-1.348-1.467-.363-.476-.724-1.063-1.207-2.03zM12.12 15.12A3 3 0 017 13s.879.5 2.5.5c0-1 .5-4 1.25-4.5.5 1 .786 1.293 1.371 1.879A2.99 2.99 0 0113 13a2.99 2.99 0 01-.879 2.121z" clipRule="evenodd" />
|
|
</svg>
|
|
<span className="text-orange-400 font-bold text-lg">{streak} Day Streak</span>
|
|
</div>
|
|
<h2 className="text-3xl font-bold text-white mb-2">Daily Check-in</h2>
|
|
<p className="text-gray-400">Come back every day to build your streak!</p>
|
|
</div>
|
|
|
|
<button
|
|
onClick={handleCheckin}
|
|
disabled={checkedIn || loading}
|
|
className={`w-full py-6 rounded-xl font-bold text-xl transition-all duration-300 ${
|
|
checkedIn
|
|
? "bg-green-500/20 border-2 border-green-500 text-green-400 cursor-not-allowed"
|
|
: loading
|
|
? "bg-gray-700 text-gray-400 cursor-wait"
|
|
: "bg-gradient-to-r from-yellow-500 to-orange-500 text-white shadow-lg shadow-yellow-500/50 hover:shadow-yellow-500/70 hover:scale-105 transform"
|
|
}`}
|
|
>
|
|
{checkedIn ? (
|
|
<div className="flex items-center justify-center gap-2">
|
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
|
</svg>
|
|
<span>Checked In Today!</span>
|
|
</div>
|
|
) : loading ? (
|
|
"Checking In..."
|
|
) : (
|
|
<div className="flex items-center justify-center gap-2">
|
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
|
|
<path fillRule="evenodd" d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
|
</svg>
|
|
<span>Check In Now</span>
|
|
</div>
|
|
)}
|
|
</button>
|
|
|
|
<p className="text-gray-500 text-sm mt-4">
|
|
{checkedIn
|
|
? "Come back tomorrow to continue your streak!"
|
|
: "Earn points and extend your streak"}
|
|
</p>
|
|
</div>
|
|
|
|
{showSuccess && (
|
|
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
|
|
<div className="bg-green-500 text-white px-8 py-4 rounded-xl shadow-2xl animate-bounce">
|
|
<div className="flex items-center gap-2">
|
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
|
|
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
|
|
</svg>
|
|
<span className="font-bold text-lg">+{earnedPoints} Points!</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|