makearmy-app/components/account/LinkStatus.tsx
makearmy 912cf71bb9 feat(support): Ko-fi end-to-end linking + badges (derive-on-read, no cron)
- Add Ko-fi webhook (/api/webhooks/kofi) with upsert by (provider, external_user_id)
  • Computes renews_at = timestamp + 1 calendar month + 1 day
  • Preserves first started_at; stores raw payload; canonicalizes by email when available
- Add Ko-fi claim flow
  • POST /api/support/kofi/claim/start — sends verification email via SMTP
  • GET  /api/support/kofi/claim/verify — finalizes link (sets app_user), redirects to /portal/account
  • POST /api/support/kofi/unlink — clears app_user on Ko-fi rows
- Add derive-on-read membership logic
  • /lib/memberships.ts — single source of truth for badges & “active” state
  • /api/support/badges — thin wrapper that returns per-provider badges
- Account UI
  • components/account/SupporterBadges.tsx — renders provider badges (Ko-fi now; extensible)
  • components/account/ConnectKofi.tsx — “Link Ko-fi” form (email → verify link)
  • components/account/LinkStatus.tsx — success/error banner on return
  • app/portal/account/AccountPanel.tsx — integrates badges, link panel, and banner
- Config/env
  • Requires: DIRECTUS_URL, DIRECTUS_TOKEN_ADMIN_SUPPORTER, KOFI_VERIFY_TOKEN
  • SMTP: SMTP_HOST, SMTP_PORT, SMTP_SECURE, SMTP_USER, SMTP_PASS, EMAIL_FROM
  • APP_ORIGIN used to build absolute verify URLs
- Misc
  • Fixed import to use @/lib/memberships
  • No cron required; UI derives active state via status === active && renews_at >= now

Refs: beta readiness for Ko-fi supporters
2025-10-19 17:51:04 -04:00

28 lines
793 B
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// /components/account/LinkStatus.tsx
"use client";
import { useSearchParams } from "next/navigation";
export default function LinkStatus() {
const sp = useSearchParams();
const linked = sp.get("linked");
if (linked !== "kofi") return null;
const isOk = sp.get("ok") === "1";
const isErr = sp.get("error") === "1";
if (!isOk && !isErr) return null;
return (
<div
className={[
"mb-3 rounded-md border p-3 text-sm",
isOk
? "border-emerald-300/50 bg-emerald-50 text-emerald-900"
: "border-red-300/50 bg-red-50 text-red-900",
].join(" ")}
>
{isOk ? "Ko-fi successfully linked to your account." : "Couldnt verify that Ko-fi link. Please try again."}
</div>
);
}