@burdenoff/microfe-workspaces 2026.731.1 → 2026.801.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/WorkspacesRoutes.js +1 -1
  2. package/dist/WorkspacesRoutes.js.map +1 -1
  3. package/dist/components/ProjectCard.js +1 -1
  4. package/dist/components/ProjectCard.js.map +1 -1
  5. package/dist/components/WorkspaceCard.js +1 -1
  6. package/dist/components/WorkspaceCard.js.map +1 -1
  7. package/dist/components/WorkspaceContextPathSync.js +1 -1
  8. package/dist/components/WorkspaceContextPathSync.js.map +1 -1
  9. package/dist/hooks/useScopedNavigation.js +1 -1
  10. package/dist/hooks/useScopedNavigation.js.map +1 -1
  11. package/dist/pages/ActivityListPage.js +1 -1
  12. package/dist/pages/ActivityListPage.js.map +1 -1
  13. package/dist/pages/CreateWorkspacePage.js +1 -1
  14. package/dist/pages/CreateWorkspacePage.js.map +1 -1
  15. package/dist/pages/InviteAcceptancePage.js +1 -1
  16. package/dist/pages/InviteAcceptancePage.js.map +1 -1
  17. package/dist/pages/MembersListPage.js +1 -1
  18. package/dist/pages/MembersListPage.js.map +1 -1
  19. package/dist/pages/PendingInvitesPage.js +1 -1
  20. package/dist/pages/PendingInvitesPage.js.map +1 -1
  21. package/dist/pages/ProjectDetailPage.js +1 -1
  22. package/dist/pages/ProjectDetailPage.js.map +1 -1
  23. package/dist/pages/ProjectMembersPage.js +1 -1
  24. package/dist/pages/ProjectMembersPage.js.map +1 -1
  25. package/dist/pages/ProjectsListPage.js +1 -1
  26. package/dist/pages/ProjectsListPage.js.map +1 -1
  27. package/dist/pages/WorkspaceDetailPage.js +1 -1
  28. package/dist/pages/WorkspaceDetailPage.js.map +1 -1
  29. package/dist/pages/WorkspaceInvitePage.js +1 -1
  30. package/dist/pages/WorkspaceInvitePage.js.map +1 -1
  31. package/dist/pages/WorkspaceSettingsPage.js +1 -1
  32. package/dist/pages/WorkspaceSettingsPage.js.map +1 -1
  33. package/dist/pages/WorkspacesListPage.js +1 -1
  34. package/dist/pages/WorkspacesListPage.js.map +1 -1
  35. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"InviteAcceptancePage.js","names":[],"sources":["../../src/pages/InviteAcceptancePage.tsx"],"sourcesContent":["import { useParams, useNavigate, useLocation } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport {\n GlassCard,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Button,\n Badge,\n Skeleton,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Mail,\n CheckCircle,\n XCircle,\n Clock,\n AlertTriangle,\n Ban,\n Home,\n RefreshCw,\n} from 'lucide-react';\nimport { useInvitation } from '../hooks/useWorkspaceMembers';\nimport { useAcceptInvitation, useRejectInvitation } from '../hooks/useMemberMutations';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport type { WorkspaceInvitation } from '../types';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport {\n getProjectDisplayNames,\n getRoleDisplayNames,\n getWorkspaceDisplayName,\n} from '../utils/invitationDisplay';\n\n/**\n * Page for accepting or rejecting workspace invitations\n * Accessible via /workspaces/invite/:invitationId\n */\nexport function InviteAcceptancePage() {\n useWorkspacesPageView('workspaces.invite');\n const { t } = useI18n();\n const { invitationId: routeInvitationId } = useParams<{ invitationId: string }>();\n const location = useLocation();\n const navigate = useNavigate();\n const { authToken } = useWorkspacesContext();\n const invitationId =\n routeInvitationId || location.pathname.split('/').filter(Boolean).at(-1) || '';\n const isAuthenticated = Boolean(authToken);\n\n const inviteLoginPath = invitationId\n ? `/auth/login?returnTo=${encodeURIComponent(`/workspaces/invite/${invitationId}`)}`\n : '/auth/login?returnTo=%2Fworkspaces%2Flist';\n\n // Fetch invitation details\n const {\n data: invitation,\n isLoading,\n error,\n refetch,\n } = useInvitation(invitationId || '', isAuthenticated);\n\n // Mutations\n const { mutate: acceptInvitation, isPending: isAccepting } = useAcceptInvitation();\n const { mutate: rejectInvitation, isPending: isRejecting } = useRejectInvitation();\n const workspaceDisplayName = invitation ? getWorkspaceDisplayName(invitation) : null;\n const roleDisplayNames = invitation ? getRoleDisplayNames(invitation) : [];\n const projectDisplayNames = invitation ? getProjectDisplayNames(invitation) : [];\n\n // Handler for accepting invitation\n const handleAccept = () => {\n if (!invitationId) return;\n\n acceptInvitation(invitationId, {\n onSuccess: (acceptedInvitation) => {\n // Redirect to workspace members page\n const workspaceId = acceptedInvitation.workspaceId || invitation?.workspaceId;\n if (workspaceId) {\n navigate(`/workspaces/${workspaceId}/members`);\n } else {\n navigate('/workspaces/list');\n }\n },\n onError: (error) => {\n console.error('Failed to accept invitation:', error);\n },\n });\n };\n\n if (!isAuthenticated) {\n return (\n <div className=\"min-h-screen flex items-center justify-center bg-bg-sunken/30 p-4\">\n <GlassCard treatment=\"glass\" className=\"max-w-xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-action-primary-bg/10 p-3\">\n <Mail className=\"text-primary\" size={24} />\n </div>\n <div>\n <CardTitle>\n {tWithFallback(\n t,\n 'inviteAcceptance.signInRequired',\n 'Sign in to view your invitation'\n )}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.signInDescription',\n 'Workspace invitations require an authenticated Burdenoff account before you can review or accept them.'\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'inviteAcceptance.signInHint',\n 'Use the same email address that received the invitation, then come back here to accept or reject it.'\n )}\n </p>\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n <Button asChild>\n <a href={inviteLoginPath}>\n {tWithFallback(t, 'inviteAcceptance.signInButton', 'Sign In')}\n </a>\n </Button>\n <Button variant=\"outline\" onClick={() => navigate('/workspaces/list')}>\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n </div>\n </CardContent>\n </GlassCard>\n </div>\n );\n }\n\n // Handler for rejecting invitation\n const handleReject = () => {\n if (!invitationId) return;\n\n rejectInvitation(invitationId, {\n onSuccess: () => {\n // Redirect to workspaces list\n navigate('/workspaces/list');\n },\n onError: (error) => {\n console.error('Failed to reject invitation:', error);\n },\n });\n };\n\n // Loading state\n if (isLoading) {\n return <LoadingSkeleton />;\n }\n\n // Error state (network error or other issues)\n if (error) {\n return <ErrorState error={error} onRetry={() => refetch()} />;\n }\n\n // Not found state\n if (!invitation) {\n return <NotFoundState />;\n }\n\n // Check invitation status and show appropriate UI\n switch (invitation.status) {\n case 'ACCEPTED':\n return <AlreadyAcceptedState invitation={invitation} />;\n case 'REJECTED':\n return <AlreadyRejectedState invitation={invitation} />;\n case 'EXPIRED':\n return <ExpiredState invitation={invitation} />;\n case 'CANCELLED':\n return <CancelledState invitation={invitation} />;\n case 'PENDING':\n // Check if invitation has expired based on expiresAt\n if (invitation.expiresAt && new Date(invitation.expiresAt) < new Date()) {\n return <ExpiredState invitation={invitation} />;\n }\n\n // Show main acceptance UI\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" glow className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-action-primary-bg/10\">\n <Mail className=\"text-primary\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(t, 'inviteAcceptance.pageTitle', 'Workspace Invitation')}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.pageSubtitle',\n \"You've been invited to join a workspace\"\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n <PagePurpose>\n {tWithFallback(\n t,\n 'inviteAcceptance.purpose',\n 'Someone has invited you to join their workspace. Review the details below — which workspace it is, the roles and projects you would receive, and when the invite expires — then accept to become a member or reject to decline.'\n )}\n </PagePurpose>\n\n {/* Invitation Details */}\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50\">\n <div>\n <p className=\"text-sm font-medium text-text-secondary\">\n {tWithFallback(t, 'inviteAcceptance.emailAddressLabel', 'Email Address')}\n </p>\n <p className=\"text-lg font-medium mt-1\">{invitation.email}</p>\n </div>\n\n <div>\n <p className=\"text-sm font-medium text-text-secondary\">\n {tWithFallback(t, 'inviteAcceptance.workspaceLabel', 'Workspace')}\n </p>\n <p className=\"text-lg font-medium mt-1 break-words\">{workspaceDisplayName}</p>\n {invitation.workspaceId ? (\n <p className=\"mt-1 font-mono text-xs text-text-secondary break-all\">\n {invitation.workspaceId}\n </p>\n ) : null}\n </div>\n\n {invitation.expiresAt && (\n <div>\n <p className=\"text-sm font-medium text-text-secondary\">\n {tWithFallback(t, 'inviteAcceptance.expiresLabel', 'Expires')}\n </p>\n <p className=\"text-lg font-medium mt-1 flex items-center gap-2\">\n <Clock size={16} className=\"text-text-secondary\" />\n {new Date(invitation.expiresAt).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n })}\n </p>\n </div>\n )}\n\n {roleDisplayNames.length > 0 && (\n <div>\n <p className=\"text-sm font-medium text-text-secondary mb-2\">\n {tWithFallback(t, 'inviteAcceptance.rolesLabel', 'Roles')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {roleDisplayNames.map((roleName, index) => (\n <Badge\n key={`${invitation.id}-${invitation.roleIds[index] ?? roleName}`}\n variant=\"secondary\"\n >\n {roleName}\n </Badge>\n ))}\n </div>\n </div>\n )}\n\n <div>\n <p className=\"text-sm font-medium text-text-secondary mb-2\">\n {tWithFallback(t, 'inviteAcceptance.projectsLabel', 'Projects')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {projectDisplayNames.length > 0 ? (\n projectDisplayNames.map((projectName, index) => (\n <Badge\n key={`${invitation.id}-project-${projectName}-${index}`}\n variant=\"outline\"\n >\n {projectName}\n </Badge>\n ))\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'inviteAcceptance.noProjects',\n 'No project memberships will be granted automatically with this invitation.'\n )}\n </p>\n )}\n </div>\n </div>\n\n <div>\n <p className=\"text-sm font-medium text-text-secondary\">\n {tWithFallback(t, 'inviteAcceptance.invitationIdLabel', 'Invitation ID')}\n </p>\n <p className=\"text-sm font-mono mt-1 text-text-secondary\">{invitation.id}</p>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex flex-col sm:flex-row gap-3 pt-4 border-t\">\n <Button\n onClick={handleAccept}\n disabled={isAccepting || isRejecting}\n className=\"flex-1\"\n size=\"lg\"\n >\n {isAccepting ? (\n <>\n <RefreshCw className=\"mr-2 size-4 animate-spin\" />\n {tWithFallback(t, 'inviteAcceptance.accepting', 'Accepting...')}\n </>\n ) : (\n <>\n <CheckCircle className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.acceptButton', 'Accept Invitation')}\n </>\n )}\n </Button>\n <Button\n variant=\"outline\"\n onClick={handleReject}\n disabled={isAccepting || isRejecting}\n className=\"flex-1\"\n size=\"lg\"\n >\n {isRejecting ? (\n <>\n <RefreshCw className=\"mr-2 size-4 animate-spin\" />\n {tWithFallback(t, 'inviteAcceptance.rejecting', 'Rejecting...')}\n </>\n ) : (\n <>\n <XCircle className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.rejectButton', 'Reject')}\n </>\n )}\n </Button>\n </div>\n </CardContent>\n </GlassCard>\n </div>\n );\n\n default:\n return <NotFoundState />;\n }\n}\n\n/**\n * Loading skeleton while fetching invitation\n */\nfunction LoadingSkeleton() {\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <Skeleton className=\"size-12 rounded-lg\" />\n <div className=\"flex-1 space-y-2\">\n <Skeleton className=\"h-6 w-48\" />\n <Skeleton className=\"h-4 w-64\" />\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50\">\n <div className=\"space-y-2\">\n <Skeleton className=\"h-4 w-24\" />\n <Skeleton className=\"h-6 w-48\" />\n </div>\n <div className=\"space-y-2\">\n <Skeleton className=\"h-4 w-24\" />\n <Skeleton className=\"h-6 w-64\" />\n </div>\n </div>\n <div className=\"flex gap-3 pt-4\">\n <Skeleton className=\"h-12 flex-1\" />\n <Skeleton className=\"h-12 flex-1\" />\n </div>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n\n/**\n * Error state with retry option\n */\nfunction ErrorState({ error, onRetry }: { error: Error; onRetry: () => void }) {\n const { t } = useI18n();\n const navigate = useNavigate();\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-status-error-bg/10\">\n <AlertTriangle className=\"text-status-error-text\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(t, 'inviteAcceptance.errorTitle', 'Error Loading Invitation')}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.errorDescription',\n \"We couldn't load this invitation. Please try again.\"\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex gap-3 p-4 rounded-lg border border-status-error-border/50 bg-status-error-bg/10\">\n <AlertTriangle className=\"size-5 text-status-error-text flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium text-status-error-text\">\n {tWithFallback(t, 'inviteAcceptance.errorDetailsLabel', 'Error Details')}\n </p>\n <p className=\"text-sm mt-1 text-status-error-text/90\">{error.message}</p>\n </div>\n </div>\n\n <div className=\"flex gap-3\">\n <Button onClick={onRetry} className=\"flex-1\">\n <RefreshCw className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.tryAgain', 'Try Again')}\n </Button>\n <Button\n variant=\"outline\"\n onClick={() => navigate('/workspaces/list')}\n className=\"flex-1\"\n >\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.goToWorkspaces', 'Go to Workspaces')}\n </Button>\n </div>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n\n/**\n * Not found state (invalid invitation ID)\n */\nfunction NotFoundState() {\n const { t } = useI18n();\n const navigate = useNavigate();\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-bg-sunken\">\n <AlertTriangle className=\"text-text-secondary\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(t, 'inviteAcceptance.notFoundTitle', 'Invitation Not Found')}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.notFoundDescription',\n 'This invitation link is invalid or has been removed'\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex gap-3 p-4 rounded-lg border border-border-subtle bg-bg-sunken/50\">\n <AlertTriangle className=\"size-5 text-text-secondary flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium\">\n {tWithFallback(t, 'inviteAcceptance.invalidLinkHeading', 'Invalid Invitation Link')}\n </p>\n <p className=\"text-sm mt-1 text-text-secondary\">\n {tWithFallback(\n t,\n 'inviteAcceptance.invalidLinkBody',\n \"The invitation you're trying to access doesn't exist. It may have been deleted or the link is incorrect.\"\n )}\n </p>\n </div>\n </div>\n\n <Button onClick={() => navigate('/workspaces/list')} className=\"w-full\">\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.goToWorkspaces', 'Go to Workspaces')}\n </Button>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n\n/**\n * Already accepted state\n */\nfunction AlreadyAcceptedState({ invitation }: { invitation: WorkspaceInvitation }) {\n const { t } = useI18n();\n const navigate = useNavigate();\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-status-success/10 transition-colors\">\n <CheckCircle className=\"text-status-success\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(\n t,\n 'inviteAcceptance.alreadyAcceptedTitle',\n 'Invitation Already Accepted'\n )}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.alreadyAcceptedDescription',\n 'This invitation has already been accepted'\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex gap-3 p-4 rounded-lg border border-status-success/30 bg-status-success/10 transition-colors\">\n <CheckCircle className=\"size-5 text-status-success flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium text-status-success\">\n {tWithFallback(t, 'inviteAcceptance.alreadyMember', \"You're already a member\")}\n </p>\n <p className=\"text-sm mt-1 text-status-success/80\">\n {tWithFallback(t, 'inviteAcceptance.alreadyAcceptedPrefix', 'This invitation to ')}\n <span className=\"font-medium\">{invitation.email}</span>\n {tWithFallback(\n t,\n 'inviteAcceptance.alreadyAcceptedSuffix',\n ' has already been accepted.'\n )}\n </p>\n </div>\n </div>\n\n <Button\n onClick={() => navigate(`/workspaces/${invitation.workspaceId}/members`)}\n className=\"w-full\"\n >\n {tWithFallback(t, 'inviteAcceptance.goToWorkspace', 'Go to Workspace')}\n </Button>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n\n/**\n * Already rejected state\n */\nfunction AlreadyRejectedState({ invitation }: { invitation: WorkspaceInvitation }) {\n const { t } = useI18n();\n const navigate = useNavigate();\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-bg-sunken\">\n <XCircle className=\"text-text-secondary\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(t, 'inviteAcceptance.rejectedTitle', 'Invitation Rejected')}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.rejectedDescription',\n \"You've already declined this invitation\"\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex gap-3 p-4 rounded-lg border border-border-subtle bg-bg-sunken/50\">\n <XCircle className=\"size-5 text-text-secondary flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium\">\n {tWithFallback(t, 'inviteAcceptance.invitationDeclined', 'Invitation Declined')}\n </p>\n <p className=\"text-sm mt-1 text-text-secondary\">\n {tWithFallback(t, 'inviteAcceptance.rejectedPrefix', 'This invitation to ')}\n <span className=\"font-medium\">{invitation.email}</span>\n {tWithFallback(t, 'inviteAcceptance.rejectedSuffix', ' was previously rejected.')}\n </p>\n </div>\n </div>\n\n <Button onClick={() => navigate('/workspaces/list')} className=\"w-full\">\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.goToWorkspaces', 'Go to Workspaces')}\n </Button>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n\n/**\n * Expired state\n */\nfunction ExpiredState({ invitation }: { invitation: WorkspaceInvitation }) {\n const { t } = useI18n();\n const navigate = useNavigate();\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-status-warning/10 transition-colors\">\n <Clock className=\"text-status-warning\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(t, 'inviteAcceptance.expiredTitle', 'Invitation Expired')}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.expiredDescription',\n 'This invitation has expired'\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex gap-3 p-4 rounded-lg border border-status-warning/30 bg-status-warning/10 transition-colors\">\n <Clock className=\"size-5 text-status-warning flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium text-status-warning\">\n {tWithFallback(t, 'inviteAcceptance.expiredHeading', 'Invitation No Longer Valid')}\n </p>\n <p className=\"text-sm mt-1 text-status-warning/80\">\n {tWithFallback(t, 'inviteAcceptance.expiredPrefix', 'This invitation to ')}\n <span className=\"font-medium\">{invitation.email}</span>\n {tWithFallback(t, 'inviteAcceptance.expiredMiddle', ' expired on ')}\n {invitation.expiresAt &&\n new Date(invitation.expiresAt).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n })}\n .\n </p>\n <p className=\"text-sm mt-2 text-status-warning/80\">\n {tWithFallback(\n t,\n 'inviteAcceptance.expiredContactAdmin',\n 'Please contact the workspace admin to request a new invitation.'\n )}\n </p>\n </div>\n </div>\n\n <Button onClick={() => navigate('/workspaces/list')} className=\"w-full\">\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.goToWorkspaces', 'Go to Workspaces')}\n </Button>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n\n/**\n * Cancelled state\n */\nfunction CancelledState({ invitation }: { invitation: WorkspaceInvitation }) {\n const { t } = useI18n();\n const navigate = useNavigate();\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-status-error-bg/10\">\n <Ban className=\"text-status-error-text\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(t, 'inviteAcceptance.cancelledTitle', 'Invitation Cancelled')}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.cancelledDescription',\n 'This invitation has been cancelled by the sender'\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex gap-3 p-4 rounded-lg border border-status-error-border/50 bg-status-error-bg/10\">\n <Ban className=\"size-5 text-status-error-text flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium text-status-error-text\">\n {tWithFallback(\n t,\n 'inviteAcceptance.cancelledHeading',\n 'Invitation No Longer Available'\n )}\n </p>\n <p className=\"text-sm mt-1 text-status-error-text/90\">\n {tWithFallback(t, 'inviteAcceptance.cancelledPrefix', 'This invitation to ')}\n <span className=\"font-medium\">{invitation.email}</span>\n {tWithFallback(\n t,\n 'inviteAcceptance.cancelledSuffix',\n ' was cancelled by the workspace admin and is no longer valid.'\n )}\n </p>\n </div>\n </div>\n\n <Button onClick={() => navigate('/workspaces/list')} className=\"w-full\">\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.goToWorkspaces', 'Go to Workspaces')}\n </Button>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"InviteAcceptancePage.js","names":[],"sources":["../../src/pages/InviteAcceptancePage.tsx"],"sourcesContent":["import { useParams, useNavigate, useLocation } from 'react-router';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport {\n GlassCard,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Button,\n Badge,\n Skeleton,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Mail,\n CheckCircle,\n XCircle,\n Clock,\n AlertTriangle,\n Ban,\n Home,\n RefreshCw,\n} from 'lucide-react';\nimport { useInvitation } from '../hooks/useWorkspaceMembers';\nimport { useAcceptInvitation, useRejectInvitation } from '../hooks/useMemberMutations';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport type { WorkspaceInvitation } from '../types';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport {\n getProjectDisplayNames,\n getRoleDisplayNames,\n getWorkspaceDisplayName,\n} from '../utils/invitationDisplay';\n\n/**\n * Page for accepting or rejecting workspace invitations\n * Accessible via /workspaces/invite/:invitationId\n */\nexport function InviteAcceptancePage() {\n useWorkspacesPageView('workspaces.invite');\n const { t } = useI18n();\n const { invitationId: routeInvitationId } = useParams<{ invitationId: string }>();\n const location = useLocation();\n const navigate = useNavigate();\n const { authToken } = useWorkspacesContext();\n const invitationId =\n routeInvitationId || location.pathname.split('/').filter(Boolean).at(-1) || '';\n const isAuthenticated = Boolean(authToken);\n\n const inviteLoginPath = invitationId\n ? `/auth/login?returnTo=${encodeURIComponent(`/workspaces/invite/${invitationId}`)}`\n : '/auth/login?returnTo=%2Fworkspaces%2Flist';\n\n // Fetch invitation details\n const {\n data: invitation,\n isLoading,\n error,\n refetch,\n } = useInvitation(invitationId || '', isAuthenticated);\n\n // Mutations\n const { mutate: acceptInvitation, isPending: isAccepting } = useAcceptInvitation();\n const { mutate: rejectInvitation, isPending: isRejecting } = useRejectInvitation();\n const workspaceDisplayName = invitation ? getWorkspaceDisplayName(invitation) : null;\n const roleDisplayNames = invitation ? getRoleDisplayNames(invitation) : [];\n const projectDisplayNames = invitation ? getProjectDisplayNames(invitation) : [];\n\n // Handler for accepting invitation\n const handleAccept = () => {\n if (!invitationId) return;\n\n acceptInvitation(invitationId, {\n onSuccess: (acceptedInvitation) => {\n // Redirect to workspace members page\n const workspaceId = acceptedInvitation.workspaceId || invitation?.workspaceId;\n if (workspaceId) {\n navigate(`/workspaces/${workspaceId}/members`);\n } else {\n navigate('/workspaces/list');\n }\n },\n onError: (error) => {\n console.error('Failed to accept invitation:', error);\n },\n });\n };\n\n if (!isAuthenticated) {\n return (\n <div className=\"min-h-screen flex items-center justify-center bg-bg-sunken/30 p-4\">\n <GlassCard treatment=\"glass\" className=\"max-w-xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-action-primary-bg/10 p-3\">\n <Mail className=\"text-primary\" size={24} />\n </div>\n <div>\n <CardTitle>\n {tWithFallback(\n t,\n 'inviteAcceptance.signInRequired',\n 'Sign in to view your invitation'\n )}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.signInDescription',\n 'Workspace invitations require an authenticated Burdenoff account before you can review or accept them.'\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'inviteAcceptance.signInHint',\n 'Use the same email address that received the invitation, then come back here to accept or reject it.'\n )}\n </p>\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n <Button asChild>\n <a href={inviteLoginPath}>\n {tWithFallback(t, 'inviteAcceptance.signInButton', 'Sign In')}\n </a>\n </Button>\n <Button variant=\"outline\" onClick={() => navigate('/workspaces/list')}>\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n </div>\n </CardContent>\n </GlassCard>\n </div>\n );\n }\n\n // Handler for rejecting invitation\n const handleReject = () => {\n if (!invitationId) return;\n\n rejectInvitation(invitationId, {\n onSuccess: () => {\n // Redirect to workspaces list\n navigate('/workspaces/list');\n },\n onError: (error) => {\n console.error('Failed to reject invitation:', error);\n },\n });\n };\n\n // Loading state\n if (isLoading) {\n return <LoadingSkeleton />;\n }\n\n // Error state (network error or other issues)\n if (error) {\n return <ErrorState error={error} onRetry={() => refetch()} />;\n }\n\n // Not found state\n if (!invitation) {\n return <NotFoundState />;\n }\n\n // Check invitation status and show appropriate UI\n switch (invitation.status) {\n case 'ACCEPTED':\n return <AlreadyAcceptedState invitation={invitation} />;\n case 'REJECTED':\n return <AlreadyRejectedState invitation={invitation} />;\n case 'EXPIRED':\n return <ExpiredState invitation={invitation} />;\n case 'CANCELLED':\n return <CancelledState invitation={invitation} />;\n case 'PENDING':\n // Check if invitation has expired based on expiresAt\n if (invitation.expiresAt && new Date(invitation.expiresAt) < new Date()) {\n return <ExpiredState invitation={invitation} />;\n }\n\n // Show main acceptance UI\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" glow className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-action-primary-bg/10\">\n <Mail className=\"text-primary\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(t, 'inviteAcceptance.pageTitle', 'Workspace Invitation')}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.pageSubtitle',\n \"You've been invited to join a workspace\"\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n <PagePurpose>\n {tWithFallback(\n t,\n 'inviteAcceptance.purpose',\n 'Someone has invited you to join their workspace. Review the details below — which workspace it is, the roles and projects you would receive, and when the invite expires — then accept to become a member or reject to decline.'\n )}\n </PagePurpose>\n\n {/* Invitation Details */}\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50\">\n <div>\n <p className=\"text-sm font-medium text-text-secondary\">\n {tWithFallback(t, 'inviteAcceptance.emailAddressLabel', 'Email Address')}\n </p>\n <p className=\"text-lg font-medium mt-1\">{invitation.email}</p>\n </div>\n\n <div>\n <p className=\"text-sm font-medium text-text-secondary\">\n {tWithFallback(t, 'inviteAcceptance.workspaceLabel', 'Workspace')}\n </p>\n <p className=\"text-lg font-medium mt-1 break-words\">{workspaceDisplayName}</p>\n {invitation.workspaceId ? (\n <p className=\"mt-1 font-mono text-xs text-text-secondary break-all\">\n {invitation.workspaceId}\n </p>\n ) : null}\n </div>\n\n {invitation.expiresAt && (\n <div>\n <p className=\"text-sm font-medium text-text-secondary\">\n {tWithFallback(t, 'inviteAcceptance.expiresLabel', 'Expires')}\n </p>\n <p className=\"text-lg font-medium mt-1 flex items-center gap-2\">\n <Clock size={16} className=\"text-text-secondary\" />\n {new Date(invitation.expiresAt).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n })}\n </p>\n </div>\n )}\n\n {roleDisplayNames.length > 0 && (\n <div>\n <p className=\"text-sm font-medium text-text-secondary mb-2\">\n {tWithFallback(t, 'inviteAcceptance.rolesLabel', 'Roles')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {roleDisplayNames.map((roleName, index) => (\n <Badge\n key={`${invitation.id}-${invitation.roleIds[index] ?? roleName}`}\n variant=\"secondary\"\n >\n {roleName}\n </Badge>\n ))}\n </div>\n </div>\n )}\n\n <div>\n <p className=\"text-sm font-medium text-text-secondary mb-2\">\n {tWithFallback(t, 'inviteAcceptance.projectsLabel', 'Projects')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {projectDisplayNames.length > 0 ? (\n projectDisplayNames.map((projectName, index) => (\n <Badge\n key={`${invitation.id}-project-${projectName}-${index}`}\n variant=\"outline\"\n >\n {projectName}\n </Badge>\n ))\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'inviteAcceptance.noProjects',\n 'No project memberships will be granted automatically with this invitation.'\n )}\n </p>\n )}\n </div>\n </div>\n\n <div>\n <p className=\"text-sm font-medium text-text-secondary\">\n {tWithFallback(t, 'inviteAcceptance.invitationIdLabel', 'Invitation ID')}\n </p>\n <p className=\"text-sm font-mono mt-1 text-text-secondary\">{invitation.id}</p>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex flex-col sm:flex-row gap-3 pt-4 border-t\">\n <Button\n onClick={handleAccept}\n disabled={isAccepting || isRejecting}\n className=\"flex-1\"\n size=\"lg\"\n >\n {isAccepting ? (\n <>\n <RefreshCw className=\"mr-2 size-4 animate-spin\" />\n {tWithFallback(t, 'inviteAcceptance.accepting', 'Accepting...')}\n </>\n ) : (\n <>\n <CheckCircle className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.acceptButton', 'Accept Invitation')}\n </>\n )}\n </Button>\n <Button\n variant=\"outline\"\n onClick={handleReject}\n disabled={isAccepting || isRejecting}\n className=\"flex-1\"\n size=\"lg\"\n >\n {isRejecting ? (\n <>\n <RefreshCw className=\"mr-2 size-4 animate-spin\" />\n {tWithFallback(t, 'inviteAcceptance.rejecting', 'Rejecting...')}\n </>\n ) : (\n <>\n <XCircle className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.rejectButton', 'Reject')}\n </>\n )}\n </Button>\n </div>\n </CardContent>\n </GlassCard>\n </div>\n );\n\n default:\n return <NotFoundState />;\n }\n}\n\n/**\n * Loading skeleton while fetching invitation\n */\nfunction LoadingSkeleton() {\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <Skeleton className=\"size-12 rounded-lg\" />\n <div className=\"flex-1 space-y-2\">\n <Skeleton className=\"h-6 w-48\" />\n <Skeleton className=\"h-4 w-64\" />\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-6\">\n <div className=\"space-y-4 p-4 rounded-lg bg-bg-sunken/50\">\n <div className=\"space-y-2\">\n <Skeleton className=\"h-4 w-24\" />\n <Skeleton className=\"h-6 w-48\" />\n </div>\n <div className=\"space-y-2\">\n <Skeleton className=\"h-4 w-24\" />\n <Skeleton className=\"h-6 w-64\" />\n </div>\n </div>\n <div className=\"flex gap-3 pt-4\">\n <Skeleton className=\"h-12 flex-1\" />\n <Skeleton className=\"h-12 flex-1\" />\n </div>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n\n/**\n * Error state with retry option\n */\nfunction ErrorState({ error, onRetry }: { error: Error; onRetry: () => void }) {\n const { t } = useI18n();\n const navigate = useNavigate();\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-status-error-bg/10\">\n <AlertTriangle className=\"text-status-error-text\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(t, 'inviteAcceptance.errorTitle', 'Error Loading Invitation')}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.errorDescription',\n \"We couldn't load this invitation. Please try again.\"\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex gap-3 p-4 rounded-lg border border-status-error-border/50 bg-status-error-bg/10\">\n <AlertTriangle className=\"size-5 text-status-error-text flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium text-status-error-text\">\n {tWithFallback(t, 'inviteAcceptance.errorDetailsLabel', 'Error Details')}\n </p>\n <p className=\"text-sm mt-1 text-status-error-text/90\">{error.message}</p>\n </div>\n </div>\n\n <div className=\"flex gap-3\">\n <Button onClick={onRetry} className=\"flex-1\">\n <RefreshCw className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.tryAgain', 'Try Again')}\n </Button>\n <Button\n variant=\"outline\"\n onClick={() => navigate('/workspaces/list')}\n className=\"flex-1\"\n >\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.goToWorkspaces', 'Go to Workspaces')}\n </Button>\n </div>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n\n/**\n * Not found state (invalid invitation ID)\n */\nfunction NotFoundState() {\n const { t } = useI18n();\n const navigate = useNavigate();\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-bg-sunken\">\n <AlertTriangle className=\"text-text-secondary\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(t, 'inviteAcceptance.notFoundTitle', 'Invitation Not Found')}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.notFoundDescription',\n 'This invitation link is invalid or has been removed'\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex gap-3 p-4 rounded-lg border border-border-subtle bg-bg-sunken/50\">\n <AlertTriangle className=\"size-5 text-text-secondary flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium\">\n {tWithFallback(t, 'inviteAcceptance.invalidLinkHeading', 'Invalid Invitation Link')}\n </p>\n <p className=\"text-sm mt-1 text-text-secondary\">\n {tWithFallback(\n t,\n 'inviteAcceptance.invalidLinkBody',\n \"The invitation you're trying to access doesn't exist. It may have been deleted or the link is incorrect.\"\n )}\n </p>\n </div>\n </div>\n\n <Button onClick={() => navigate('/workspaces/list')} className=\"w-full\">\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.goToWorkspaces', 'Go to Workspaces')}\n </Button>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n\n/**\n * Already accepted state\n */\nfunction AlreadyAcceptedState({ invitation }: { invitation: WorkspaceInvitation }) {\n const { t } = useI18n();\n const navigate = useNavigate();\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-status-success/10 transition-colors\">\n <CheckCircle className=\"text-status-success\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(\n t,\n 'inviteAcceptance.alreadyAcceptedTitle',\n 'Invitation Already Accepted'\n )}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.alreadyAcceptedDescription',\n 'This invitation has already been accepted'\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex gap-3 p-4 rounded-lg border border-status-success/30 bg-status-success/10 transition-colors\">\n <CheckCircle className=\"size-5 text-status-success flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium text-status-success\">\n {tWithFallback(t, 'inviteAcceptance.alreadyMember', \"You're already a member\")}\n </p>\n <p className=\"text-sm mt-1 text-status-success/80\">\n {tWithFallback(t, 'inviteAcceptance.alreadyAcceptedPrefix', 'This invitation to ')}\n <span className=\"font-medium\">{invitation.email}</span>\n {tWithFallback(\n t,\n 'inviteAcceptance.alreadyAcceptedSuffix',\n ' has already been accepted.'\n )}\n </p>\n </div>\n </div>\n\n <Button\n onClick={() => navigate(`/workspaces/${invitation.workspaceId}/members`)}\n className=\"w-full\"\n >\n {tWithFallback(t, 'inviteAcceptance.goToWorkspace', 'Go to Workspace')}\n </Button>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n\n/**\n * Already rejected state\n */\nfunction AlreadyRejectedState({ invitation }: { invitation: WorkspaceInvitation }) {\n const { t } = useI18n();\n const navigate = useNavigate();\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-bg-sunken\">\n <XCircle className=\"text-text-secondary\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(t, 'inviteAcceptance.rejectedTitle', 'Invitation Rejected')}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.rejectedDescription',\n \"You've already declined this invitation\"\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex gap-3 p-4 rounded-lg border border-border-subtle bg-bg-sunken/50\">\n <XCircle className=\"size-5 text-text-secondary flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium\">\n {tWithFallback(t, 'inviteAcceptance.invitationDeclined', 'Invitation Declined')}\n </p>\n <p className=\"text-sm mt-1 text-text-secondary\">\n {tWithFallback(t, 'inviteAcceptance.rejectedPrefix', 'This invitation to ')}\n <span className=\"font-medium\">{invitation.email}</span>\n {tWithFallback(t, 'inviteAcceptance.rejectedSuffix', ' was previously rejected.')}\n </p>\n </div>\n </div>\n\n <Button onClick={() => navigate('/workspaces/list')} className=\"w-full\">\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.goToWorkspaces', 'Go to Workspaces')}\n </Button>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n\n/**\n * Expired state\n */\nfunction ExpiredState({ invitation }: { invitation: WorkspaceInvitation }) {\n const { t } = useI18n();\n const navigate = useNavigate();\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-status-warning/10 transition-colors\">\n <Clock className=\"text-status-warning\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(t, 'inviteAcceptance.expiredTitle', 'Invitation Expired')}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.expiredDescription',\n 'This invitation has expired'\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex gap-3 p-4 rounded-lg border border-status-warning/30 bg-status-warning/10 transition-colors\">\n <Clock className=\"size-5 text-status-warning flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium text-status-warning\">\n {tWithFallback(t, 'inviteAcceptance.expiredHeading', 'Invitation No Longer Valid')}\n </p>\n <p className=\"text-sm mt-1 text-status-warning/80\">\n {tWithFallback(t, 'inviteAcceptance.expiredPrefix', 'This invitation to ')}\n <span className=\"font-medium\">{invitation.email}</span>\n {tWithFallback(t, 'inviteAcceptance.expiredMiddle', ' expired on ')}\n {invitation.expiresAt &&\n new Date(invitation.expiresAt).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n })}\n .\n </p>\n <p className=\"text-sm mt-2 text-status-warning/80\">\n {tWithFallback(\n t,\n 'inviteAcceptance.expiredContactAdmin',\n 'Please contact the workspace admin to request a new invitation.'\n )}\n </p>\n </div>\n </div>\n\n <Button onClick={() => navigate('/workspaces/list')} className=\"w-full\">\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.goToWorkspaces', 'Go to Workspaces')}\n </Button>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n\n/**\n * Cancelled state\n */\nfunction CancelledState({ invitation }: { invitation: WorkspaceInvitation }) {\n const { t } = useI18n();\n const navigate = useNavigate();\n return (\n <div className=\"min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30\">\n <GlassCard treatment=\"glass\" className=\"max-w-2xl w-full\">\n <CardHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-status-error-bg/10\">\n <Ban className=\"text-status-error-text\" size={24} />\n </div>\n <div className=\"flex-1\">\n <CardTitle>\n {tWithFallback(t, 'inviteAcceptance.cancelledTitle', 'Invitation Cancelled')}\n </CardTitle>\n <CardDescription>\n {tWithFallback(\n t,\n 'inviteAcceptance.cancelledDescription',\n 'This invitation has been cancelled by the sender'\n )}\n </CardDescription>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex gap-3 p-4 rounded-lg border border-status-error-border/50 bg-status-error-bg/10\">\n <Ban className=\"size-5 text-status-error-text flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium text-status-error-text\">\n {tWithFallback(\n t,\n 'inviteAcceptance.cancelledHeading',\n 'Invitation No Longer Available'\n )}\n </p>\n <p className=\"text-sm mt-1 text-status-error-text/90\">\n {tWithFallback(t, 'inviteAcceptance.cancelledPrefix', 'This invitation to ')}\n <span className=\"font-medium\">{invitation.email}</span>\n {tWithFallback(\n t,\n 'inviteAcceptance.cancelledSuffix',\n ' was cancelled by the workspace admin and is no longer valid.'\n )}\n </p>\n </div>\n </div>\n\n <Button onClick={() => navigate('/workspaces/list')} className=\"w-full\">\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'inviteAcceptance.goToWorkspaces', 'Go to Workspaces')}\n </Button>\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -13,7 +13,7 @@ import { useEffect as o, useMemo as s, useState as c } from "react";
13
13
  import { AlertCircle as ce, RefreshCw as le, Search as ue, UserPlus as l, Users as de } from "lucide-react";
14
14
  import { jsx as u, jsxs as d } from "react/jsx-runtime";
15
15
  import { Badge as f, Button as p, IllustratedEmptyState as m, Input as fe, PageContainer as pe, PagePurpose as me, Skeleton as h } from "@burdenoff/fe-libs/ui";
16
- import { useNavigate as he, useParams as ge, useSearchParams as _e } from "react-router-dom";
16
+ import { useNavigate as he, useParams as ge, useSearchParams as _e } from "react-router";
17
17
  import { useDebounce as ve } from "@burdenoff/fe-libs/shared/hooks";
18
18
  import { PermissionButton as g } from "@burdenoff/fe-libs/shared/components";
19
19
  import { useI18n as ye } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
@@ -1 +1 @@
1
- {"version":3,"file":"MembersListPage.js","names":[],"sources":["../../src/pages/MembersListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useNavigate, useParams, useSearchParams } from 'react-router-dom';\nimport { Users, UserPlus, Search, AlertCircle, RefreshCw } from 'lucide-react';\nimport {\n Button,\n Input,\n Skeleton,\n Badge,\n PageContainer,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useActorProfiles, type ActorInput, type ActorProfileMap } from '@burdenoff/fe-libs/shared';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { WORKSPACE_PERMISSIONS } from '../constants/permissions';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { useWorkspaceMembers, useWorkspaceInvitations } from '../hooks/useWorkspaceMembers';\nimport { useRemoveUserFromWorkspace, useResendInvitation } from '../hooks/useMemberMutations';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { MemberListItem } from '../components/MemberListItem';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\n\nexport function MembersListPage() {\n const { t } = useI18n();\n\n const {\n memberFilters,\n setMemberFilters,\n selectedMemberIds,\n toggleMemberSelection,\n clearMemberSelection,\n } = useWorkspacesStore();\n const navigate = useNavigate();\n const { workspaceId: contextWorkspaceId } = useWorkspacesContext();\n const { basePath = '/workspaces' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { workspaceId: urlWorkspaceId } = useParams<{ workspaceId: string }>();\n\n // Prefer URL workspaceId over context (user navigated to specific workspace).\n // WorkspaceContextPathSync keeps this URL segment in step with the active context\n // on a context switch, so the override can't point at a stale workspace.\n // (BOFF-2805 follow-up)\n const workspaceId = urlWorkspaceId || contextWorkspaceId;\n\n useWorkspacesPageView('workspaces.members', { workspaceId, requireParams: true });\n\n const [searchValue, setSearchValue] = useState(memberFilters.search || '');\n const debouncedSearch = useDebounce(searchValue, 500);\n\n // Update filters when debounced search changes\n useEffect(() => {\n setMemberFilters({ search: debouncedSearch });\n }, [debouncedSearch, setMemberFilters]);\n\n // Fetch members and invitations\n const {\n data: membersData,\n isLoading: isLoadingMembers,\n error: membersError,\n } = useWorkspaceMembers(workspaceId || '');\n\n const { data: invitationsData, isLoading: isLoadingInvitations } = useWorkspaceInvitations(\n workspaceId || '',\n 'PENDING'\n );\n\n const { mutate: resendInvitation, isPending: isResending } = useResendInvitation();\n const { mutate: removeUserFromWorkspace, isPending: isRemovingMembers } =\n useRemoveUserFromWorkspace();\n const [resendingId, setResendingId] = useState<string | null>(null);\n const [selectedInvitationIds, setSelectedInvitationIds] = useState<Set<string>>(new Set());\n\n const handleResendInvitation = (invitationId: string) => {\n setResendingId(invitationId);\n resendInvitation(invitationId, {\n onSuccess: () => {\n setResendingId(null);\n },\n onError: (error) => {\n console.error('Failed to resend invitation:', error);\n setResendingId(null);\n },\n });\n };\n\n const members = useMemo(() => membersData?.items || [], [membersData?.items]);\n const pendingInvitations = useMemo(() => invitationsData?.items || [], [invitationsData?.items]);\n const isLoading = isLoadingMembers || isLoadingInvitations;\n const hasMembers = members.length > 0;\n const hasPendingInvitations = pendingInvitations.length > 0;\n\n // Resolve every member's userId to displayName + email via the shared\n // ActorProfile cache so search can match human-friendly fields instead of\n // raw UUIDs.\n const { apiGatewayUrl, authToken, organizationId } = useWorkspacesContext();\n const memberActors = useMemo<ActorInput[]>(\n () => members.map((m) => ({ actorId: m.userId, actorType: 'user' })),\n [members]\n );\n const profileMap: ActorProfileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n workspaceId,\n });\n\n // Filter members by resolved identity (displayName, email) — fall back to\n // raw userId only if the profile hasn't resolved yet so search still works\n // during the brief loading window.\n const filteredMembers = members.filter((member) => {\n if (!debouncedSearch) return true;\n const needle = debouncedSearch.toLowerCase();\n\n // Primary: the shared ActorProfile cache — canonical, works for every\n // member regardless of how they joined.\n const profile = profileMap[member.userId];\n if (profile) {\n if (profile.displayName.toLowerCase().includes(needle)) return true;\n if (profile.email?.toLowerCase().includes(needle)) return true;\n }\n\n // Fallback: the denormalized identity surfaced by the backend through\n // `WorkspaceMember.context.metadata` (populated from\n // `WorkspaceMember.email` for members who joined via invitation — PR #66).\n // Available synchronously, so search still matches during the brief window\n // before the ActorProfile lookup resolves.\n //\n // The metadata field is typed `Record<string, unknown>`, so we MUST guard\n // every read with `typeof === 'string'`. A non-string value (e.g. a number,\n // null, or a nested object accidentally surfaced by a future backend\n // mapper) would otherwise crash the page on `.toLowerCase()`. Reviewed:\n // PR #24 codex (P2).\n const meta = member.context?.metadata;\n const metaEmail = typeof meta?.email === 'string' ? meta.email : '';\n const metaDisplayRaw =\n typeof meta?.displayName === 'string'\n ? meta.displayName\n : typeof meta?.name === 'string'\n ? meta.name\n : '';\n if (metaEmail.toLowerCase().includes(needle)) return true;\n if (metaDisplayRaw.toLowerCase().includes(needle)) return true;\n\n // Last resort: raw userId — keeps the previous behaviour for members with\n // neither a resolved profile nor a denormalized identity yet.\n return member.userId.toLowerCase().includes(needle);\n });\n\n const selectedMembers = useMemo(\n () => filteredMembers.filter((member) => selectedMemberIds.has(member.id)),\n [filteredMembers, selectedMemberIds]\n );\n\n const selectedInvitations = useMemo(\n () => pendingInvitations.filter((invitation) => selectedInvitationIds.has(invitation.id)),\n [pendingInvitations, selectedInvitationIds]\n );\n\n const allVisibleMembersSelected =\n filteredMembers.length > 0 &&\n filteredMembers.every((member) => selectedMemberIds.has(member.id));\n const partiallySelectedMembers =\n filteredMembers.some((member) => selectedMemberIds.has(member.id)) &&\n !allVisibleMembersSelected;\n\n const allVisibleInvitationsSelected =\n pendingInvitations.length > 0 &&\n pendingInvitations.every((invitation) => selectedInvitationIds.has(invitation.id));\n const partiallySelectedInvitations =\n pendingInvitations.some((invitation) => selectedInvitationIds.has(invitation.id)) &&\n !allVisibleInvitationsSelected;\n\n // Clear selection on unmount\n useEffect(() => {\n return () => clearMemberSelection();\n }, [clearMemberSelection]);\n\n // Redirect to the canonical workspace invite page when arriving through\n // older members deep links that used ?invite=true.\n const [searchParams, setSearchParams] = useSearchParams();\n useEffect(() => {\n if (searchParams.get('invite') === 'true' && workspaceId) {\n const next = new URLSearchParams(searchParams);\n next.delete('invite');\n setSearchParams(next, { replace: true });\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/invite`), { replace: true });\n }\n }, [searchParams, setSearchParams, workspaceId, navigate, withCurrentSearch, basePath]);\n\n const {\n canListMembers,\n canInviteMember,\n canRemoveMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <Skeleton className=\"h-64\" />\n </div>\n );\n }\n\n if (!canListMembers && !canInviteMember) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'membersList.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'membersList.noPermissionToViewMembers',\n \"You don't have permission to view workspace members.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const handleInviteMember = () => {\n if (workspaceId) {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/invite`));\n }\n };\n\n const toggleAllMembers = (checked: boolean) => {\n filteredMembers.forEach((member) => {\n const isSelected = selectedMemberIds.has(member.id);\n if (checked !== isSelected) {\n toggleMemberSelection(member.id);\n }\n });\n };\n\n const toggleInvitationSelection = (invitationId: string, checked: boolean) => {\n setSelectedInvitationIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(invitationId);\n } else {\n next.delete(invitationId);\n }\n return next;\n });\n };\n\n const toggleAllInvitations = (checked: boolean) => {\n setSelectedInvitationIds((prev) => {\n const next = new Set(prev);\n pendingInvitations.forEach((invitation) => {\n if (checked) {\n next.add(invitation.id);\n } else {\n next.delete(invitation.id);\n }\n });\n return next;\n });\n };\n\n const handleBulkRemoveMembers = async () => {\n if (selectedMembers.length === 0) return;\n void nativeImpact('medium');\n const message = `${tWithFallback(t, 'membersList.bulkRemoveConfirmPrefix', 'Remove')} ${selectedMembers.length} ${\n selectedMembers.length === 1\n ? tWithFallback(t, 'membersList.bulkRemoveConfirmSingular', 'selected member')\n : tWithFallback(t, 'membersList.bulkRemoveConfirmPlural', 'selected members')\n } ${tWithFallback(t, 'membersList.bulkRemoveConfirmSuffix', 'from this workspace?')}`;\n const native = await nativeConfirm({\n title: 'Remove members',\n message,\n okButtonTitle: 'Remove',\n cancelButtonTitle: 'Cancel',\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) return;\n\n selectedMembers.forEach((member) => {\n removeUserFromWorkspace(member.userId);\n });\n clearMemberSelection();\n };\n\n const handleBulkResendInvitations = () => {\n if (selectedInvitations.length === 0) return;\n selectedInvitations.forEach((invitation) => {\n resendInvitation(invitation.id, {\n onSuccess: () => {\n setSelectedInvitationIds((prev) => {\n const next = new Set(prev);\n next.delete(invitation.id);\n return next;\n });\n },\n });\n });\n };\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'membersList.pageTitle', 'Members')}\n </h1>\n <p className=\"text-text-secondary text-sm sm:text-base\">\n {tWithFallback(\n t,\n 'membersList.pageSubtitle',\n 'Manage workspace members and invitations'\n )}\n </p>\n </div>\n\n <div className=\"flex gap-2\">\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.MEMBER_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'membersList.noPermissionToInvite',\n \"You don't have permission to invite members\"\n )}\n >\n <Button onClick={handleInviteMember} disabled={!workspaceId}>\n <UserPlus className=\"size-4 sm:mr-2\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, 'membersList.inviteMember', 'Invite Member')}\n </span>\n </Button>\n </PermissionButton>\n </div>\n </div>\n\n <PagePurpose>\n {tWithFallback(\n t,\n 'membersList.purpose',\n 'Members are the people who belong to this workspace. From here you invite teammates, see who has accepted, resend or track invitations that are still pending, and remove people who no longer need access. Search by name or email to find someone quickly.'\n )}\n </PagePurpose>\n\n {/* Search & Stats */}\n <div className=\"flex flex-col sm:flex-row gap-3 sm:gap-4\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <Input\n placeholder={tWithFallback(t, 'membersList.searchPlaceholder', 'Search members...')}\n value={searchValue}\n onChange={(e) => setSearchValue(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n\n {hasPendingInvitations && (\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\" className=\"text-sm\">\n {pendingInvitations.length}{' '}\n {pendingInvitations.length !== 1\n ? tWithFallback(t, 'membersList.pendingInvitations', 'Pending Invitations')\n : tWithFallback(t, 'membersList.pendingInvitation', 'Pending Invitation')}\n </Badge>\n </div>\n )}\n </div>\n\n {filteredMembers.length > 0 && (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={allVisibleMembersSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelectedMembers && !allVisibleMembersSelected;\n }\n }}\n onChange={(event) => toggleAllMembers(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'membersList.selectAllMembersAriaLabel',\n 'Select all workspace members'\n )}\n className=\"size-4 rounded border-border-subtle text-primary\"\n />\n <span>\n {tWithFallback(\n t,\n 'membersList.selectAllVisibleMembers',\n 'Select all visible members'\n )}\n </span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">\n {selectedMemberIds.size} {tWithFallback(t, 'membersList.selected', 'selected')}\n </span>\n {selectedMemberIds.size > 0 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearMemberSelection}>\n {tWithFallback(t, 'membersList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {canRemoveMember && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkRemoveMembers}\n disabled={selectedMembers.length === 0 || isRemovingMembers}\n className=\"text-status-error-text\"\n >\n {tWithFallback(t, 'membersList.removeSelected', 'Remove Selected')}\n {selectedMembers.length > 0 ? ` (${selectedMembers.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n )}\n\n {/* Loading State */}\n {isLoading && (\n <div className=\"space-y-3\">\n {[...Array(5)].map((_, i) => (\n <Skeleton key={i} className=\"h-20 w-full\" />\n ))}\n </div>\n )}\n\n {/* Error State */}\n {!isLoading && membersError && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-status-error-bg/10 p-6 mb-4\">\n <AlertCircle size={48} className=\"text-status-error-text\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'membersList.failedToLoad', 'Failed to Load Members')}\n </h3>\n <p className=\"text-text-secondary max-w-sm\">\n {membersError instanceof Error\n ? membersError.message\n : tWithFallback(\n t,\n 'membersList.errorLoadingMembers',\n 'An error occurred while loading members'\n )}\n </p>\n </div>\n )}\n\n {/* Empty State - No Workspace */}\n {!isLoading && !membersError && !workspaceId && (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title={tWithFallback(t, 'membersList.noWorkspaceSelected', 'No Workspace Selected')}\n description={tWithFallback(\n t,\n 'membersList.selectWorkspacePrompt',\n 'Select a workspace to view and manage its members'\n )}\n />\n )}\n\n {/* Empty State - No Members */}\n {!isLoading && !membersError && workspaceId && !hasMembers && (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title={tWithFallback(t, 'membersList.noMembersYet', 'No Members Yet')}\n description={tWithFallback(\n t,\n 'membersList.noMembersPrompt',\n 'Start building your team by inviting members to this workspace'\n )}\n action={\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.MEMBER_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'membersList.noPermissionToInvite',\n \"You don't have permission to invite members\"\n )}\n >\n <Button onClick={handleInviteMember}>\n <UserPlus className=\"mr-2 size-4\" />\n {tWithFallback(t, 'membersList.inviteFirstMember', 'Invite Your First Member')}\n </Button>\n </PermissionButton>\n }\n />\n )}\n\n {/* Empty State - No Search Results */}\n {!isLoading &&\n !membersError &&\n workspaceId &&\n hasMembers &&\n filteredMembers.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tWithFallback(t, 'membersList.noMembersFound', 'No Members Found')}\n description={tWithFallback(\n t,\n 'membersList.noMembersFoundPrompt',\n 'No members match your search criteria. Try adjusting your search.'\n )}\n />\n )}\n\n {/* Members List */}\n {!isLoading && filteredMembers.length > 0 && (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'membersList.activeMembers', 'Active Members')} (\n {filteredMembers.length})\n </h2>\n </div>\n <div className=\"space-y-2\">\n {filteredMembers.map((member) => (\n <MemberListItem\n key={member.id}\n member={member}\n selected={selectedMemberIds.has(member.id)}\n onToggleSelect={(checked) => {\n const isSelected = selectedMemberIds.has(member.id);\n if (checked !== isSelected) {\n toggleMemberSelection(member.id);\n }\n }}\n canRemove={canRemoveMember}\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Pending Invitations Section - Now at bottom */}\n {!isLoading && hasPendingInvitations && (\n <div className=\"space-y-3\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'membersList.pendingInvitationsHeader', 'Pending Invitations')}\n </h2>\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={allVisibleInvitationsSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate =\n partiallySelectedInvitations && !allVisibleInvitationsSelected;\n }\n }}\n onChange={(event) => toggleAllInvitations(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'membersList.selectAllInvitationsAriaLabel',\n 'Select all pending invitations'\n )}\n className=\"size-4 rounded border-border-subtle text-primary\"\n />\n <span>\n {tWithFallback(t, 'membersList.selectAllInvitations', 'Select all invitations')}\n </span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">\n {selectedInvitationIds.size}{' '}\n {tWithFallback(t, 'membersList.selected', 'selected')}\n </span>\n {selectedInvitationIds.size > 0 && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setSelectedInvitationIds(new Set())}\n >\n {tWithFallback(t, 'membersList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {canInviteMember && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkResendInvitations}\n disabled={selectedInvitations.length === 0 || isResending}\n >\n {tWithFallback(t, 'membersList.resendSelected', 'Resend Selected')}\n {selectedInvitations.length > 0 ? ` (${selectedInvitations.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n <div className=\"space-y-2\">\n {pendingInvitations.map((invitation) => (\n <div\n key={invitation.id}\n className=\"flex items-center justify-between p-4 border rounded-lg bg-bg-surface\"\n >\n <div className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={selectedInvitationIds.has(invitation.id)}\n onChange={(event) =>\n toggleInvitationSelection(invitation.id, event.target.checked)\n }\n aria-label={`${tWithFallback(t, 'membersList.selectInvitationAriaLabel', 'Select invitation')} ${invitation.email}`}\n className=\"size-4 rounded border-border-subtle text-primary\"\n />\n <div className=\"size-10 rounded-full bg-status-warning/10 flex items-center justify-center transition-colors\">\n <Users className=\"size-5 text-status-warning\" />\n </div>\n <div>\n <p className=\"font-medium text-sm\">{invitation.email}</p>\n <p className=\"text-xs text-text-secondary\">\n {tWithFallback(t, 'membersList.invited', 'Invited')}{' '}\n {new Date(invitation.createdAt).toLocaleDateString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.MEMBER_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'membersList.noPermissionToResend',\n \"You don't have permission to resend invitations\"\n )}\n >\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleResendInvitation(invitation.id)}\n disabled={isResending && resendingId === invitation.id}\n >\n <RefreshCw\n className={`size-4 mr-1 ${isResending && resendingId === invitation.id ? 'animate-spin' : ''}`}\n />\n {isResending && resendingId === invitation.id\n ? tWithFallback(t, 'membersList.sending', 'Sending...')\n : tWithFallback(t, 'membersList.resend', 'Resend')}\n </Button>\n </PermissionButton>\n <Badge\n variant=\"outline\"\n className=\"bg-status-warning/10 text-status-warning border-status-warning/30 transition-colors\"\n >\n {tWithFallback(t, 'membersList.pending', 'Pending')}\n </Badge>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n </PageContainer>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MembersListPage.js","names":[],"sources":["../../src/pages/MembersListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useNavigate, useParams, useSearchParams } from 'react-router';\nimport { Users, UserPlus, Search, AlertCircle, RefreshCw } from 'lucide-react';\nimport {\n Button,\n Input,\n Skeleton,\n Badge,\n PageContainer,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useActorProfiles, type ActorInput, type ActorProfileMap } from '@burdenoff/fe-libs/shared';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { PermissionButton } from '@burdenoff/fe-libs/shared/components';\nimport { WORKSPACE_PERMISSIONS } from '../constants/permissions';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { useWorkspaceMembers, useWorkspaceInvitations } from '../hooks/useWorkspaceMembers';\nimport { useRemoveUserFromWorkspace, useResendInvitation } from '../hooks/useMemberMutations';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { MemberListItem } from '../components/MemberListItem';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\n\nexport function MembersListPage() {\n const { t } = useI18n();\n\n const {\n memberFilters,\n setMemberFilters,\n selectedMemberIds,\n toggleMemberSelection,\n clearMemberSelection,\n } = useWorkspacesStore();\n const navigate = useNavigate();\n const { workspaceId: contextWorkspaceId } = useWorkspacesContext();\n const { basePath = '/workspaces' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { workspaceId: urlWorkspaceId } = useParams<{ workspaceId: string }>();\n\n // Prefer URL workspaceId over context (user navigated to specific workspace).\n // WorkspaceContextPathSync keeps this URL segment in step with the active context\n // on a context switch, so the override can't point at a stale workspace.\n // (BOFF-2805 follow-up)\n const workspaceId = urlWorkspaceId || contextWorkspaceId;\n\n useWorkspacesPageView('workspaces.members', { workspaceId, requireParams: true });\n\n const [searchValue, setSearchValue] = useState(memberFilters.search || '');\n const debouncedSearch = useDebounce(searchValue, 500);\n\n // Update filters when debounced search changes\n useEffect(() => {\n setMemberFilters({ search: debouncedSearch });\n }, [debouncedSearch, setMemberFilters]);\n\n // Fetch members and invitations\n const {\n data: membersData,\n isLoading: isLoadingMembers,\n error: membersError,\n } = useWorkspaceMembers(workspaceId || '');\n\n const { data: invitationsData, isLoading: isLoadingInvitations } = useWorkspaceInvitations(\n workspaceId || '',\n 'PENDING'\n );\n\n const { mutate: resendInvitation, isPending: isResending } = useResendInvitation();\n const { mutate: removeUserFromWorkspace, isPending: isRemovingMembers } =\n useRemoveUserFromWorkspace();\n const [resendingId, setResendingId] = useState<string | null>(null);\n const [selectedInvitationIds, setSelectedInvitationIds] = useState<Set<string>>(new Set());\n\n const handleResendInvitation = (invitationId: string) => {\n setResendingId(invitationId);\n resendInvitation(invitationId, {\n onSuccess: () => {\n setResendingId(null);\n },\n onError: (error) => {\n console.error('Failed to resend invitation:', error);\n setResendingId(null);\n },\n });\n };\n\n const members = useMemo(() => membersData?.items || [], [membersData?.items]);\n const pendingInvitations = useMemo(() => invitationsData?.items || [], [invitationsData?.items]);\n const isLoading = isLoadingMembers || isLoadingInvitations;\n const hasMembers = members.length > 0;\n const hasPendingInvitations = pendingInvitations.length > 0;\n\n // Resolve every member's userId to displayName + email via the shared\n // ActorProfile cache so search can match human-friendly fields instead of\n // raw UUIDs.\n const { apiGatewayUrl, authToken, organizationId } = useWorkspacesContext();\n const memberActors = useMemo<ActorInput[]>(\n () => members.map((m) => ({ actorId: m.userId, actorType: 'user' })),\n [members]\n );\n const profileMap: ActorProfileMap = useActorProfiles(memberActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n workspaceId,\n });\n\n // Filter members by resolved identity (displayName, email) — fall back to\n // raw userId only if the profile hasn't resolved yet so search still works\n // during the brief loading window.\n const filteredMembers = members.filter((member) => {\n if (!debouncedSearch) return true;\n const needle = debouncedSearch.toLowerCase();\n\n // Primary: the shared ActorProfile cache — canonical, works for every\n // member regardless of how they joined.\n const profile = profileMap[member.userId];\n if (profile) {\n if (profile.displayName.toLowerCase().includes(needle)) return true;\n if (profile.email?.toLowerCase().includes(needle)) return true;\n }\n\n // Fallback: the denormalized identity surfaced by the backend through\n // `WorkspaceMember.context.metadata` (populated from\n // `WorkspaceMember.email` for members who joined via invitation — PR #66).\n // Available synchronously, so search still matches during the brief window\n // before the ActorProfile lookup resolves.\n //\n // The metadata field is typed `Record<string, unknown>`, so we MUST guard\n // every read with `typeof === 'string'`. A non-string value (e.g. a number,\n // null, or a nested object accidentally surfaced by a future backend\n // mapper) would otherwise crash the page on `.toLowerCase()`. Reviewed:\n // PR #24 codex (P2).\n const meta = member.context?.metadata;\n const metaEmail = typeof meta?.email === 'string' ? meta.email : '';\n const metaDisplayRaw =\n typeof meta?.displayName === 'string'\n ? meta.displayName\n : typeof meta?.name === 'string'\n ? meta.name\n : '';\n if (metaEmail.toLowerCase().includes(needle)) return true;\n if (metaDisplayRaw.toLowerCase().includes(needle)) return true;\n\n // Last resort: raw userId — keeps the previous behaviour for members with\n // neither a resolved profile nor a denormalized identity yet.\n return member.userId.toLowerCase().includes(needle);\n });\n\n const selectedMembers = useMemo(\n () => filteredMembers.filter((member) => selectedMemberIds.has(member.id)),\n [filteredMembers, selectedMemberIds]\n );\n\n const selectedInvitations = useMemo(\n () => pendingInvitations.filter((invitation) => selectedInvitationIds.has(invitation.id)),\n [pendingInvitations, selectedInvitationIds]\n );\n\n const allVisibleMembersSelected =\n filteredMembers.length > 0 &&\n filteredMembers.every((member) => selectedMemberIds.has(member.id));\n const partiallySelectedMembers =\n filteredMembers.some((member) => selectedMemberIds.has(member.id)) &&\n !allVisibleMembersSelected;\n\n const allVisibleInvitationsSelected =\n pendingInvitations.length > 0 &&\n pendingInvitations.every((invitation) => selectedInvitationIds.has(invitation.id));\n const partiallySelectedInvitations =\n pendingInvitations.some((invitation) => selectedInvitationIds.has(invitation.id)) &&\n !allVisibleInvitationsSelected;\n\n // Clear selection on unmount\n useEffect(() => {\n return () => clearMemberSelection();\n }, [clearMemberSelection]);\n\n // Redirect to the canonical workspace invite page when arriving through\n // older members deep links that used ?invite=true.\n const [searchParams, setSearchParams] = useSearchParams();\n useEffect(() => {\n if (searchParams.get('invite') === 'true' && workspaceId) {\n const next = new URLSearchParams(searchParams);\n next.delete('invite');\n setSearchParams(next, { replace: true });\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/invite`), { replace: true });\n }\n }, [searchParams, setSearchParams, workspaceId, navigate, withCurrentSearch, basePath]);\n\n const {\n canListMembers,\n canInviteMember,\n canRemoveMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <Skeleton className=\"h-64\" />\n </div>\n );\n }\n\n if (!canListMembers && !canInviteMember) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'membersList.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'membersList.noPermissionToViewMembers',\n \"You don't have permission to view workspace members.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const handleInviteMember = () => {\n if (workspaceId) {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/invite`));\n }\n };\n\n const toggleAllMembers = (checked: boolean) => {\n filteredMembers.forEach((member) => {\n const isSelected = selectedMemberIds.has(member.id);\n if (checked !== isSelected) {\n toggleMemberSelection(member.id);\n }\n });\n };\n\n const toggleInvitationSelection = (invitationId: string, checked: boolean) => {\n setSelectedInvitationIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(invitationId);\n } else {\n next.delete(invitationId);\n }\n return next;\n });\n };\n\n const toggleAllInvitations = (checked: boolean) => {\n setSelectedInvitationIds((prev) => {\n const next = new Set(prev);\n pendingInvitations.forEach((invitation) => {\n if (checked) {\n next.add(invitation.id);\n } else {\n next.delete(invitation.id);\n }\n });\n return next;\n });\n };\n\n const handleBulkRemoveMembers = async () => {\n if (selectedMembers.length === 0) return;\n void nativeImpact('medium');\n const message = `${tWithFallback(t, 'membersList.bulkRemoveConfirmPrefix', 'Remove')} ${selectedMembers.length} ${\n selectedMembers.length === 1\n ? tWithFallback(t, 'membersList.bulkRemoveConfirmSingular', 'selected member')\n : tWithFallback(t, 'membersList.bulkRemoveConfirmPlural', 'selected members')\n } ${tWithFallback(t, 'membersList.bulkRemoveConfirmSuffix', 'from this workspace?')}`;\n const native = await nativeConfirm({\n title: 'Remove members',\n message,\n okButtonTitle: 'Remove',\n cancelButtonTitle: 'Cancel',\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) return;\n\n selectedMembers.forEach((member) => {\n removeUserFromWorkspace(member.userId);\n });\n clearMemberSelection();\n };\n\n const handleBulkResendInvitations = () => {\n if (selectedInvitations.length === 0) return;\n selectedInvitations.forEach((invitation) => {\n resendInvitation(invitation.id, {\n onSuccess: () => {\n setSelectedInvitationIds((prev) => {\n const next = new Set(prev);\n next.delete(invitation.id);\n return next;\n });\n },\n });\n });\n };\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'membersList.pageTitle', 'Members')}\n </h1>\n <p className=\"text-text-secondary text-sm sm:text-base\">\n {tWithFallback(\n t,\n 'membersList.pageSubtitle',\n 'Manage workspace members and invitations'\n )}\n </p>\n </div>\n\n <div className=\"flex gap-2\">\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.MEMBER_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'membersList.noPermissionToInvite',\n \"You don't have permission to invite members\"\n )}\n >\n <Button onClick={handleInviteMember} disabled={!workspaceId}>\n <UserPlus className=\"size-4 sm:mr-2\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, 'membersList.inviteMember', 'Invite Member')}\n </span>\n </Button>\n </PermissionButton>\n </div>\n </div>\n\n <PagePurpose>\n {tWithFallback(\n t,\n 'membersList.purpose',\n 'Members are the people who belong to this workspace. From here you invite teammates, see who has accepted, resend or track invitations that are still pending, and remove people who no longer need access. Search by name or email to find someone quickly.'\n )}\n </PagePurpose>\n\n {/* Search & Stats */}\n <div className=\"flex flex-col sm:flex-row gap-3 sm:gap-4\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-secondary\" />\n <Input\n placeholder={tWithFallback(t, 'membersList.searchPlaceholder', 'Search members...')}\n value={searchValue}\n onChange={(e) => setSearchValue(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n\n {hasPendingInvitations && (\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\" className=\"text-sm\">\n {pendingInvitations.length}{' '}\n {pendingInvitations.length !== 1\n ? tWithFallback(t, 'membersList.pendingInvitations', 'Pending Invitations')\n : tWithFallback(t, 'membersList.pendingInvitation', 'Pending Invitation')}\n </Badge>\n </div>\n )}\n </div>\n\n {filteredMembers.length > 0 && (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={allVisibleMembersSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelectedMembers && !allVisibleMembersSelected;\n }\n }}\n onChange={(event) => toggleAllMembers(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'membersList.selectAllMembersAriaLabel',\n 'Select all workspace members'\n )}\n className=\"size-4 rounded border-border-subtle text-primary\"\n />\n <span>\n {tWithFallback(\n t,\n 'membersList.selectAllVisibleMembers',\n 'Select all visible members'\n )}\n </span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">\n {selectedMemberIds.size} {tWithFallback(t, 'membersList.selected', 'selected')}\n </span>\n {selectedMemberIds.size > 0 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearMemberSelection}>\n {tWithFallback(t, 'membersList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {canRemoveMember && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkRemoveMembers}\n disabled={selectedMembers.length === 0 || isRemovingMembers}\n className=\"text-status-error-text\"\n >\n {tWithFallback(t, 'membersList.removeSelected', 'Remove Selected')}\n {selectedMembers.length > 0 ? ` (${selectedMembers.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n )}\n\n {/* Loading State */}\n {isLoading && (\n <div className=\"space-y-3\">\n {[...Array(5)].map((_, i) => (\n <Skeleton key={i} className=\"h-20 w-full\" />\n ))}\n </div>\n )}\n\n {/* Error State */}\n {!isLoading && membersError && (\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <div className=\"rounded-full bg-status-error-bg/10 p-6 mb-4\">\n <AlertCircle size={48} className=\"text-status-error-text\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'membersList.failedToLoad', 'Failed to Load Members')}\n </h3>\n <p className=\"text-text-secondary max-w-sm\">\n {membersError instanceof Error\n ? membersError.message\n : tWithFallback(\n t,\n 'membersList.errorLoadingMembers',\n 'An error occurred while loading members'\n )}\n </p>\n </div>\n )}\n\n {/* Empty State - No Workspace */}\n {!isLoading && !membersError && !workspaceId && (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title={tWithFallback(t, 'membersList.noWorkspaceSelected', 'No Workspace Selected')}\n description={tWithFallback(\n t,\n 'membersList.selectWorkspacePrompt',\n 'Select a workspace to view and manage its members'\n )}\n />\n )}\n\n {/* Empty State - No Members */}\n {!isLoading && !membersError && workspaceId && !hasMembers && (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title={tWithFallback(t, 'membersList.noMembersYet', 'No Members Yet')}\n description={tWithFallback(\n t,\n 'membersList.noMembersPrompt',\n 'Start building your team by inviting members to this workspace'\n )}\n action={\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.MEMBER_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'membersList.noPermissionToInvite',\n \"You don't have permission to invite members\"\n )}\n >\n <Button onClick={handleInviteMember}>\n <UserPlus className=\"mr-2 size-4\" />\n {tWithFallback(t, 'membersList.inviteFirstMember', 'Invite Your First Member')}\n </Button>\n </PermissionButton>\n }\n />\n )}\n\n {/* Empty State - No Search Results */}\n {!isLoading &&\n !membersError &&\n workspaceId &&\n hasMembers &&\n filteredMembers.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tWithFallback(t, 'membersList.noMembersFound', 'No Members Found')}\n description={tWithFallback(\n t,\n 'membersList.noMembersFoundPrompt',\n 'No members match your search criteria. Try adjusting your search.'\n )}\n />\n )}\n\n {/* Members List */}\n {!isLoading && filteredMembers.length > 0 && (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'membersList.activeMembers', 'Active Members')} (\n {filteredMembers.length})\n </h2>\n </div>\n <div className=\"space-y-2\">\n {filteredMembers.map((member) => (\n <MemberListItem\n key={member.id}\n member={member}\n selected={selectedMemberIds.has(member.id)}\n onToggleSelect={(checked) => {\n const isSelected = selectedMemberIds.has(member.id);\n if (checked !== isSelected) {\n toggleMemberSelection(member.id);\n }\n }}\n canRemove={canRemoveMember}\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Pending Invitations Section - Now at bottom */}\n {!isLoading && hasPendingInvitations && (\n <div className=\"space-y-3\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'membersList.pendingInvitationsHeader', 'Pending Invitations')}\n </h2>\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={allVisibleInvitationsSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate =\n partiallySelectedInvitations && !allVisibleInvitationsSelected;\n }\n }}\n onChange={(event) => toggleAllInvitations(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'membersList.selectAllInvitationsAriaLabel',\n 'Select all pending invitations'\n )}\n className=\"size-4 rounded border-border-subtle text-primary\"\n />\n <span>\n {tWithFallback(t, 'membersList.selectAllInvitations', 'Select all invitations')}\n </span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">\n {selectedInvitationIds.size}{' '}\n {tWithFallback(t, 'membersList.selected', 'selected')}\n </span>\n {selectedInvitationIds.size > 0 && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setSelectedInvitationIds(new Set())}\n >\n {tWithFallback(t, 'membersList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {canInviteMember && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkResendInvitations}\n disabled={selectedInvitations.length === 0 || isResending}\n >\n {tWithFallback(t, 'membersList.resendSelected', 'Resend Selected')}\n {selectedInvitations.length > 0 ? ` (${selectedInvitations.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n <div className=\"space-y-2\">\n {pendingInvitations.map((invitation) => (\n <div\n key={invitation.id}\n className=\"flex items-center justify-between p-4 border rounded-lg bg-bg-surface\"\n >\n <div className=\"flex items-center gap-3\">\n <input\n type=\"checkbox\"\n checked={selectedInvitationIds.has(invitation.id)}\n onChange={(event) =>\n toggleInvitationSelection(invitation.id, event.target.checked)\n }\n aria-label={`${tWithFallback(t, 'membersList.selectInvitationAriaLabel', 'Select invitation')} ${invitation.email}`}\n className=\"size-4 rounded border-border-subtle text-primary\"\n />\n <div className=\"size-10 rounded-full bg-status-warning/10 flex items-center justify-center transition-colors\">\n <Users className=\"size-5 text-status-warning\" />\n </div>\n <div>\n <p className=\"font-medium text-sm\">{invitation.email}</p>\n <p className=\"text-xs text-text-secondary\">\n {tWithFallback(t, 'membersList.invited', 'Invited')}{' '}\n {new Date(invitation.createdAt).toLocaleDateString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <PermissionButton\n permission={WORKSPACE_PERMISSIONS.MEMBER_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tWithFallback(\n t,\n 'membersList.noPermissionToResend',\n \"You don't have permission to resend invitations\"\n )}\n >\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleResendInvitation(invitation.id)}\n disabled={isResending && resendingId === invitation.id}\n >\n <RefreshCw\n className={`size-4 mr-1 ${isResending && resendingId === invitation.id ? 'animate-spin' : ''}`}\n />\n {isResending && resendingId === invitation.id\n ? tWithFallback(t, 'membersList.sending', 'Sending...')\n : tWithFallback(t, 'membersList.resend', 'Resend')}\n </Button>\n </PermissionButton>\n <Badge\n variant=\"outline\"\n className=\"bg-status-warning/10 text-status-warning border-status-warning/30 transition-colors\"\n >\n {tWithFallback(t, 'membersList.pending', 'Pending')}\n </Badge>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n </PageContainer>\n );\n}\n"],"mappings":"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"}
@@ -8,7 +8,7 @@ import { useMemo as l, useState as u } from "react";
8
8
  import { AlertTriangle as d, CheckCircle as f, Home as p, Mail as m, RefreshCw as h, XCircle as g } from "lucide-react";
9
9
  import { jsx as _, jsxs as v } from "react/jsx-runtime";
10
10
  import { Badge as y, Button as b, GlassCard as x, IllustratedEmptyState as S, PageContainer as C, PagePurpose as w, Skeleton as T } from "@burdenoff/fe-libs/ui";
11
- import { useNavigate as E } from "react-router-dom";
11
+ import { useNavigate as E } from "react-router";
12
12
  import { useI18n as D } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
13
13
  import { ActorIdentity as O, useActorProfiles as k } from "@burdenoff/fe-libs/shared";
14
14
  //#region src/pages/PendingInvitesPage.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"PendingInvitesPage.js","names":[],"sources":["../../src/pages/PendingInvitesPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { Mail, RefreshCw, CheckCircle, XCircle, AlertTriangle, Home } from 'lucide-react';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport {\n Badge,\n Button,\n GlassCard,\n PageContainer,\n Skeleton,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport {\n getProjectDisplayNames,\n getRoleDisplayNames,\n getWorkspaceDisplayName,\n} from '../utils/invitationDisplay';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useAcceptInvitation, useRejectInvitation } from '../hooks/useMemberMutations';\nimport { useMyPendingInvitations } from '../hooks/usePendingInvitations';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\n\nfunction formatDate(value?: string) {\n if (!value) return null;\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return null;\n\n return date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nexport function PendingInvitesPage() {\n useWorkspacesPageView('workspaces.pending-invites');\n\n const { t } = useI18n();\n const navigate = useNavigate();\n const {\n currentUser,\n authToken,\n apiGatewayUrl,\n organizationId,\n basePath = '/workspaces',\n } = useWorkspacesContext();\n const [processingInviteId, setProcessingInviteId] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const {\n data: pendingInvitations = [],\n isLoading,\n error,\n } = useMyPendingInvitations(currentUser?.email);\n\n const sortedInvitations = useMemo(\n () =>\n [...pendingInvitations].sort(\n (left, right) => new Date(right.createdAt).getTime() - new Date(left.createdAt).getTime()\n ),\n [pendingInvitations]\n );\n\n const inviterActors = useMemo<ActorInput[]>(\n () =>\n sortedInvitations.flatMap((invitation) =>\n invitation.invitedById\n ? [{ actorId: invitation.invitedById, actorType: 'user' as const }]\n : []\n ),\n [sortedInvitations]\n );\n\n const inviterProfiles: ActorProfileMap = useActorProfiles(inviterActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n });\n\n const { mutate: acceptInvitation, isPending: isAccepting } = useAcceptInvitation();\n const { mutate: rejectInvitation, isPending: isRejecting } = useRejectInvitation();\n\n const isMutating = isAccepting || isRejecting;\n\n const handleBackToWorkspaces = () => {\n navigate(`${basePath}/list`);\n };\n\n const handleAccept = (invitationId: string) => {\n setProcessingInviteId(invitationId);\n setActionError(null);\n\n acceptInvitation(invitationId, {\n onSuccess: () => {\n setProcessingInviteId(null);\n },\n onError: (mutationError) => {\n setProcessingInviteId(null);\n setActionError(\n mutationError instanceof Error\n ? mutationError.message\n : tWithFallback(t, 'pendingInvites.acceptError', 'Failed to accept invitation')\n );\n },\n });\n };\n\n const handleReject = (invitationId: string) => {\n setProcessingInviteId(invitationId);\n setActionError(null);\n\n rejectInvitation(invitationId, {\n onSuccess: () => {\n setProcessingInviteId(null);\n },\n onError: (mutationError) => {\n setProcessingInviteId(null);\n setActionError(\n mutationError instanceof Error\n ? mutationError.message\n : tWithFallback(t, 'pendingInvites.rejectError', 'Failed to reject invitation')\n );\n },\n });\n };\n\n if (!authToken || !currentUser?.email) {\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <div className=\"rounded-lg border border-border-seam bg-bg-surface p-6 text-center\">\n <AlertTriangle className=\"mx-auto mb-3 size-10 text-text-secondary\" />\n <h1 className=\"text-2xl font-bold\">\n {tWithFallback(\n t,\n 'pendingInvites.signInTitle',\n 'Sign in to view your pending invites'\n )}\n </h1>\n <p className=\"mt-2 text-text-secondary\">\n {tWithFallback(\n t,\n 'pendingInvites.signInDescription',\n 'You need an authenticated account before you can review or respond to workspace invitations.'\n )}\n </p>\n <div className=\"mt-4 flex justify-center\">\n <Button onClick={handleBackToWorkspaces} variant=\"outline\">\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'pendingInvites.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n </div>\n </div>\n </div>\n </PageContainer>\n );\n }\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'pendingInvites.pageTitle', 'Pending Invites')}\n </h1>\n <p className=\"text-sm sm:text-base text-text-secondary\">\n {tWithFallback(\n t,\n 'pendingInvites.pageSubtitle',\n 'Review and respond to workspace invitations sent to your account.'\n )}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\">{sortedInvitations.length}</Badge>\n <Button variant=\"outline\" onClick={handleBackToWorkspaces}>\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'pendingInvites.viewWorkspaces', 'View Workspaces')}\n </Button>\n </div>\n </div>\n\n <PagePurpose>\n {tWithFallback(\n t,\n 'pendingInvites.purpose',\n 'These are workspace invitations sent to your email but not yet answered. Accepting one adds you to that workspace (with the roles and project access shown); rejecting declines it. Invitations expire, so respond to the ones you want before their expiry date.'\n )}\n </PagePurpose>\n\n {actionError ? (\n <div className=\"flex items-start gap-2 rounded-lg border border-status-error-border/30 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n <AlertTriangle className=\"mt-0.5 size-4 shrink-0\" />\n <span>{actionError}</span>\n </div>\n ) : null}\n\n {error ? (\n <div className=\"rounded-lg border border-status-error-border/30 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n {error instanceof Error\n ? error.message\n : tWithFallback(t, 'pendingInvites.loadError', 'Failed to load pending invitations')}\n </div>\n ) : null}\n\n {isLoading ? (\n <div className=\"space-y-4\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div\n key={index}\n className=\"rounded-lg border border-border-seam bg-bg-surface p-5 space-y-3\"\n >\n <Skeleton className=\"h-5 w-40\" />\n <Skeleton className=\"h-4 w-64\" />\n <Skeleton className=\"h-4 w-52\" />\n <div className=\"flex gap-2 pt-2\">\n <Skeleton className=\"h-10 w-36\" />\n <Skeleton className=\"h-10 w-28\" />\n </div>\n </div>\n ))}\n </div>\n ) : sortedInvitations.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={tWithFallback(t, 'pendingInvites.emptyTitle', 'No pending invites')}\n description={tWithFallback(\n t,\n 'pendingInvites.emptyDescription',\n 'When someone invites you to a workspace, it will appear here.'\n )}\n />\n ) : (\n <div className=\"space-y-4\">\n {sortedInvitations.map((invitation, invitationIndex) => {\n const inviterProfile = invitation.invitedById\n ? inviterProfiles[invitation.invitedById]\n : undefined;\n const inviterLabel =\n inviterProfile?.displayName ||\n inviterProfile?.email ||\n invitation.invitedById ||\n '—';\n const expiresAt = formatDate(invitation.expiresAt);\n const createdAt = formatDate(invitation.createdAt);\n const workspaceDisplayName = getWorkspaceDisplayName(invitation);\n const roleDisplayNames = getRoleDisplayNames(invitation);\n const projectDisplayNames = getProjectDisplayNames(invitation);\n const isProcessingThisInvite = processingInviteId === invitation.id && isMutating;\n\n return (\n <GlassCard\n key={invitation.id}\n treatment=\"insight\"\n glow={invitationIndex === 0}\n className=\"border-border-seam p-5\"\n >\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-3 min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <div className=\"flex items-center gap-2 font-semibold text-lg\">\n <Mail className=\"size-4 text-primary\" />\n <span>{invitation.email}</span>\n </div>\n <Badge variant=\"secondary\">{invitation.status}</Badge>\n </div>\n\n <dl className=\"grid gap-2 text-sm text-text-secondary sm:grid-cols-2\">\n <div>\n <dt className=\"font-medium text-text-primary\">\n {tWithFallback(t, 'pendingInvites.workspaceLabel', 'Workspace')}\n </dt>\n <dd className=\"break-words font-medium text-text-primary\">\n {workspaceDisplayName}\n </dd>\n {invitation.workspaceId ? (\n <dd className=\"mt-1 break-all font-mono text-xs text-text-secondary\">\n {invitation.workspaceId}\n </dd>\n ) : null}\n </div>\n <div>\n <dt className=\"font-medium text-text-primary\">\n {tWithFallback(t, 'pendingInvites.invitedByLabel', 'Invited by')}\n </dt>\n <dd className=\"break-all\">\n {inviterProfile ? (\n <ActorIdentity\n profile={inviterProfile}\n variant=\"inline\"\n hideIdDrilldown\n />\n ) : (\n <span className=\"font-medium text-text-primary\">{inviterLabel}</span>\n )}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-primary\">\n {tWithFallback(t, 'pendingInvites.receivedLabel', 'Received')}\n </dt>\n <dd>{createdAt ?? '—'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-primary\">\n {tWithFallback(t, 'pendingInvites.expiresLabel', 'Expires')}\n </dt>\n <dd>{expiresAt ?? '—'}</dd>\n </div>\n </dl>\n\n <div>\n <p className=\"text-sm font-medium text-text-primary mb-2\">\n {tWithFallback(t, 'pendingInvites.rolesLabel', 'Roles')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {roleDisplayNames.length > 0 ? (\n roleDisplayNames.map((roleName, index) => (\n <Badge\n key={`${invitation.id}-${invitation.roleIds[index] ?? roleName}`}\n variant=\"outline\"\n >\n {roleName}\n </Badge>\n ))\n ) : (\n <span className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'pendingInvites.noRoles',\n 'No explicit roles assigned'\n )}\n </span>\n )}\n </div>\n </div>\n\n <div>\n <p className=\"text-sm font-medium text-text-primary mb-2\">\n {tWithFallback(t, 'pendingInvites.projectsLabel', 'Projects')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {projectDisplayNames.length > 0 ? (\n projectDisplayNames.map((projectName, index) => (\n <Badge\n key={`${invitation.id}-project-${projectName}-${index}`}\n variant=\"secondary\"\n >\n {projectName}\n </Badge>\n ))\n ) : (\n <span className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'pendingInvites.noProjects',\n 'No project access will be granted automatically'\n )}\n </span>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row lg:flex-col lg:min-w-44\">\n <Button\n onClick={() => handleAccept(invitation.id)}\n disabled={isMutating}\n className=\"w-full\"\n >\n {isProcessingThisInvite && isAccepting ? (\n <RefreshCw className=\"mr-2 size-4 animate-spin\" />\n ) : (\n <CheckCircle className=\"mr-2 size-4\" />\n )}\n {tWithFallback(t, 'pendingInvites.acceptButton', 'Accept')}\n </Button>\n <Button\n variant=\"outline\"\n onClick={() => handleReject(invitation.id)}\n disabled={isMutating}\n className=\"w-full\"\n >\n {isProcessingThisInvite && isRejecting ? (\n <RefreshCw className=\"mr-2 size-4 animate-spin\" />\n ) : (\n <XCircle className=\"mr-2 size-4\" />\n )}\n {tWithFallback(t, 'pendingInvites.rejectButton', 'Reject')}\n </Button>\n </div>\n </div>\n </GlassCard>\n );\n })}\n </div>\n )}\n </div>\n </PageContainer>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PendingInvitesPage.js","names":[],"sources":["../../src/pages/PendingInvitesPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useNavigate } from 'react-router';\nimport { Mail, RefreshCw, CheckCircle, XCircle, AlertTriangle, Home } from 'lucide-react';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport {\n Badge,\n Button,\n GlassCard,\n PageContainer,\n Skeleton,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport {\n getProjectDisplayNames,\n getRoleDisplayNames,\n getWorkspaceDisplayName,\n} from '../utils/invitationDisplay';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useAcceptInvitation, useRejectInvitation } from '../hooks/useMemberMutations';\nimport { useMyPendingInvitations } from '../hooks/usePendingInvitations';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\n\nfunction formatDate(value?: string) {\n if (!value) return null;\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return null;\n\n return date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nexport function PendingInvitesPage() {\n useWorkspacesPageView('workspaces.pending-invites');\n\n const { t } = useI18n();\n const navigate = useNavigate();\n const {\n currentUser,\n authToken,\n apiGatewayUrl,\n organizationId,\n basePath = '/workspaces',\n } = useWorkspacesContext();\n const [processingInviteId, setProcessingInviteId] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n\n const {\n data: pendingInvitations = [],\n isLoading,\n error,\n } = useMyPendingInvitations(currentUser?.email);\n\n const sortedInvitations = useMemo(\n () =>\n [...pendingInvitations].sort(\n (left, right) => new Date(right.createdAt).getTime() - new Date(left.createdAt).getTime()\n ),\n [pendingInvitations]\n );\n\n const inviterActors = useMemo<ActorInput[]>(\n () =>\n sortedInvitations.flatMap((invitation) =>\n invitation.invitedById\n ? [{ actorId: invitation.invitedById, actorType: 'user' as const }]\n : []\n ),\n [sortedInvitations]\n );\n\n const inviterProfiles: ActorProfileMap = useActorProfiles(inviterActors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n });\n\n const { mutate: acceptInvitation, isPending: isAccepting } = useAcceptInvitation();\n const { mutate: rejectInvitation, isPending: isRejecting } = useRejectInvitation();\n\n const isMutating = isAccepting || isRejecting;\n\n const handleBackToWorkspaces = () => {\n navigate(`${basePath}/list`);\n };\n\n const handleAccept = (invitationId: string) => {\n setProcessingInviteId(invitationId);\n setActionError(null);\n\n acceptInvitation(invitationId, {\n onSuccess: () => {\n setProcessingInviteId(null);\n },\n onError: (mutationError) => {\n setProcessingInviteId(null);\n setActionError(\n mutationError instanceof Error\n ? mutationError.message\n : tWithFallback(t, 'pendingInvites.acceptError', 'Failed to accept invitation')\n );\n },\n });\n };\n\n const handleReject = (invitationId: string) => {\n setProcessingInviteId(invitationId);\n setActionError(null);\n\n rejectInvitation(invitationId, {\n onSuccess: () => {\n setProcessingInviteId(null);\n },\n onError: (mutationError) => {\n setProcessingInviteId(null);\n setActionError(\n mutationError instanceof Error\n ? mutationError.message\n : tWithFallback(t, 'pendingInvites.rejectError', 'Failed to reject invitation')\n );\n },\n });\n };\n\n if (!authToken || !currentUser?.email) {\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <div className=\"rounded-lg border border-border-seam bg-bg-surface p-6 text-center\">\n <AlertTriangle className=\"mx-auto mb-3 size-10 text-text-secondary\" />\n <h1 className=\"text-2xl font-bold\">\n {tWithFallback(\n t,\n 'pendingInvites.signInTitle',\n 'Sign in to view your pending invites'\n )}\n </h1>\n <p className=\"mt-2 text-text-secondary\">\n {tWithFallback(\n t,\n 'pendingInvites.signInDescription',\n 'You need an authenticated account before you can review or respond to workspace invitations.'\n )}\n </p>\n <div className=\"mt-4 flex justify-center\">\n <Button onClick={handleBackToWorkspaces} variant=\"outline\">\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'pendingInvites.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n </div>\n </div>\n </div>\n </PageContainer>\n );\n }\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'pendingInvites.pageTitle', 'Pending Invites')}\n </h1>\n <p className=\"text-sm sm:text-base text-text-secondary\">\n {tWithFallback(\n t,\n 'pendingInvites.pageSubtitle',\n 'Review and respond to workspace invitations sent to your account.'\n )}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\">{sortedInvitations.length}</Badge>\n <Button variant=\"outline\" onClick={handleBackToWorkspaces}>\n <Home className=\"mr-2 size-4\" />\n {tWithFallback(t, 'pendingInvites.viewWorkspaces', 'View Workspaces')}\n </Button>\n </div>\n </div>\n\n <PagePurpose>\n {tWithFallback(\n t,\n 'pendingInvites.purpose',\n 'These are workspace invitations sent to your email but not yet answered. Accepting one adds you to that workspace (with the roles and project access shown); rejecting declines it. Invitations expire, so respond to the ones you want before their expiry date.'\n )}\n </PagePurpose>\n\n {actionError ? (\n <div className=\"flex items-start gap-2 rounded-lg border border-status-error-border/30 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n <AlertTriangle className=\"mt-0.5 size-4 shrink-0\" />\n <span>{actionError}</span>\n </div>\n ) : null}\n\n {error ? (\n <div className=\"rounded-lg border border-status-error-border/30 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n {error instanceof Error\n ? error.message\n : tWithFallback(t, 'pendingInvites.loadError', 'Failed to load pending invitations')}\n </div>\n ) : null}\n\n {isLoading ? (\n <div className=\"space-y-4\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div\n key={index}\n className=\"rounded-lg border border-border-seam bg-bg-surface p-5 space-y-3\"\n >\n <Skeleton className=\"h-5 w-40\" />\n <Skeleton className=\"h-4 w-64\" />\n <Skeleton className=\"h-4 w-52\" />\n <div className=\"flex gap-2 pt-2\">\n <Skeleton className=\"h-10 w-36\" />\n <Skeleton className=\"h-10 w-28\" />\n </div>\n </div>\n ))}\n </div>\n ) : sortedInvitations.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={tWithFallback(t, 'pendingInvites.emptyTitle', 'No pending invites')}\n description={tWithFallback(\n t,\n 'pendingInvites.emptyDescription',\n 'When someone invites you to a workspace, it will appear here.'\n )}\n />\n ) : (\n <div className=\"space-y-4\">\n {sortedInvitations.map((invitation, invitationIndex) => {\n const inviterProfile = invitation.invitedById\n ? inviterProfiles[invitation.invitedById]\n : undefined;\n const inviterLabel =\n inviterProfile?.displayName ||\n inviterProfile?.email ||\n invitation.invitedById ||\n '—';\n const expiresAt = formatDate(invitation.expiresAt);\n const createdAt = formatDate(invitation.createdAt);\n const workspaceDisplayName = getWorkspaceDisplayName(invitation);\n const roleDisplayNames = getRoleDisplayNames(invitation);\n const projectDisplayNames = getProjectDisplayNames(invitation);\n const isProcessingThisInvite = processingInviteId === invitation.id && isMutating;\n\n return (\n <GlassCard\n key={invitation.id}\n treatment=\"insight\"\n glow={invitationIndex === 0}\n className=\"border-border-seam p-5\"\n >\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-3 min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <div className=\"flex items-center gap-2 font-semibold text-lg\">\n <Mail className=\"size-4 text-primary\" />\n <span>{invitation.email}</span>\n </div>\n <Badge variant=\"secondary\">{invitation.status}</Badge>\n </div>\n\n <dl className=\"grid gap-2 text-sm text-text-secondary sm:grid-cols-2\">\n <div>\n <dt className=\"font-medium text-text-primary\">\n {tWithFallback(t, 'pendingInvites.workspaceLabel', 'Workspace')}\n </dt>\n <dd className=\"break-words font-medium text-text-primary\">\n {workspaceDisplayName}\n </dd>\n {invitation.workspaceId ? (\n <dd className=\"mt-1 break-all font-mono text-xs text-text-secondary\">\n {invitation.workspaceId}\n </dd>\n ) : null}\n </div>\n <div>\n <dt className=\"font-medium text-text-primary\">\n {tWithFallback(t, 'pendingInvites.invitedByLabel', 'Invited by')}\n </dt>\n <dd className=\"break-all\">\n {inviterProfile ? (\n <ActorIdentity\n profile={inviterProfile}\n variant=\"inline\"\n hideIdDrilldown\n />\n ) : (\n <span className=\"font-medium text-text-primary\">{inviterLabel}</span>\n )}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-primary\">\n {tWithFallback(t, 'pendingInvites.receivedLabel', 'Received')}\n </dt>\n <dd>{createdAt ?? '—'}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-primary\">\n {tWithFallback(t, 'pendingInvites.expiresLabel', 'Expires')}\n </dt>\n <dd>{expiresAt ?? '—'}</dd>\n </div>\n </dl>\n\n <div>\n <p className=\"text-sm font-medium text-text-primary mb-2\">\n {tWithFallback(t, 'pendingInvites.rolesLabel', 'Roles')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {roleDisplayNames.length > 0 ? (\n roleDisplayNames.map((roleName, index) => (\n <Badge\n key={`${invitation.id}-${invitation.roleIds[index] ?? roleName}`}\n variant=\"outline\"\n >\n {roleName}\n </Badge>\n ))\n ) : (\n <span className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'pendingInvites.noRoles',\n 'No explicit roles assigned'\n )}\n </span>\n )}\n </div>\n </div>\n\n <div>\n <p className=\"text-sm font-medium text-text-primary mb-2\">\n {tWithFallback(t, 'pendingInvites.projectsLabel', 'Projects')}\n </p>\n <div className=\"flex flex-wrap gap-2\">\n {projectDisplayNames.length > 0 ? (\n projectDisplayNames.map((projectName, index) => (\n <Badge\n key={`${invitation.id}-project-${projectName}-${index}`}\n variant=\"secondary\"\n >\n {projectName}\n </Badge>\n ))\n ) : (\n <span className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'pendingInvites.noProjects',\n 'No project access will be granted automatically'\n )}\n </span>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"flex flex-col gap-2 sm:flex-row lg:flex-col lg:min-w-44\">\n <Button\n onClick={() => handleAccept(invitation.id)}\n disabled={isMutating}\n className=\"w-full\"\n >\n {isProcessingThisInvite && isAccepting ? (\n <RefreshCw className=\"mr-2 size-4 animate-spin\" />\n ) : (\n <CheckCircle className=\"mr-2 size-4\" />\n )}\n {tWithFallback(t, 'pendingInvites.acceptButton', 'Accept')}\n </Button>\n <Button\n variant=\"outline\"\n onClick={() => handleReject(invitation.id)}\n disabled={isMutating}\n className=\"w-full\"\n >\n {isProcessingThisInvite && isRejecting ? (\n <RefreshCw className=\"mr-2 size-4 animate-spin\" />\n ) : (\n <XCircle className=\"mr-2 size-4\" />\n )}\n {tWithFallback(t, 'pendingInvites.rejectButton', 'Reject')}\n </Button>\n </div>\n </div>\n </GlassCard>\n );\n })}\n </div>\n )}\n </div>\n </PageContainer>\n );\n}\n"],"mappings":"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"}
@@ -17,7 +17,7 @@ import { useMemo as ge, useState as s } from "react";
17
17
  import { Activity as c, Archive as l, ArrowLeft as u, Clock as d, FolderKanban as f, MoreVertical as _e, Pencil as ve, UserPlus as p, Users as m } from "lucide-react";
18
18
  import { jsx as h, jsxs as g } from "react/jsx-runtime";
19
19
  import { AlertDialog as ye, AlertDialogAction as be, AlertDialogCancel as xe, AlertDialogContent as Se, AlertDialogDescription as Ce, AlertDialogFooter as we, AlertDialogHeader as Te, AlertDialogTitle as Ee, Badge as _, Button as v, CardContent as y, CardHeader as b, CardTitle as x, DropdownMenu as De, DropdownMenuContent as Oe, DropdownMenuItem as S, DropdownMenuSeparator as C, DropdownMenuTrigger as w, EntityIdModal as T, GlassCard as E, IllustratedEmptyState as ke, NextSteps as Ae, PagePurpose as je, ShowMore as Me, Skeleton as D, useEntityIdModal as Ne } from "@burdenoff/fe-libs/ui";
20
- import { useNavigate as Pe, useParams as O } from "react-router-dom";
20
+ import { useNavigate as Pe, useParams as O } from "react-router";
21
21
  import { useI18n as Fe } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
22
22
  //#region src/pages/ProjectDetailPage.tsx
23
23
  function k() {