import { useState } from 'react'; import { errorMessage } from '../../api/client'; import { Alert, AlertDialog, Badge, Button, Card, DataTable, EmptyState, FormField, Input, Spinner, useToast, } from '@cameleer/design-system'; import type { Column } from '@cameleer/design-system'; import { Plus, Users } from 'lucide-react'; import { useTenantTeam, useInviteTeamMember, useRemoveTeamMember, useResetTeamMemberPassword, useResetTeamMemberMfa, } from '../../api/tenant-hooks'; import styles from '../../styles/platform.module.css'; // DataTable requires T extends { id: string } interface TeamMember { id: string; name: string; email: string; role: string; } function toMember(raw: Record): TeamMember { return { id: String(raw['id'] ?? raw['userId'] ?? ''), name: String(raw['name'] ?? raw['username'] ?? '—'), email: String(raw['email'] ?? '—'), role: String(raw['role'] ?? raw['orgRole'] ?? '—'), }; } const ROLES = [ { id: 'owner', label: 'Owner' }, { id: 'operator', label: 'Operator' }, { id: 'viewer', label: 'Viewer' }, ]; function roleColor(role: string): 'primary' | 'success' | 'warning' | 'auto' { switch (role?.toLowerCase()) { case 'owner': return 'primary'; case 'operator': return 'success'; case 'viewer': return 'warning'; default: return 'auto'; } } export function TeamPage() { const { data: rawTeam, isLoading, isError } = useTenantTeam(); const inviteMember = useInviteTeamMember(); const removeMember = useRemoveTeamMember(); const resetPassword = useResetTeamMemberPassword(); const resetMfa = useResetTeamMemberMfa(); const { toast } = useToast(); const [showInvite, setShowInvite] = useState(false); const [inviteEmail, setInviteEmail] = useState(''); const [inviteRole, setInviteRole] = useState('viewer'); const [removeTarget, setRemoveTarget] = useState(null); const [pwTarget, setPwTarget] = useState(null); const [pwValue, setPwValue] = useState(''); const [mfaResetTarget, setMfaResetTarget] = useState(null); const team: TeamMember[] = (rawTeam ?? []).map(toMember).filter((m) => m.id !== ''); const columns: Column[] = [ { key: 'name', header: 'Name', render: (_v, row) => row.name, }, { key: 'email', header: 'Email', render: (_v, row) => ( {row.email} ), }, { key: 'role', header: 'Role', render: (_v, row) => , }, { key: 'id', header: 'Actions', render: (_v, row) => (
), }, ]; async function handleInvite(e: React.FormEvent) { e.preventDefault(); if (!inviteEmail.trim()) return; try { await inviteMember.mutateAsync({ email: inviteEmail.trim(), roleId: inviteRole }); toast({ title: `Invited ${inviteEmail}`, variant: 'success' }); setInviteEmail(''); setInviteRole('viewer'); setShowInvite(false); } catch (err) { toast({ title: 'Invite failed', description: errorMessage(err), variant: 'error' }); } } async function handleRemove() { if (!removeTarget) return; try { await removeMember.mutateAsync(removeTarget.id); toast({ title: `Removed ${removeTarget.name}`, variant: 'success' }); setRemoveTarget(null); } catch (err) { toast({ title: 'Remove failed', description: errorMessage(err), variant: 'error' }); setRemoveTarget(null); } } if (isLoading) { return (
); } if (isError) { return (
Could not fetch team members. Please refresh.
); } return (
{/* Header */}

Team

{/* Inline invite form */} {showInvite && (
setInviteEmail(e.target.value)} required />
)} {/* Team table */} {team.length === 0 ? ( } title="No team members yet" description="Invite colleagues to collaborate on this tenant." action={ } /> ) : ( )} {/* Remove confirmation dialog */} setRemoveTarget(null)} onConfirm={handleRemove} title="Remove Team Member" description={`Are you sure you want to remove "${removeTarget?.name ?? ''}" from the team? They will lose access immediately.`} confirmLabel="Remove" cancelLabel="Cancel" variant="danger" loading={removeMember.isPending} /> {/* Reset MFA inline form */} {mfaResetTarget && ( This will remove all MFA factors for this user. They will need to re-enroll if MFA is required.
)} {/* Reset password inline form */} {pwTarget && (
{ e.preventDefault(); if (pwValue.length < 8) { toast({ title: 'Password must be at least 8 characters', variant: 'error' }); return; } try { await resetPassword.mutateAsync({ userId: pwTarget.id, password: pwValue }); toast({ title: `Password reset for ${pwTarget.name}`, variant: 'success' }); setPwTarget(null); setPwValue(''); } catch (err) { toast({ title: 'Reset failed', description: errorMessage(err), variant: 'error' }); } }} style={{ display: 'flex', flexDirection: 'column', gap: 16 }} > setPwValue(e.target.value)} required minLength={8} />
)}
); }