import React, { useState } from 'react'; import { Users, Mail, CheckCircle, AlertCircle, ChevronRight, UserPlus, Clock, ShieldCheck, RefreshCw } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import { Link } from 'react-router-dom'; import { usePartnersQuery } from '../../hooks/use-partners-query'; import { useInvitePartnerMutation } from '../../hooks/use-partners-mutation'; import PageHeader from '../../components/ui/PageHeader'; const STATUS_CONFIG: Record = { PENDING_ONBOARDING: { label: 'Pending Onboarding', color: 'text-ink-500', bg: 'bg-ink-50', border: 'border-ink-200', }, PENDING_APPROVAL: { label: 'Awaiting Approval', color: 'text-ink-0 bg-ink-900', bg: 'bg-ink-900', border: 'border-ink-800', }, APPROVED: { label: 'Active', color: 'text-ink-900 font-extrabold', bg: 'bg-ink-100', border: 'border-ink-300', }, }; const getStatusConfig = (status: string) => STATUS_CONFIG[status] ?? { label: status, color: 'text-ink-500', bg: 'bg-ink-100', border: 'border-ink-200', }; export const DirectoryPage: React.FC = () => { const [email, setEmail] = useState(''); const [inviteResult, setInviteResult] = useState<{ token?: string; error?: string } | null>(null); const [isInviteOpen, setIsInviteOpen] = useState(false); const [currentPage, setCurrentPage] = useState(1); const ITEMS_PER_PAGE = 5; const { data: partners = [], isLoading: loadingPartners, refetch: fetchPartners } = usePartnersQuery(); const inviteMutation = useInvitePartnerMutation(); const handleInvite = async (e: React.FormEvent) => { e.preventDefault(); setInviteResult(null); inviteMutation.mutate(email, { onSuccess: (data) => { setInviteResult({ token: data.token }); setEmail(''); }, onError: (err: any) => { setInviteResult({ error: err.response?.data?.error || 'Failed to send invite' }); } }); }; const counts = { total: partners.length, active: partners.filter(p => p.onboardingStatus === 'APPROVED').length, pendingOnboarding: partners.filter(p => p.onboardingStatus === 'PENDING_ONBOARDING').length, awaitingApproval: partners.filter(p => p.onboardingStatus === 'PENDING_APPROVAL').length, }; const statCards = [ { label: 'Total Partners', value: counts.total, icon: Users, accentClass: '' }, { label: 'Active', value: counts.active, icon: ShieldCheck, accentClass: '' }, { label: 'Pending Onboarding', value: counts.pendingOnboarding, icon: Clock, accentClass: '' }, ]; if (counts.awaitingApproval > 0) { statCards.push({ label: 'Awaiting Approval', value: counts.awaitingApproval, icon: AlertCircle, accentClass: 'border-ink-900/30 bg-ink-900/5 shadow-[0_0_15px_rgba(0,0,0,0.01)] border-dashed animate-pulse' }); } const totalItems = partners.length; const totalPages = Math.ceil(totalItems / ITEMS_PER_PAGE) || 1; const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; const endIndex = Math.min(startIndex + ITEMS_PER_PAGE, totalItems); const paginatedPartners = partners.slice(startIndex, endIndex); return (
} /> {/* Dynamic Real-time Approval Notification Banner */} {counts.awaitingApproval > 0 && (

Partner approvals pending

There are {counts.awaitingApproval} partners awaiting document review and access authorization.

Review Queue )} {/* Stats Row */}
0 ? 'grid-cols-2 md:grid-cols-4' : 'grid-cols-1 md:grid-cols-3'}`}> {statCards.map((stat, i) => (

{stat.label}

{stat.value}

))}
{/* Partner List Table (Full Width) */}
{loadingPartners ? ( ) : partners.length === 0 ? ( ) : ( paginatedPartners.map(partner => { const sc = getStatusConfig(partner.onboardingStatus); return ( ); }) )}
Partner Status MFA Joined

No partners yet

Use the invite button to add your first partner.

{partner.email.charAt(0).toUpperCase()}
{partner.email}
{sc.label} {partner.mfaEnabled ? ( Enabled ) : ( Disabled )} {new Date(partner.createdAt).toLocaleDateString()}
{/* Pagination Controls */} {totalItems > 0 && (

Showing {startIndex + 1}-{endIndex} of {totalItems} partners

{Array.from({ length: totalPages }).map((_, idx) => { const pageNum = idx + 1; return ( ); })}
)}
{/* Invite Modal Overlay */} {isInviteOpen && (
setIsInviteOpen(false)} className="fixed inset-0 bg-ink-900/40 backdrop-blur-sm" />

Invite Partner

Generate a secure invitation link for a new partner.

{!inviteResult?.token ? (
setEmail(e.target.value)} placeholder="partner@company.com" required className="w-full pl-9 pr-4 py-2 bg-ink-50 border border-ink-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-ink-900/10 text-ink-900 placeholder-ink-400 transition-all font-semibold" />
{inviteResult?.error && (

{inviteResult.error}

)}
) : (
Invite Created!

Send this secure link to the partner:

{window.location.origin}/invite?token={inviteResult.token}
)}
)}
); }; export default DirectoryPage;