le-app/app/components/account/SupporterBadges.tsx

105 lines
3.2 KiB
TypeScript
Raw Normal View History

// components/account/SupporterBadges.tsx
"use client";
import { useEffect, useState } from "react";
type SupportBadge = {
provider: "kofi" | "patreon" | "mighty" | string;
active: boolean; // currently entitled (status==="active" && renews_at >= now)
kind: "member" | "one_time" | "inactive";
label: string; // e.g., "Ko-fi • Bronze" or "Ko-fi Supporter"
tier?: string | null;
renews_at?: string | null;
started_at?: string | null;
};
function providerIcon(p: string) {
// Swap these for real icons later if you want
if (p === "kofi") return "☕";
if (p === "patreon") return "🅿️";
if (p === "mighty") return "💬";
return "🎖️";
}
export default function SupporterBadges({
email,
userId,
}: {
email?: string | null;
userId?: string | null;
}) {
const [badges, setBadges] = useState<SupportBadge[] | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
fetch("/api/support/badges", { cache: "no-store", credentials: "include" })
.then((r) => (r.ok ? r.json() : Promise.reject(r)))
.then((json) => setBadges(Array.isArray(json?.badges) ? json.badges : []))
.catch(async (e) => {
try {
const t = await e.text();
setError(t || String(e));
} catch {
setError(String(e));
}
});
}, [email, userId]);
if (error) {
return (
<div className="mt-4 rounded-lg border border-red-300/50 bg-red-50 p-3 text-sm text-red-800">
Couldnt load supporter badges.
</div>
);
}
if (!badges) {
return (
<div className="mt-4 animate-pulse rounded-lg border p-3 text-sm opacity-60">
Loading supporter badges
</div>
);
}
if (badges.length === 0) {
return (
<div className="mt-4 rounded-lg border p-3 text-sm opacity-70">
No supporter badges yet.
</div>
);
}
return (
<div className="mt-4 space-y-2">
<div className="text-sm font-medium opacity-80">Supporter Badges</div>
<div className="flex flex-wrap gap-2">
{badges.map((b, i) => (
<span
key={`${b.provider}-${i}`}
className={[
"inline-flex items-center gap-1 rounded-full border px-3 py-1 text-sm",
b.active
? "border-green-300 bg-green-50 text-green-900"
: b.kind === "one_time"
? "border-amber-300 bg-amber-50 text-amber-900"
: "border-slate-300 bg-slate-50 text-slate-700",
].join(" ")}
title={
b.active
? b.renews_at
? `Active • renews by ${new Date(b.renews_at).toLocaleDateString()}`
: "Active"
: b.kind === "one_time"
? "One-time support"
: "Inactive"
}
>
<span>{providerIcon(b.provider)}</span>
<span>{b.label}</span>
</span>
))}
</div>
</div>
);
}