"use client"; import Link from "next/link"; import { useQuery } from "@tanstack/react-query"; import { Flame, Heart, Sparkles, Trophy } from "lucide-react"; import { useState } from "react"; import { useAuth } from "@/lib/auth-store"; import { http, toUserError } from "@/lib/api"; import type { LeaderboardData, MeetingEvent } from "@/lib/types"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; import { ProfileCertificateSlot } from "@/components/profile-certificate-slot"; import { OwnerProfileHeader } from "@/components/profile-header"; import { ChallengeCards } from "@/components/challenge-cards"; import { AvatarStudio } from "@/components/avatar-studio"; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { formatFaNumber, formatJalaliStamp, formatStreakLabel } from "@/lib/utils"; export default function ProfilePage() { const user = useAuth((s) => s.user); const [studioOpen, setStudioOpen] = useState(false); const { data: lb, isError: lbError, error: lbErr } = useQuery({ queryKey: ["leaderboard"], queryFn: () => http.get("/api/leaderboard/weekly"), enabled: !!user, }); if (!user) { return (

برای مشاهدهٔ پروفایل وارد شوید.

); } return (
setStudioOpen(true)} />
} label="امتیاز کل" value={formatFaNumber(user.xp)} /> } label="قلب‌ها" value={formatFaNumber(user.hearts)} /> } label="زنجیرهٔ فعلی" value={formatStreakLabel(user.streakCurrent)} /> } label="بلندترین زنجیره" value={formatStreakLabel(user.streakLongest)} />

رتبهٔ لیگ این هفته

{lbError ? (

{toUserError(lbErr)}

) : lb?.me && lb.me.rank > 0 ? (

رتبه {formatFaNumber(lb.me.rank)} با {formatFaNumber(Math.round(lb.me.xp))} امتیاز هفتگی

) : lb ? (

هنوز در رتبه‌بندی این هفته نیستید

) : ( )}

چالش‌های فعال

گواهینامه

ویرایش آواتار setStudioOpen(false)} />
); } function ProfileActivity() { const activityQ = useQuery({ queryKey: ["me-activity"], queryFn: () => http.get<{ days: { day: string; count: number }[] }>("/api/me/activity"), }); const eventsQ = useQuery({ queryKey: ["events", "profile"], queryFn: () => http.get<{ events: MeetingEvent[] }>("/api/events"), }); return (

فعالیت

تاریخچهٔ ماهانه

هر بار یک ماه. پررنگ‌تر یعنی فعالیت بیشتر.

{activityQ.isPending ? ( ) : activityQ.isError ? (

{toUserError(activityQ.error, "بارگذاری فعالیت ممکن نشد")}

) : ( )}

رویدادها

{eventsQ.isPending ? (

در حال بارگذاری…

) : eventsQ.isError ? (

{toUserError(eventsQ.error, "بارگذاری رویدادها ممکن نشد")}

) : (eventsQ.data?.events ?? []).length === 0 ? (

رویدادی نیست.

) : (
    {(eventsQ.data?.events ?? []).map((ev) => (
  • {ev.title}
  • ))}
)}
); } function activityCountLabel(count: number) { if (count <= 0) return "بدون فعالیت"; return `${formatFaNumber(count)} فعالیت`; } const WEEKDAYS = ["ش", "ی", "د", "س", "چ", "پ", "ج"]; function ActivityGrid({ days }: { days: { day: string; count: number }[] }) { const [index, setIndex] = useState(0); const map = new Map(days.map((d) => [d.day.slice(0, 10), d.count])); const order: string[] = []; const months = new Map(); const today = new Date(); today.setHours(12, 0, 0, 0); for (let i = 119; i >= 0; i--) { const d = new Date(today); d.setDate(today.getDate() - i); const key = d.toISOString().slice(0, 10); const id = jalaliMonthId(d); if (!months.has(id)) { order.push(id); months.set(id, { label: jalaliMonth(d), cells: [] }); } months.get(id)!.cells.push({ day: key, count: map.get(key) ?? 0 }); } const newestFirst = [...order].reverse(); const safeIndex = Math.min(index, Math.max(newestFirst.length - 1, 0)); const id = newestFirst[safeIndex]; const month = id ? months.get(id) : undefined; if (!month || !id) return null; const pad = weekdayOffset(month.cells[0]?.day ?? ""); return (

{month.label}

{WEEKDAYS.map((name) => ( {name} ))} {Array.from({ length: pad }, (_, i) => ( ))} {month.cells.map((c) => { const date = jalaliDay(c.day); const countLabel = activityCountLabel(c.count); return ( ); })}
); } function weekdayOffset(day: string) { if (!day) return 0; const d = new Date(day + "T12:00:00"); return (d.getDay() + 1) % 7; } function jalaliMonthId(d: Date) { const parts = new Intl.DateTimeFormat("en-u-ca-persian", { year: "numeric", month: "2-digit" }).formatToParts(d); const year = parts.find((p) => p.type === "year")?.value ?? ""; const month = parts.find((p) => p.type === "month")?.value ?? ""; return `${year}-${month}`; } function jalaliMonth(d: Date) { const parts = new Intl.DateTimeFormat("fa-IR-u-ca-persian", { month: "long", year: "numeric" }).formatToParts(d); const month = parts.find((p) => p.type === "month")?.value ?? ""; const year = parts.find((p) => p.type === "year")?.value ?? ""; return `${month} ${year}`; } function jalaliDay(day: string) { return new Intl.DateTimeFormat("fa-IR-u-ca-persian", { year: "numeric", month: "short", day: "numeric" }).format( new Date(day + "T12:00:00"), ); } function StatCard({ icon, label, value }: { icon: React.ReactNode; label: string; value: string }) { return (
{icon}

{label}

{value}

); }