"use client"; import { useState } from "react"; import { invitesApi } from "../api"; import { PageLayout } from "../components/PageLayout"; import { useRequireAuth } from "../hooks/useRequireAuth"; import { useAsyncData } from "../hooks/useAsyncData"; import { components } from "../generated/api"; import constants from "../../../shared/constants.json"; import { Permission } from "../auth-context"; import { cardStyles, typographyStyles, badgeStyles, buttonStyles } from "../styles/shared"; // Use generated type from OpenAPI schema type Invite = components["schemas"]["UserInviteResponse"]; export default function InvitesPage() { const { user, isLoading, isAuthorized } = useRequireAuth({ requiredPermission: Permission.VIEW_OWN_INVITES, fallbackRedirect: "/admin/trades", }); const { data: invites = [], isLoading: isLoadingInvites } = useAsyncData( () => invitesApi.getInvites(), { enabled: !!user && isAuthorized, initialData: [], } ); const [copiedId, setCopiedId] = useState(null); const getInviteUrl = (identifier: string) => { if (typeof window !== "undefined") { return `${window.location.origin}/signup/${identifier}`; } return `/signup/${identifier}`; }; const copyToClipboard = async (invite: Invite) => { const url = getInviteUrl(invite.identifier); try { await navigator.clipboard.writeText(url); setCopiedId(invite.id); setTimeout(() => setCopiedId(null), 2000); } catch (err) { console.error("Failed to copy:", err); } }; const { READY, SPENT, REVOKED } = constants.inviteStatuses; const readyInvites = invites.filter((i) => i.status === READY); const spentInvites = invites.filter((i) => i.status === SPENT); const revokedInvites = invites.filter((i) => i.status === REVOKED); return (

My Invites

Share your invite codes with friends to let them join

{invites.length === 0 ? (

You don't have any invites yet.

Contact an admin if you need invite codes to share.

) : (
{/* Ready Invites */} {readyInvites.length > 0 && (

Available ({readyInvites.length})

Share these links with people you want to invite

{readyInvites.map((invite) => (
{invite.identifier}
))}
)} {/* Spent Invites */} {spentInvites.length > 0 && (

Used ({spentInvites.length})

{spentInvites.map((invite) => (
{invite.identifier}
Used by {invite.used_by_email}
))}
)} {/* Revoked Invites */} {revokedInvites.length > 0 && (

Revoked ({revokedInvites.length})

{revokedInvites.map((invite) => (
{invite.identifier}
Revoked
))}
)}
)}
); } // Page-specific styles const styles: Record = { pageCard: { ...cardStyles.card, width: "100%", maxWidth: "600px", }, emptyState: { textAlign: "center", padding: "2rem 0", }, emptyText: { fontFamily: "'DM Sans', system-ui, sans-serif", color: "rgba(255, 255, 255, 0.6)", fontSize: "1rem", margin: 0, }, emptyHint: { fontFamily: "'DM Sans', system-ui, sans-serif", color: "rgba(255, 255, 255, 0.4)", fontSize: "0.85rem", marginTop: "0.5rem", }, sections: { display: "flex", flexDirection: "column", gap: "2rem", }, section: { display: "flex", flexDirection: "column", gap: "0.75rem", }, inviteList: { display: "flex", flexDirection: "column", gap: "0.75rem", }, inviteCard: { background: "rgba(99, 102, 241, 0.1)", border: "1px solid rgba(99, 102, 241, 0.3)", borderRadius: "12px", padding: "1rem", display: "flex", justifyContent: "space-between", alignItems: "center", }, inviteCardSpent: { background: "rgba(34, 197, 94, 0.08)", border: "1px solid rgba(34, 197, 94, 0.2)", borderRadius: "12px", padding: "1rem", display: "flex", justifyContent: "space-between", alignItems: "center", }, inviteCardRevoked: { background: "rgba(239, 68, 68, 0.08)", border: "1px solid rgba(239, 68, 68, 0.2)", borderRadius: "12px", padding: "1rem", display: "flex", justifyContent: "space-between", alignItems: "center", opacity: 0.7, }, inviteCode: { fontFamily: "'DM Mono', monospace", fontSize: "0.95rem", color: "#fff", letterSpacing: "0.02em", }, inviteActions: { display: "flex", gap: "0.5rem", }, inviteeMeta: { display: "flex", alignItems: "center", gap: "0.5rem", }, inviteeEmail: { fontFamily: "'DM Sans', system-ui, sans-serif", fontSize: "0.8rem", color: "rgba(255, 255, 255, 0.6)", }, };