EventBuzz_Website/src/pages/RewardsPage.tsx

132 lines
5.5 KiB
TypeScript

import { useEffect, useState } from "react";
import { Trophy } from "lucide-react";
import { Link, useSearchParams } from "react-router-dom";
import { getVisitorEventRewardBalance, getVisitorRewardHistory } from "../lib/api";
import { getAttendeeToken } from "../lib/attendeeAuth";
import { HeaderMobile } from "../components/HeaderMobile";
import { MobileShell } from "../components/MobileShell";
import { readStoredVisitorContext } from "../types/visitorContext";
function historyLabel(source?: string, description?: string): string {
if (description && String(description).trim()) return String(description).trim();
const m: Record<string, string> = {
registration: "Registration",
question_ask: "Asked a question",
question_vote: "Question vote",
quiz_poll: "Quiz",
poll: "Poll or survey",
manual: "Adjustment",
feedback: "Feedback",
rating: "Rating",
quiz: "Quiz",
};
return m[source || ""] || source || "Points";
}
export function RewardsPage() {
const [searchParams] = useSearchParams();
const urlEventId = searchParams.get("event_id")?.trim() || "";
const ctx = readStoredVisitorContext();
const eventId = urlEventId || ctx?.eventId || "";
const [totalPoints, setTotalPoints] = useState<number | null>(null);
const [historyItems, setHistoryItems] = useState<
Awaited<ReturnType<typeof getVisitorRewardHistory>>["items"]
>([]);
const [historyLoading, setHistoryLoading] = useState(false);
useEffect(() => {
if (!eventId || !getAttendeeToken()) {
setTotalPoints(null);
setHistoryItems([]);
return;
}
let cancelled = false;
getVisitorEventRewardBalance(eventId)
.then((n) => {
if (!cancelled) setTotalPoints(Math.max(0, n));
})
.catch(() => {
if (!cancelled) setTotalPoints(0);
});
setHistoryLoading(true);
getVisitorRewardHistory(eventId, { page: 1, limit: 30 })
.then((r) => {
if (!cancelled) setHistoryItems(r.items);
})
.catch(() => {
if (!cancelled) setHistoryItems([]);
})
.finally(() => {
if (!cancelled) setHistoryLoading(false);
});
return () => {
cancelled = true;
};
}, [eventId]);
const displayTotal =
!getAttendeeToken() || !eventId ? "—" : totalPoints === null ? "…" : String(totalPoints);
return (
<MobileShell className="relative">
<HeaderMobile logoUrl={ctx?.event?.logoUrl} eventId={eventId || null} />
<div className="eb-gradient-auth flex flex-1 flex-col items-center px-6 pb-10 pt-6">
<div className="flex flex-col w-full max-w-[354px] items-center card-bg p-4 rounded-md">
<Trophy className="size-[68px] text-amber-300" strokeWidth={1.25} />
<h1 className="mt-1 font-inter text-xl font-semibold text-white">Your Rewards</h1>
<Link
to={eventId ? `/leaderboard?event_id=${encodeURIComponent(eventId)}` : "/leaderboard"}
className="mt-6 w-full max-w-[354px] rounded-full border border-[rgba(214,200,200,0.6)] bg-[var(--theme-button-color)] transition-colors duration-300 ease-in-out hover:bg-[var(--theme-button-color)]/90 py-3 text-center text-xs font-semibold uppercase tracking-wide text-white shadow-[0_4px_4px_rgba(0,0,0,0.15)]"
>
Leaderboard
</Link>
</div>
<div className="mt-6 w-full max-w-[354px] rounded-xl bg-white shadow-[0_8px_12px_6px_rgba(0,0,0,0.15),0_4px_4px_rgba(0,0,0,0.3)]">
<div className="p-4">
<div className="eb-rank-highlight flex flex-col items-center justify-center gap-2 rounded-3xl border-4 border-white py-4 text-white shadow-[0_9px_16px_rgba(0,0,0,0.14)]">
<p className="font-inter text-base font-semibold uppercase tracking-wide">Total Points</p>
<p className="font-inter text-5xl font-bold tabular-nums drop-shadow-[0_4px_4px_rgba(0,0,0,0.25)]">
{displayTotal}
</p>
</div>
<h2 className="mt-6 font-inter text-base font-semibold text-[#24282f]">Points history</h2>
<ul className="mt-3 flex flex-col gap-3">
{historyLoading && (
<li className="text-xs text-[rgba(36,40,47,0.67)]">Loading…</li>
)}
{!historyLoading && historyItems.length === 0 && (
<li className="text-xs text-[rgba(36,40,47,0.67)]">No transactions yet.</li>
)}
{!historyLoading &&
historyItems.map((row) => {
const signed = row.type === "redeemed" ? "-" : "+";
const cls =
row.type === "redeemed"
? "bg-[#fff0f0] text-[#c02424]"
: row.type === "earned"
? "bg-[#ebffee] text-[#009951]"
: "bg-[#f3f4f6] text-[#24282f]";
return (
<li
key={row._id}
className="flex gap-4 border-b border-[#ededed] pb-3 last:border-0"
>
<span className={`shrink-0 rounded px-3 py-2 text-xs font-medium shadow-sm ${cls}`}>
{signed}
{row.points} pts
</span>
<span className="text-xs leading-snug text-[rgba(36,40,47,0.67)]">
{historyLabel(row.source, row.description)}
</span>
</li>
);
})}
</ul>
</div>
</div>
</div>
</MobileShell>
);
}