"use client"; import { useState } from "react"; import { formatDistanceToNow } from "date-fns"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { toast } from "@/lib/toast"; import { useRevokeInvitation, useResendInvitation, type Invitation, } from "@/hooks/use-api"; import { Copy, RefreshCw, Trash2 } from "lucide-react"; interface InvitationsGridProps { invitations: Invitation[]; onRefetch: () => void; } export function InvitationsGrid({ invitations, onRefetch, }: InvitationsGridProps) { const revokeInvitation = useRevokeInvitation(); const resendInvitation = useResendInvitation(); const [actioningId, setActioningId] = useState(null); const handleCopyUrl = async (url: string) => { try { await navigator.clipboard.writeText(url); toast.success("Invitation link copied to clipboard"); } catch (error) { toast.error("Failed to copy link"); } }; const handleRevoke = async (invitation: Invitation) => { if (!confirm(`Cancel invitation for ${invitation.emailAddress}?`)) { return; } setActioningId(invitation.id); try { await revokeInvitation.mutateAsync(invitation.id); toast.success("Invitation cancelled"); onRefetch(); } catch (error) { toast.error("Failed to cancel invitation"); } finally { setActioningId(null); } }; const handleResend = async (invitation: Invitation) => { setActioningId(invitation.id); try { await resendInvitation.mutateAsync(invitation.id); toast.success("Invitation resent"); onRefetch(); } catch (error) { toast.error("Failed to resend invitation"); } finally { setActioningId(null); } }; if (invitations.length === 0) { return ( No pending invitations ); } return (
{invitations.map((invitation) => { const role = invitation.publicMetadata?.role || "unknown"; const createdDate = new Date(invitation.createdAt); const isActioning = actioningId === invitation.id; return (

{invitation.emailAddress}

Pending {role}

Sent {formatDistanceToNow(createdDate, { addSuffix: true })}

{invitation.url && ( )}
); })}
); }