@burdenoff/microfe-workspaces 2026.624.1 → 2026.625.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.
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspaceInvitePage.js","names":[],"sources":["../../src/pages/WorkspaceInvitePage.tsx"],"sourcesContent":["import { useMemo, useRef, useState } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport {\n AlertCircle,\n ArrowLeft,\n CheckCircle2,\n Download,\n Loader2,\n Mail,\n RefreshCw,\n Send,\n Upload,\n UserPlus,\n XCircle,\n} from 'lucide-react';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Checkbox,\n Input,\n Skeleton,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { PermissionProvider } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { getProjectDisplayNames, getRoleDisplayNames } from '../utils/invitationDisplay';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useWorkspace } from '../hooks/useWorkspaces';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspaceRoles } from '../hooks/useWorkspaceRoles';\nimport { useProjects } from '../hooks/useProjects';\nimport { useWorkspaceInvitations } from '../hooks/useWorkspaceMembers';\nimport {\n useBulkCreateInvitations,\n useCancelInvitation,\n useCreateInvitation,\n useResendInvitation,\n} from '../hooks/useMemberMutations';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport type { Project, WorkspaceInvitation } from '../types';\nimport {\n downloadWorkspaceBulkInviteTemplate,\n parseWorkspaceBulkInviteCsv,\n type WorkspaceBulkInviteRow,\n} from '../utils/workspaceBulkInviteCsv';\n\ntype InviteMode = 'single' | 'bulk';\n\ntype RowStatus =\n | { kind: 'idle' }\n | { kind: 'sending' }\n | { kind: 'invited' }\n | { kind: 'failed'; message: string };\n\ninterface PreviewRow extends WorkspaceBulkInviteRow {\n status: RowStatus;\n}\n\ntype FeedbackState = {\n tone: 'success' | 'error';\n message: string;\n} | null;\n\nconst MAX_BULK_INVITE_FILE_SIZE = 1024 * 1024;\nconst MAX_BULK_INVITE_ROWS = 500;\n\nfunction isCsvFile(file: File): boolean {\n return file.type === 'text/csv' || file.name.toLowerCase().endsWith('.csv');\n}\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\nfunction formatInviteError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes('CONFLICT')) {\n return 'An invite already exists for this email address.';\n }\n if (message.includes('already a member')) {\n return 'This user is already a member of the workspace.';\n }\n return 'Something went wrong. Please try again or contact support.';\n}\n\nfunction formatSelectionSummary(\n items: string[],\n emptyLabel: string,\n formatter?: (value: string) => string\n) {\n if (items.length === 0) {\n return emptyLabel;\n }\n\n return items.map((item) => (formatter ? formatter(item) : item)).join(', ');\n}\n\nfunction buildInvitationPayload(\n workspaceId: string,\n email: string,\n roleIds: string[],\n projectIds: string[]\n) {\n return {\n email,\n workspaceId,\n roleIds,\n ...(projectIds.length > 0\n ? {\n context: {\n type: 'workspace.invitation',\n metadata: { projectIds },\n },\n }\n : {}),\n };\n}\n\nfunction WorkspaceSelectorSection({\n selectedRoleIds,\n onRoleToggle,\n selectedProjectIds,\n onProjectToggle,\n availableRoles,\n availableProjects,\n isLoadingRoles,\n isLoadingProjects,\n disabled,\n}: {\n selectedRoleIds: string[];\n onRoleToggle: (roleId: string, checked: boolean) => void;\n selectedProjectIds: string[];\n onProjectToggle: (projectId: string, checked: boolean) => void;\n availableRoles: Array<{\n id: string;\n name: string;\n description?: string | null;\n isSystem: boolean;\n }>;\n availableProjects: Project[];\n isLoadingRoles: boolean;\n isLoadingProjects: boolean;\n disabled: boolean;\n}) {\n return (\n <div className=\"space-y-6\">\n <div className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"space-y-1\">\n <h3 className=\"text-base font-semibold\">Roles (Optional)</h3>\n <p className=\"text-sm text-text-secondary\">\n Choose roles. Hover any role to preview its full description.\n </p>\n </div>\n {selectedRoleIds.length > 0 ? (\n <Badge variant=\"secondary\">{selectedRoleIds.length} selected</Badge>\n ) : null}\n </div>\n <TooltipProvider>\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3\">\n {isLoadingRoles ? (\n <p className=\"text-sm text-text-secondary\">Loading roles…</p>\n ) : availableRoles.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No assignable roles were returned for this workspace.\n </p>\n ) : (\n availableRoles.map((role) => {\n const isSelected = selectedRoleIds.includes(role.id);\n return (\n <Tooltip key={role.id}>\n <TooltipTrigger asChild>\n <label\n className={`flex cursor-pointer items-center justify-between gap-3 rounded-lg border px-3 py-3 text-left text-sm font-medium transition-colors focus-within:ring-2 focus-within:ring-[var(--color-focus-ring)] focus-within:ring-offset-2 ${\n isSelected\n ? 'border-primary bg-action-primary-bg/10 text-text-primary shadow-sm'\n : 'border-border-subtle bg-bg-surface text-text-primary hover:bg-accent'\n } ${disabled ? 'opacity-70' : ''}`}\n >\n <span className=\"min-w-0 flex-1 truncate\">{role.name}</span>\n <Checkbox\n checked={isSelected}\n disabled={disabled}\n onCheckedChange={(checked) => onRoleToggle(role.id, Boolean(checked))}\n aria-label={`Select ${role.name}`}\n />\n </label>\n </TooltipTrigger>\n <TooltipContent className=\"max-w-xs text-sm leading-6\">\n <p className=\"font-medium\">{role.name}</p>\n <p className=\"mt-1 text-text-secondary\">\n {role.description || 'No description available for this role.'}\n </p>\n {role.isSystem ? <p className=\"mt-2 text-xs\">System role</p> : null}\n </TooltipContent>\n </Tooltip>\n );\n })\n )}\n </div>\n </TooltipProvider>\n </div>\n\n <div className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"space-y-1\">\n <h3 className=\"text-base font-semibold\">Projects to add on acceptance (Optional)</h3>\n <p className=\"text-sm text-text-secondary\">\n The invited user will be added to these projects automatically after they accept.\n </p>\n </div>\n {selectedProjectIds.length > 0 ? (\n <Badge variant=\"secondary\">{selectedProjectIds.length} selected</Badge>\n ) : null}\n </div>\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3\">\n {isLoadingProjects ? (\n <p className=\"text-sm text-text-secondary\">Loading projects…</p>\n ) : availableProjects.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No active projects are available in this workspace yet.\n </p>\n ) : (\n availableProjects.map((project) => {\n const isSelected = selectedProjectIds.includes(project.id);\n return (\n <label\n key={project.id}\n className={`flex cursor-pointer items-start justify-between gap-3 rounded-lg border px-3 py-3 transition-colors focus-within:ring-2 focus-within:ring-[var(--color-focus-ring)] focus-within:ring-offset-2 ${\n isSelected\n ? 'border-primary bg-action-primary-bg/10 text-text-primary shadow-sm'\n : 'border-border-subtle bg-bg-surface text-text-primary hover:bg-accent'\n } ${disabled ? 'opacity-70' : ''}`}\n >\n <div className=\"min-w-0 flex-1 space-y-1\">\n <p className=\"truncate text-sm font-semibold leading-tight\">{project.name}</p>\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <span className=\"truncate font-mono\">{project.key}</span>\n <Badge variant=\"outline\">{project.status}</Badge>\n </div>\n </div>\n <Checkbox\n checked={isSelected}\n disabled={disabled}\n onCheckedChange={(checked) => onProjectToggle(project.id, Boolean(checked))}\n aria-label={`Select ${project.name}`}\n />\n </label>\n );\n })\n )}\n </div>\n </div>\n </div>\n );\n}\n\nfunction WorkspaceInvitePageContent() {\n const { workspaceId: routeWorkspaceId } = useParams<{ workspaceId: string }>();\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/workspaces', workspaceId: contextWorkspaceId } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const workspaceId = routeWorkspaceId || contextWorkspaceId || '';\n\n useWorkspacesPageView('workspaces.invite', { workspaceId: workspaceId || undefined });\n\n const { canInviteMember, isLoading: permissionsLoading } = useWorkspacePermissions();\n const {\n data: workspace,\n isLoading: isLoadingWorkspace,\n error: workspaceError,\n } = useWorkspace(workspaceId);\n const { data: availableRoles = [], isLoading: isLoadingRoles } = useWorkspaceRoles(workspaceId);\n const { data: projectList, isLoading: isLoadingProjects } = useProjects(\n { archived: false },\n { limit: 100 },\n workspaceId\n );\n const {\n data: invitationsData,\n isLoading: isLoadingInvitations,\n isFetching: isFetchingInvitations,\n error: invitationsError,\n refetch: refetchInvitations,\n } = useWorkspaceInvitations(workspaceId, 'PENDING', { limit: 50 }, { retry: false });\n const availableProjects = useMemo(\n () => (projectList?.items ?? []).filter((project) => !project.archived),\n [projectList?.items]\n );\n\n const [mode, setMode] = useState<InviteMode>('single');\n const [email, setEmail] = useState('');\n const [selectedRoleIds, setSelectedRoleIds] = useState<string[]>([]);\n const [selectedProjectIds, setSelectedProjectIds] = useState<string[]>([]);\n const [singleError, setSingleError] = useState<string | null>(null);\n const [singleSuccess, setSingleSuccess] = useState<string | null>(null);\n const [bulkError, setBulkError] = useState<string | null>(null);\n const [bulkSuccess, setBulkSuccess] = useState<string | null>(null);\n const [pendingFeedback, setPendingFeedback] = useState<FeedbackState>(null);\n const [rows, setRows] = useState<PreviewRow[]>([]);\n const [fileName, setFileName] = useState<string | null>(null);\n const [dragActive, setDragActive] = useState(false);\n const [isSubmittingBulk, setIsSubmittingBulk] = useState(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [cancelTarget, setCancelTarget] = useState<WorkspaceInvitation | null>(null);\n\n const { mutateAsync: createInvitation, isPending: isCreatingInvitation } =\n useCreateInvitation(workspaceId);\n const { mutateAsync: bulkCreateInvitations, isPending: isCreatingBulkInvites } =\n useBulkCreateInvitations();\n const { mutate: resendInvitation, isPending: isResendingInvitation } =\n useResendInvitation(workspaceId);\n const { mutate: cancelInvitation, isPending: isCancellingInvitation } =\n useCancelInvitation(workspaceId);\n const [invitationActionId, setInvitationActionId] = useState<string | null>(null);\n\n const pendingInvitations = invitationsData?.items ?? [];\n const isMutatingInvite = isCreatingInvitation || isCreatingBulkInvites || isSubmittingBulk;\n const selectedRoleNames = useMemo(\n () =>\n availableRoles.filter((role) => selectedRoleIds.includes(role.id)).map((role) => role.name),\n [availableRoles, selectedRoleIds]\n );\n const selectedProjectNames = useMemo(\n () =>\n availableProjects\n .filter((project) => selectedProjectIds.includes(project.id))\n .map((project) => project.name),\n [availableProjects, selectedProjectIds]\n );\n\n const readyRows = useMemo(\n () => rows.filter((row) => row.errors.length === 0 && row.status.kind !== 'invited'),\n [rows]\n );\n const invalidRows = useMemo(() => rows.filter((row) => row.errors.length > 0), [rows]);\n\n if (!workspaceId) {\n return (\n <div className=\"space-y-4 p-6\">\n <div className=\"rounded-xl border bg-bg-surface p-8 text-center\">\n <h1 className=\"text-2xl font-bold\">\n {tWithFallback(t, 'workspaceInvite.noWorkspaceTitle', 'Select a workspace first')}\n </h1>\n <p className=\"mt-2 text-text-secondary\">\n {tWithFallback(\n t,\n 'workspaceInvite.noWorkspaceDescription',\n 'Choose a current workspace from the header or open a workspace detail page before inviting members.'\n )}\n </p>\n <div className=\"mt-4\">\n <Button\n onClick={() => navigate(withCurrentSearch(`${basePath}/list`))}\n variant=\"outline\"\n >\n {tWithFallback(t, 'workspaceInvite.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n </div>\n </div>\n </div>\n );\n }\n\n if (permissionsLoading || isLoadingWorkspace) {\n return (\n <div className=\"space-y-6 p-6\">\n <Skeleton className=\"h-10 w-48\" />\n <Skeleton className=\"h-40 w-full\" />\n <Skeleton className=\"h-80 w-full\" />\n </div>\n );\n }\n\n if (!canInviteMember) {\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"space-y-2 text-center\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'workspaceInvite.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'workspaceInvite.accessDeniedDescription',\n \"You don't have permission to invite members into this workspace.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (workspaceError || !workspace) {\n return (\n <div className=\"space-y-4 p-6\">\n <Button\n variant=\"ghost\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/list`))}\n className=\"gap-2\"\n >\n <ArrowLeft size={18} />\n {tWithFallback(t, 'workspaceInvite.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n <div className=\"rounded-xl border bg-bg-surface p-8 text-center text-status-error-text\">\n {tWithFallback(\n t,\n 'workspaceInvite.workspaceLoadError',\n 'Unable to load workspace invite details.'\n )}\n </div>\n </div>\n );\n }\n\n const handleRoleToggle = (roleId: string, checked: boolean) => {\n setSelectedRoleIds((prev) =>\n checked\n ? [...prev, roleId].filter((value, index, array) => array.indexOf(value) === index)\n : prev.filter((id) => id !== roleId)\n );\n };\n\n const handleProjectToggle = (projectId: string, checked: boolean) => {\n setSelectedProjectIds((prev) =>\n checked\n ? [...prev, projectId].filter((value, index, array) => array.indexOf(value) === index)\n : prev.filter((id) => id !== projectId)\n );\n };\n\n const handleSingleInvite = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setSingleError(null);\n setSingleSuccess(null);\n setPendingFeedback(null);\n\n if (!email.trim()) {\n setSingleError('Email is required.');\n return;\n }\n\n try {\n await createInvitation(\n buildInvitationPayload(\n workspaceId,\n email.trim().toLowerCase(),\n selectedRoleIds,\n selectedProjectIds\n )\n );\n setEmail('');\n setSingleSuccess(`Invitation sent to ${email.trim().toLowerCase()}.`);\n } catch (error) {\n setSingleError(formatInviteError(error));\n }\n };\n\n const handleFile = (file: File) => {\n if (!isCsvFile(file)) {\n setBulkError('Please upload a .csv file.');\n return;\n }\n\n if (file.size > MAX_BULK_INVITE_FILE_SIZE) {\n setBulkError('CSV file is too large (max 1 MB).');\n return;\n }\n\n const reader = new FileReader();\n reader.onload = () => {\n const text = typeof reader.result === 'string' ? reader.result : '';\n const parsed = parseWorkspaceBulkInviteCsv(text);\n const limitedRows = parsed.slice(0, MAX_BULK_INVITE_ROWS);\n setRows(limitedRows.map((row) => ({ ...row, status: { kind: 'idle' } })));\n setFileName(file.name);\n setBulkError(\n parsed.length > MAX_BULK_INVITE_ROWS\n ? `Only the first ${MAX_BULK_INVITE_ROWS} rows were loaded. Split larger CSV files into smaller batches.`\n : null\n );\n setBulkSuccess(null);\n setPendingFeedback(null);\n };\n reader.onerror = () => {\n setBulkError('Failed to read CSV file.');\n };\n reader.readAsText(file);\n };\n\n const handleBulkInvite = async () => {\n if (readyRows.length === 0 || isSubmittingBulk) {\n return;\n }\n\n setBulkError(null);\n setBulkSuccess(null);\n setPendingFeedback(null);\n setIsSubmittingBulk(true);\n setRows((prev) =>\n prev.map((row) =>\n row.errors.length === 0 && row.status.kind !== 'invited'\n ? { ...row, status: { kind: 'sending' } }\n : row\n )\n );\n\n try {\n await bulkCreateInvitations(\n readyRows.map((row) =>\n buildInvitationPayload(workspaceId, row.email, selectedRoleIds, selectedProjectIds)\n )\n );\n\n setRows((prev) =>\n prev.map((row) =>\n row.status.kind === 'sending' ? { ...row, status: { kind: 'invited' } } : row\n )\n );\n setBulkSuccess(\n `Sent ${readyRows.length} workspace invitation${readyRows.length === 1 ? '' : 's'}.`\n );\n } catch (error) {\n const message = formatInviteError(error);\n setRows((prev) =>\n prev.map((row) =>\n row.status.kind === 'sending' ? { ...row, status: { kind: 'failed', message } } : row\n )\n );\n setBulkError(message);\n } finally {\n setIsSubmittingBulk(false);\n }\n };\n\n const handleResendInvitation = (invitation: WorkspaceInvitation) => {\n setPendingFeedback(null);\n setInvitationActionId(invitation.id);\n resendInvitation(invitation.id, {\n onSuccess: () => {\n setPendingFeedback({\n tone: 'success',\n message: `Invitation resent to ${invitation.email}.`,\n });\n },\n onError: (error) => {\n setPendingFeedback({ tone: 'error', message: formatInviteError(error) });\n },\n onSettled: () => setInvitationActionId(null),\n });\n };\n\n const handleConfirmCancel = () => {\n if (!cancelTarget) {\n return;\n }\n\n setPendingFeedback(null);\n setInvitationActionId(cancelTarget.id);\n cancelInvitation(cancelTarget.id, {\n onSuccess: () => {\n setPendingFeedback({\n tone: 'success',\n message: `Invitation cancelled for ${cancelTarget.email}.`,\n });\n setCancelTarget(null);\n },\n onError: (error) => {\n setPendingFeedback({ tone: 'error', message: formatInviteError(error) });\n },\n onSettled: () => setInvitationActionId(null),\n });\n };\n\n return (\n <>\n <AlertDialog\n open={Boolean(cancelTarget)}\n onOpenChange={(open) => !open && setCancelTarget(null)}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Cancel pending invitation?</AlertDialogTitle>\n <AlertDialogDescription>\n {cancelTarget\n ? `This will revoke the pending invite for ${cancelTarget.email}. They will need a new invitation to join later.`\n : 'This will revoke the pending invitation.'}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={isCancellingInvitation}>Keep invitation</AlertDialogCancel>\n <AlertDialogAction onClick={handleConfirmCancel} disabled={isCancellingInvitation}>\n {isCancellingInvitation ? <Loader2 className=\"size-4 animate-spin\" /> : null}\n Cancel invitation\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n <div className=\"space-y-6 p-3 sm:p-6\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n <div>\n <Button\n variant=\"ghost\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}`))}\n className=\"-ml-2 mb-2 gap-2\"\n >\n <ArrowLeft size={18} />\n {tWithFallback(t, 'workspaceInvite.backToWorkspace', 'Back to Workspace')}\n </Button>\n <div className=\"flex items-center gap-2\">\n <UserPlus className=\"text-primary\" size={24} />\n <h1 className=\"text-2xl font-bold sm:text-3xl\">\n {tWithFallback(t, 'workspaceInvite.title', 'Invite Members')}\n </h1>\n </div>\n <p className=\"mt-2 text-sm text-text-secondary sm:text-base\">\n {workspace.name} —{' '}\n {tWithFallback(\n t,\n 'workspaceInvite.subtitle',\n 'Invite people into this workspace and optionally grant project access.'\n )}\n </p>\n </div>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>\n {tWithFallback(t, 'workspaceInvite.setupTitle', 'Access to grant on acceptance')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <WorkspaceSelectorSection\n selectedRoleIds={selectedRoleIds}\n onRoleToggle={handleRoleToggle}\n selectedProjectIds={selectedProjectIds}\n onProjectToggle={handleProjectToggle}\n availableRoles={availableRoles}\n availableProjects={availableProjects}\n isLoadingRoles={isLoadingRoles}\n isLoadingProjects={isLoadingProjects}\n disabled={isMutatingInvite}\n />\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Selection summary</CardTitle>\n </CardHeader>\n <CardContent className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2 rounded-lg border p-4\">\n <div className=\"flex items-center justify-between gap-2\">\n <p className=\"text-sm font-medium\">Roles</p>\n <Badge variant=\"secondary\">{selectedRoleNames.length}</Badge>\n </div>\n <p className=\"text-sm text-text-secondary\">\n {formatSelectionSummary(selectedRoleNames, 'No roles selected')}\n </p>\n </div>\n <div className=\"space-y-2 rounded-lg border p-4\">\n <div className=\"flex items-center justify-between gap-2\">\n <p className=\"text-sm font-medium\">Projects</p>\n <Badge variant=\"secondary\">{selectedProjectNames.length}</Badge>\n </div>\n <p className=\"text-sm text-text-secondary\">\n {formatSelectionSummary(\n selectedProjectNames,\n 'No project access will be granted automatically'\n )}\n </p>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>\n {tWithFallback(t, 'workspaceInvite.sendTitle', 'Send invitations')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <Tabs\n value={mode}\n onValueChange={(value) => setMode(value as InviteMode)}\n className=\"space-y-5\"\n >\n <TabsList>\n <TabsTrigger value=\"single\">\n {tWithFallback(t, 'workspaceInvite.modeSingle', 'Single')}\n </TabsTrigger>\n <TabsTrigger value=\"bulk\">\n {tWithFallback(t, 'workspaceInvite.modeBulk', 'Bulk CSV')}\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"single\" className=\"space-y-4\">\n {singleError ? (\n <div className=\"flex items-center gap-2 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span>{singleError}</span>\n </div>\n ) : null}\n {singleSuccess ? (\n <div className=\"flex items-center gap-2 rounded-md border border-green-500/20 bg-green-500/10 p-3 text-sm text-green-700 dark:text-green-400\">\n <CheckCircle2 className=\"size-4 shrink-0\" />\n <span>{singleSuccess}</span>\n </div>\n ) : null}\n\n <form onSubmit={handleSingleInvite} className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label htmlFor=\"workspace-invite-email\" className=\"text-sm font-medium\">\n {tWithFallback(t, 'workspaceInvite.emailLabel', 'Email Address')}\n </label>\n <Input\n id=\"workspace-invite-email\"\n type=\"email\"\n placeholder=\"member@example.com\"\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n disabled={isCreatingInvitation}\n />\n </div>\n\n <div className=\"flex justify-end\">\n <Button type=\"submit\" disabled={isCreatingInvitation} className=\"gap-2\">\n {isCreatingInvitation ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Send className=\"size-4\" />\n )}\n {tWithFallback(t, 'workspaceInvite.sendSingleButton', 'Send Invitation')}\n </Button>\n </div>\n </form>\n </TabsContent>\n\n <TabsContent value=\"bulk\" className=\"space-y-5\">\n {bulkError ? (\n <div className=\"flex items-center gap-2 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span>{bulkError}</span>\n </div>\n ) : null}\n {bulkSuccess ? (\n <div className=\"flex items-center gap-2 rounded-md border border-green-500/20 bg-green-500/10 p-3 text-sm text-green-700 dark:text-green-400\">\n <CheckCircle2 className=\"size-4 shrink-0\" />\n <span>{bulkSuccess}</span>\n </div>\n ) : null}\n\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"space-y-1\">\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'workspaceInvite.bulkDescription',\n 'Upload a CSV with one email column. All valid rows will receive the currently selected roles and projects.'\n )}\n </p>\n <div className=\"rounded-md border bg-bg-sunken/30 px-3 py-2 text-xs text-text-secondary\">\n <p className=\"font-medium text-text-primary\">Sample format</p>\n <pre className=\"mt-1 whitespace-pre-wrap\">\n email{`\\n`}alice@example.com{`\\n`}bob@example.com\n </pre>\n </div>\n {(projectList?.total ?? 0) > availableProjects.length ? (\n <p className=\"text-xs text-text-secondary\">\n Showing the first {availableProjects.length} active projects. Narrow the\n workspace project list if you need to invite into more.\n </p>\n ) : null}\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={downloadWorkspaceBulkInviteTemplate}\n className=\"gap-2\"\n >\n <Download size={16} />\n {tWithFallback(t, 'workspaceInvite.downloadTemplate', 'Download template')}\n </Button>\n </div>\n\n <div\n onDragOver={(event) => {\n event.preventDefault();\n setDragActive(true);\n }}\n onDragLeave={() => setDragActive(false)}\n onDrop={(event) => {\n event.preventDefault();\n setDragActive(false);\n const file = event.dataTransfer.files?.[0];\n if (file) {\n handleFile(file);\n } else {\n setBulkError('Please upload a .csv file.');\n }\n }}\n className={`flex flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed p-8 transition-colors ${\n dragActive ? 'border-primary bg-action-primary-bg/5' : 'border-border-subtle bg-bg-surface'\n }`}\n >\n <Upload size={28} className=\"text-text-secondary\" />\n <div className=\"text-center\">\n <p className=\"text-sm font-medium\">\n {fileName || 'Drop a CSV file here or click to browse'}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n CSV files only. Required column: email.\n </p>\n </div>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".csv,text/csv\"\n onChange={(event) => {\n const file = event.target.files?.[0];\n if (file) {\n handleFile(file);\n }\n event.target.value = '';\n }}\n className=\"hidden\"\n aria-label=\"Choose CSV file\"\n />\n <Button type=\"button\" onClick={() => fileInputRef.current?.click()}>\n {tWithFallback(t, 'workspaceInvite.chooseFile', 'Choose file')}\n </Button>\n </div>\n\n {rows.length > 0 ? (\n <>\n <div className=\"flex flex-wrap gap-4 text-xs text-text-secondary\">\n <span>Total rows: {rows.length}</span>\n <span>Ready: {readyRows.length}</span>\n <span>With errors: {invalidRows.length}</span>\n {invalidRows.length > 0 ? (\n <Badge variant=\"destructive\">Invalid rows will be skipped</Badge>\n ) : null}\n </div>\n {invalidRows.length > 0 ? (\n <div className=\"rounded-md border border-status-error-border/20 bg-status-error-bg/10 px-3 py-2 text-sm text-status-error-text\">\n {invalidRows.length} row{invalidRows.length === 1 ? '' : 's'} need\n attention. Only valid rows can be invited.\n </div>\n ) : null}\n <div className=\"overflow-hidden rounded-xl border\">\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full\">\n <thead className=\"bg-bg-sunken/40\">\n <tr>\n <th className=\"px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n #\n </th>\n <th className=\"px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n Email\n </th>\n <th className=\"px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n Status\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y\">\n {rows.map((row) => {\n const hasParseErrors = row.errors.length > 0;\n return (\n <tr\n key={`${row.rowNumber}-${row.email}`}\n className={hasParseErrors ? 'bg-status-error-bg/5' : undefined}\n >\n <td className=\"px-3 py-2 text-xs text-text-secondary\">\n {row.rowNumber}\n </td>\n <td className=\"px-3 py-2 text-sm\">\n {row.email || (\n <span className=\"italic text-text-secondary\">(empty)</span>\n )}\n </td>\n <td className=\"px-3 py-2 text-xs\">\n {hasParseErrors ? (\n <div className=\"space-y-1 text-status-error-text\">\n {row.errors.map((errorText) => (\n <span\n key={`${row.rowNumber}-${errorText}`}\n className=\"inline-flex items-center gap-1 pr-3\"\n >\n <XCircle size={14} />\n {errorText}\n </span>\n ))}\n </div>\n ) : row.status.kind === 'sending' ? (\n <span className=\"inline-flex items-center gap-1 text-text-secondary\">\n <Loader2 size={14} className=\"animate-spin\" />\n Sending...\n </span>\n ) : row.status.kind === 'invited' ? (\n <span className=\"inline-flex items-center gap-1 text-green-700 dark:text-green-400\">\n <CheckCircle2 size={14} />\n Invited\n </span>\n ) : row.status.kind === 'failed' ? (\n <span className=\"inline-flex items-center gap-1 text-status-error-text\">\n <XCircle size={14} />\n {row.status.message}\n </span>\n ) : (\n <span className=\"text-text-secondary\">Ready</span>\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </div>\n </>\n ) : (\n <div className=\"rounded-xl border border-dashed p-6 text-center text-sm text-text-secondary\">\n Upload a CSV to preview invite rows here before sending.\n </div>\n )}\n\n <div className=\"flex justify-end\">\n <Button\n type=\"button\"\n onClick={handleBulkInvite}\n disabled={readyRows.length === 0 || isCreatingBulkInvites || isSubmittingBulk}\n className=\"gap-2\"\n >\n {isCreatingBulkInvites || isSubmittingBulk ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Send className=\"size-4\" />\n )}\n {tWithFallback(t, 'workspaceInvite.sendBulkButton', 'Send Bulk Invites')}\n </Button>\n </div>\n </TabsContent>\n </Tabs>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Mail className=\"size-5 text-primary\" />\n {tWithFallback(t, 'workspaceInvite.pendingTitle', 'Pending Invitations')}\n <Badge variant=\"secondary\">{pendingInvitations.length}</Badge>\n </CardTitle>\n </CardHeader>\n <CardContent>\n {pendingFeedback ? (\n <div\n className={`mb-4 flex items-center gap-2 rounded-md border p-3 text-sm ${\n pendingFeedback.tone === 'success'\n ? 'border-green-500/20 bg-green-500/10 text-green-700 dark:text-green-400'\n : 'border-status-error-border/20 bg-status-error-bg/10 text-status-error-text'\n }`}\n >\n {pendingFeedback.tone === 'success' ? (\n <CheckCircle2 className=\"size-4 shrink-0\" />\n ) : (\n <AlertCircle className=\"size-4 shrink-0\" />\n )}\n <span>{pendingFeedback.message}</span>\n </div>\n ) : null}\n {isLoadingInvitations ? (\n <div className=\"space-y-3\">\n <Skeleton className=\"h-20 w-full\" />\n <Skeleton className=\"h-20 w-full\" />\n </div>\n ) : invitationsError && pendingInvitations.length === 0 ? (\n <div\n role=\"alert\"\n className=\"space-y-3 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\"\n >\n <p>\n {tWithFallback(\n t,\n 'workspaceInvite.pendingLoadFailed',\n 'Failed to load pending invitations for this workspace.'\n )}\n </p>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"w-fit\"\n disabled={isFetchingInvitations}\n onClick={() => {\n void refetchInvitations();\n }}\n >\n {isFetchingInvitations\n ? tWithFallback(t, 'workspaceInvite.retrying', 'Retrying…')\n : tWithFallback(t, 'workspaceInvite.tryAgain', 'Try again')}\n </Button>\n </div>\n ) : pendingInvitations.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'workspaceInvite.pendingEmpty',\n 'No pending invitations for this workspace.'\n )}\n </p>\n ) : (\n <div className=\"space-y-3\">\n {invitationsError ? (\n <div\n role=\"alert\"\n className=\"flex flex-wrap items-center justify-between gap-3 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\"\n >\n <span>\n {tWithFallback(\n t,\n 'workspaceInvite.pendingStaleWarning',\n 'Pending invitations may be outdated. Try again to refresh this list.'\n )}\n </span>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"w-fit\"\n disabled={isFetchingInvitations}\n onClick={() => {\n void refetchInvitations();\n }}\n >\n {isFetchingInvitations\n ? tWithFallback(t, 'workspaceInvite.retrying', 'Retrying…')\n : tWithFallback(t, 'workspaceInvite.tryAgain', 'Try again')}\n </Button>\n </div>\n ) : null}\n {pendingInvitations.map((invitation) => {\n const roleNames = getRoleDisplayNames(invitation);\n const projectNames = getProjectDisplayNames(invitation);\n const isRunningAction =\n invitationActionId === invitation.id &&\n (isResendingInvitation || isCancellingInvitation);\n\n return (\n <div key={invitation.id} className=\"rounded-lg border p-4\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <p className=\"font-semibold\">{invitation.email}</p>\n <Badge variant=\"outline\">{invitation.status}</Badge>\n </div>\n <div className=\"grid gap-3 text-sm text-text-secondary sm:grid-cols-2\">\n <div>\n <p className=\"font-medium text-text-primary\">Received</p>\n <p>{formatDate(invitation.createdAt) ?? '—'}</p>\n </div>\n <div>\n <p className=\"font-medium text-text-primary\">Expires</p>\n <p>{formatDate(invitation.expiresAt) ?? '—'}</p>\n </div>\n </div>\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">Roles</p>\n <div className=\"flex flex-wrap gap-2\">\n {roleNames.length > 0 ? (\n roleNames.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 No roles selected\n </span>\n )}\n </div>\n </div>\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">Projects</p>\n <div className=\"flex flex-wrap gap-2\">\n {projectNames.length > 0 ? (\n projectNames.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 No project access will be granted automatically\n </span>\n )}\n </div>\n </div>\n </div>\n <div className=\"flex gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled={isResendingInvitation || isCancellingInvitation}\n onClick={() => handleResendInvitation(invitation)}\n className=\"gap-2\"\n >\n {isRunningAction && isResendingInvitation ? (\n <RefreshCw className=\"size-4 animate-spin\" />\n ) : (\n <RefreshCw className=\"size-4\" />\n )}\n Resend\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled={isResendingInvitation || isCancellingInvitation}\n onClick={() => setCancelTarget(invitation)}\n className=\"gap-2\"\n >\n {isRunningAction && isCancellingInvitation ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <XCircle className=\"size-4\" />\n )}\n Cancel\n </Button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </>\n );\n}\n\nexport function WorkspaceInvitePage() {\n const { workspaceId: routeWorkspaceId } = useParams<{ workspaceId: string }>();\n const { workspaceId: contextWorkspaceId } = useWorkspacesContext();\n const permissionScopeId = routeWorkspaceId || contextWorkspaceId || null;\n\n return (\n <PermissionProvider scopeId={permissionScopeId} gateway=\"workspace\" debug={false}>\n <WorkspaceInvitePageContent />\n </PermissionProvider>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"WorkspaceInvitePage.js","names":[],"sources":["../../src/pages/WorkspaceInvitePage.tsx"],"sourcesContent":["import { useMemo, useRef, useState } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport {\n AlertCircle,\n ArrowLeft,\n CheckCircle2,\n Download,\n Loader2,\n Mail,\n RefreshCw,\n Send,\n Upload,\n UserPlus,\n XCircle,\n} from 'lucide-react';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Checkbox,\n Input,\n Skeleton,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n ResponsiveTable,\n} from '@burdenoff/fe-libs/ui';\nimport type { ResponsiveTableColumn } from '@burdenoff/fe-libs/ui';\nimport { PermissionProvider } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { getProjectDisplayNames, getRoleDisplayNames } from '../utils/invitationDisplay';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useWorkspace } from '../hooks/useWorkspaces';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspaceRoles } from '../hooks/useWorkspaceRoles';\nimport { useProjects } from '../hooks/useProjects';\nimport { useWorkspaceInvitations } from '../hooks/useWorkspaceMembers';\nimport {\n useBulkCreateInvitations,\n useCancelInvitation,\n useCreateInvitation,\n useResendInvitation,\n} from '../hooks/useMemberMutations';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport type { Project, WorkspaceInvitation } from '../types';\nimport {\n downloadWorkspaceBulkInviteTemplate,\n parseWorkspaceBulkInviteCsv,\n type WorkspaceBulkInviteRow,\n} from '../utils/workspaceBulkInviteCsv';\n\ntype InviteMode = 'single' | 'bulk';\n\ntype RowStatus =\n | { kind: 'idle' }\n | { kind: 'sending' }\n | { kind: 'invited' }\n | { kind: 'failed'; message: string };\n\ninterface PreviewRow extends WorkspaceBulkInviteRow {\n status: RowStatus;\n}\n\ntype FeedbackState = {\n tone: 'success' | 'error';\n message: string;\n} | null;\n\nconst MAX_BULK_INVITE_FILE_SIZE = 1024 * 1024;\nconst MAX_BULK_INVITE_ROWS = 500;\n\nconst previewColumns: ResponsiveTableColumn<PreviewRow>[] = [\n {\n key: 'rowNumber',\n header: '#',\n priority: 'secondary',\n headerClassName: 'text-xs font-semibold uppercase tracking-wide text-text-secondary',\n cellClassName: 'text-xs text-text-secondary tabular-nums',\n cell: (row) => row.rowNumber,\n },\n {\n key: 'email',\n header: 'Email',\n priority: 'primary',\n headerClassName: 'text-xs font-semibold uppercase tracking-wide text-text-secondary',\n cellClassName: 'text-sm',\n cell: (row) => row.email || <span className=\"italic text-text-secondary\">(empty)</span>,\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'secondary',\n headerClassName: 'text-xs font-semibold uppercase tracking-wide text-text-secondary',\n cellClassName: 'text-xs',\n cell: (row) => {\n const hasParseErrors = row.errors.length > 0;\n if (hasParseErrors) {\n return (\n <div className=\"space-y-1 text-status-error-text\">\n {row.errors.map((errorText) => (\n <span\n key={`${row.rowNumber}-${errorText}`}\n className=\"inline-flex items-center gap-1 pr-3\"\n >\n <XCircle size={14} />\n {errorText}\n </span>\n ))}\n </div>\n );\n }\n if (row.status.kind === 'sending') {\n return (\n <span className=\"inline-flex items-center gap-1 text-text-secondary\">\n <Loader2 size={14} className=\"animate-spin\" />\n Sending...\n </span>\n );\n }\n if (row.status.kind === 'invited') {\n return (\n <span className=\"inline-flex items-center gap-1 text-green-700 dark:text-green-400\">\n <CheckCircle2 size={14} />\n Invited\n </span>\n );\n }\n if (row.status.kind === 'failed') {\n return (\n <span className=\"inline-flex items-center gap-1 text-status-error-text\">\n <XCircle size={14} />\n {row.status.message}\n </span>\n );\n }\n return <span className=\"text-text-secondary\">Ready</span>;\n },\n },\n];\n\nfunction isCsvFile(file: File): boolean {\n return file.type === 'text/csv' || file.name.toLowerCase().endsWith('.csv');\n}\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\nfunction formatInviteError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n if (message.includes('CONFLICT')) {\n return 'An invite already exists for this email address.';\n }\n if (message.includes('already a member')) {\n return 'This user is already a member of the workspace.';\n }\n return 'Something went wrong. Please try again or contact support.';\n}\n\nfunction formatSelectionSummary(\n items: string[],\n emptyLabel: string,\n formatter?: (value: string) => string\n) {\n if (items.length === 0) {\n return emptyLabel;\n }\n\n return items.map((item) => (formatter ? formatter(item) : item)).join(', ');\n}\n\nfunction buildInvitationPayload(\n workspaceId: string,\n email: string,\n roleIds: string[],\n projectIds: string[]\n) {\n return {\n email,\n workspaceId,\n roleIds,\n ...(projectIds.length > 0\n ? {\n context: {\n type: 'workspace.invitation',\n metadata: { projectIds },\n },\n }\n : {}),\n };\n}\n\nfunction WorkspaceSelectorSection({\n selectedRoleIds,\n onRoleToggle,\n selectedProjectIds,\n onProjectToggle,\n availableRoles,\n availableProjects,\n isLoadingRoles,\n isLoadingProjects,\n disabled,\n}: {\n selectedRoleIds: string[];\n onRoleToggle: (roleId: string, checked: boolean) => void;\n selectedProjectIds: string[];\n onProjectToggle: (projectId: string, checked: boolean) => void;\n availableRoles: Array<{\n id: string;\n name: string;\n description?: string | null;\n isSystem: boolean;\n }>;\n availableProjects: Project[];\n isLoadingRoles: boolean;\n isLoadingProjects: boolean;\n disabled: boolean;\n}) {\n return (\n <div className=\"space-y-6\">\n <div className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"space-y-1\">\n <h3 className=\"text-base font-semibold\">Roles (Optional)</h3>\n <p className=\"text-sm text-text-secondary\">\n Choose roles. Hover any role to preview its full description.\n </p>\n </div>\n {selectedRoleIds.length > 0 ? (\n <Badge variant=\"secondary\">{selectedRoleIds.length} selected</Badge>\n ) : null}\n </div>\n <TooltipProvider>\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3\">\n {isLoadingRoles ? (\n <p className=\"text-sm text-text-secondary\">Loading roles…</p>\n ) : availableRoles.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No assignable roles were returned for this workspace.\n </p>\n ) : (\n availableRoles.map((role) => {\n const isSelected = selectedRoleIds.includes(role.id);\n return (\n <Tooltip key={role.id}>\n <TooltipTrigger asChild>\n <label\n className={`flex cursor-pointer items-center justify-between gap-3 rounded-lg border px-3 py-3 text-left text-sm font-medium transition-colors focus-within:ring-2 focus-within:ring-[var(--color-focus-ring)] focus-within:ring-offset-2 ${\n isSelected\n ? 'border-primary bg-action-primary-bg/10 text-text-primary shadow-sm'\n : 'border-border-subtle bg-bg-surface text-text-primary hover:bg-accent'\n } ${disabled ? 'opacity-70' : ''}`}\n >\n <span className=\"min-w-0 flex-1 truncate\">{role.name}</span>\n <Checkbox\n checked={isSelected}\n disabled={disabled}\n onCheckedChange={(checked) => onRoleToggle(role.id, Boolean(checked))}\n aria-label={`Select ${role.name}`}\n />\n </label>\n </TooltipTrigger>\n <TooltipContent className=\"max-w-xs text-sm leading-6\">\n <p className=\"font-medium\">{role.name}</p>\n <p className=\"mt-1 text-text-secondary\">\n {role.description || 'No description available for this role.'}\n </p>\n {role.isSystem ? <p className=\"mt-2 text-xs\">System role</p> : null}\n </TooltipContent>\n </Tooltip>\n );\n })\n )}\n </div>\n </TooltipProvider>\n </div>\n\n <div className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <div className=\"space-y-1\">\n <h3 className=\"text-base font-semibold\">Projects to add on acceptance (Optional)</h3>\n <p className=\"text-sm text-text-secondary\">\n The invited user will be added to these projects automatically after they accept.\n </p>\n </div>\n {selectedProjectIds.length > 0 ? (\n <Badge variant=\"secondary\">{selectedProjectIds.length} selected</Badge>\n ) : null}\n </div>\n <div className=\"grid grid-cols-1 gap-2 md:grid-cols-2 xl:grid-cols-3\">\n {isLoadingProjects ? (\n <p className=\"text-sm text-text-secondary\">Loading projects…</p>\n ) : availableProjects.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No active projects are available in this workspace yet.\n </p>\n ) : (\n availableProjects.map((project) => {\n const isSelected = selectedProjectIds.includes(project.id);\n return (\n <label\n key={project.id}\n className={`flex cursor-pointer items-start justify-between gap-3 rounded-lg border px-3 py-3 transition-colors focus-within:ring-2 focus-within:ring-[var(--color-focus-ring)] focus-within:ring-offset-2 ${\n isSelected\n ? 'border-primary bg-action-primary-bg/10 text-text-primary shadow-sm'\n : 'border-border-subtle bg-bg-surface text-text-primary hover:bg-accent'\n } ${disabled ? 'opacity-70' : ''}`}\n >\n <div className=\"min-w-0 flex-1 space-y-1\">\n <p className=\"truncate text-sm font-semibold leading-tight\">{project.name}</p>\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n <span className=\"truncate font-mono\">{project.key}</span>\n <Badge variant=\"outline\">{project.status}</Badge>\n </div>\n </div>\n <Checkbox\n checked={isSelected}\n disabled={disabled}\n onCheckedChange={(checked) => onProjectToggle(project.id, Boolean(checked))}\n aria-label={`Select ${project.name}`}\n />\n </label>\n );\n })\n )}\n </div>\n </div>\n </div>\n );\n}\n\nfunction WorkspaceInvitePageContent() {\n const { workspaceId: routeWorkspaceId } = useParams<{ workspaceId: string }>();\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/workspaces', workspaceId: contextWorkspaceId } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const workspaceId = routeWorkspaceId || contextWorkspaceId || '';\n\n useWorkspacesPageView('workspaces.invite', { workspaceId: workspaceId || undefined });\n\n const { canInviteMember, isLoading: permissionsLoading } = useWorkspacePermissions();\n const {\n data: workspace,\n isLoading: isLoadingWorkspace,\n error: workspaceError,\n } = useWorkspace(workspaceId);\n const { data: availableRoles = [], isLoading: isLoadingRoles } = useWorkspaceRoles(workspaceId);\n const { data: projectList, isLoading: isLoadingProjects } = useProjects(\n { archived: false },\n { limit: 100 },\n workspaceId\n );\n const {\n data: invitationsData,\n isLoading: isLoadingInvitations,\n isFetching: isFetchingInvitations,\n error: invitationsError,\n refetch: refetchInvitations,\n } = useWorkspaceInvitations(workspaceId, 'PENDING', { limit: 50 }, { retry: false });\n const availableProjects = useMemo(\n () => (projectList?.items ?? []).filter((project) => !project.archived),\n [projectList?.items]\n );\n\n const [mode, setMode] = useState<InviteMode>('single');\n const [email, setEmail] = useState('');\n const [selectedRoleIds, setSelectedRoleIds] = useState<string[]>([]);\n const [selectedProjectIds, setSelectedProjectIds] = useState<string[]>([]);\n const [singleError, setSingleError] = useState<string | null>(null);\n const [singleSuccess, setSingleSuccess] = useState<string | null>(null);\n const [bulkError, setBulkError] = useState<string | null>(null);\n const [bulkSuccess, setBulkSuccess] = useState<string | null>(null);\n const [pendingFeedback, setPendingFeedback] = useState<FeedbackState>(null);\n const [rows, setRows] = useState<PreviewRow[]>([]);\n const [fileName, setFileName] = useState<string | null>(null);\n const [dragActive, setDragActive] = useState(false);\n const [isSubmittingBulk, setIsSubmittingBulk] = useState(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [cancelTarget, setCancelTarget] = useState<WorkspaceInvitation | null>(null);\n\n const { mutateAsync: createInvitation, isPending: isCreatingInvitation } =\n useCreateInvitation(workspaceId);\n const { mutateAsync: bulkCreateInvitations, isPending: isCreatingBulkInvites } =\n useBulkCreateInvitations();\n const { mutate: resendInvitation, isPending: isResendingInvitation } =\n useResendInvitation(workspaceId);\n const { mutate: cancelInvitation, isPending: isCancellingInvitation } =\n useCancelInvitation(workspaceId);\n const [invitationActionId, setInvitationActionId] = useState<string | null>(null);\n\n const pendingInvitations = invitationsData?.items ?? [];\n const isMutatingInvite = isCreatingInvitation || isCreatingBulkInvites || isSubmittingBulk;\n const selectedRoleNames = useMemo(\n () =>\n availableRoles.filter((role) => selectedRoleIds.includes(role.id)).map((role) => role.name),\n [availableRoles, selectedRoleIds]\n );\n const selectedProjectNames = useMemo(\n () =>\n availableProjects\n .filter((project) => selectedProjectIds.includes(project.id))\n .map((project) => project.name),\n [availableProjects, selectedProjectIds]\n );\n\n const readyRows = useMemo(\n () => rows.filter((row) => row.errors.length === 0 && row.status.kind !== 'invited'),\n [rows]\n );\n const invalidRows = useMemo(() => rows.filter((row) => row.errors.length > 0), [rows]);\n\n if (!workspaceId) {\n return (\n <div className=\"space-y-4 p-6\">\n <div className=\"rounded-xl border bg-bg-surface p-8 text-center\">\n <h1 className=\"text-2xl font-bold\">\n {tWithFallback(t, 'workspaceInvite.noWorkspaceTitle', 'Select a workspace first')}\n </h1>\n <p className=\"mt-2 text-text-secondary\">\n {tWithFallback(\n t,\n 'workspaceInvite.noWorkspaceDescription',\n 'Choose a current workspace from the header or open a workspace detail page before inviting members.'\n )}\n </p>\n <div className=\"mt-4\">\n <Button\n onClick={() => navigate(withCurrentSearch(`${basePath}/list`))}\n variant=\"outline\"\n >\n {tWithFallback(t, 'workspaceInvite.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n </div>\n </div>\n </div>\n );\n }\n\n if (permissionsLoading || isLoadingWorkspace) {\n return (\n <div className=\"space-y-6 p-6\">\n <Skeleton className=\"h-10 w-48\" />\n <Skeleton className=\"h-40 w-full\" />\n <Skeleton className=\"h-80 w-full\" />\n </div>\n );\n }\n\n if (!canInviteMember) {\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"space-y-2 text-center\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'workspaceInvite.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'workspaceInvite.accessDeniedDescription',\n \"You don't have permission to invite members into this workspace.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (workspaceError || !workspace) {\n return (\n <div className=\"space-y-4 p-6\">\n <Button\n variant=\"ghost\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/list`))}\n className=\"gap-2\"\n >\n <ArrowLeft size={18} />\n {tWithFallback(t, 'workspaceInvite.backToWorkspaces', 'Back to Workspaces')}\n </Button>\n <div className=\"rounded-xl border bg-bg-surface p-8 text-center text-status-error-text\">\n {tWithFallback(\n t,\n 'workspaceInvite.workspaceLoadError',\n 'Unable to load workspace invite details.'\n )}\n </div>\n </div>\n );\n }\n\n const handleRoleToggle = (roleId: string, checked: boolean) => {\n setSelectedRoleIds((prev) =>\n checked\n ? [...prev, roleId].filter((value, index, array) => array.indexOf(value) === index)\n : prev.filter((id) => id !== roleId)\n );\n };\n\n const handleProjectToggle = (projectId: string, checked: boolean) => {\n setSelectedProjectIds((prev) =>\n checked\n ? [...prev, projectId].filter((value, index, array) => array.indexOf(value) === index)\n : prev.filter((id) => id !== projectId)\n );\n };\n\n const handleSingleInvite = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n setSingleError(null);\n setSingleSuccess(null);\n setPendingFeedback(null);\n\n if (!email.trim()) {\n setSingleError('Email is required.');\n return;\n }\n\n try {\n await createInvitation(\n buildInvitationPayload(\n workspaceId,\n email.trim().toLowerCase(),\n selectedRoleIds,\n selectedProjectIds\n )\n );\n setEmail('');\n setSingleSuccess(`Invitation sent to ${email.trim().toLowerCase()}.`);\n } catch (error) {\n setSingleError(formatInviteError(error));\n }\n };\n\n const handleFile = (file: File) => {\n if (!isCsvFile(file)) {\n setBulkError('Please upload a .csv file.');\n return;\n }\n\n if (file.size > MAX_BULK_INVITE_FILE_SIZE) {\n setBulkError('CSV file is too large (max 1 MB).');\n return;\n }\n\n const reader = new FileReader();\n reader.onload = () => {\n const text = typeof reader.result === 'string' ? reader.result : '';\n const parsed = parseWorkspaceBulkInviteCsv(text);\n const limitedRows = parsed.slice(0, MAX_BULK_INVITE_ROWS);\n setRows(limitedRows.map((row) => ({ ...row, status: { kind: 'idle' } })));\n setFileName(file.name);\n setBulkError(\n parsed.length > MAX_BULK_INVITE_ROWS\n ? `Only the first ${MAX_BULK_INVITE_ROWS} rows were loaded. Split larger CSV files into smaller batches.`\n : null\n );\n setBulkSuccess(null);\n setPendingFeedback(null);\n };\n reader.onerror = () => {\n setBulkError('Failed to read CSV file.');\n };\n reader.readAsText(file);\n };\n\n const handleBulkInvite = async () => {\n if (readyRows.length === 0 || isSubmittingBulk) {\n return;\n }\n\n setBulkError(null);\n setBulkSuccess(null);\n setPendingFeedback(null);\n setIsSubmittingBulk(true);\n setRows((prev) =>\n prev.map((row) =>\n row.errors.length === 0 && row.status.kind !== 'invited'\n ? { ...row, status: { kind: 'sending' } }\n : row\n )\n );\n\n try {\n await bulkCreateInvitations(\n readyRows.map((row) =>\n buildInvitationPayload(workspaceId, row.email, selectedRoleIds, selectedProjectIds)\n )\n );\n\n setRows((prev) =>\n prev.map((row) =>\n row.status.kind === 'sending' ? { ...row, status: { kind: 'invited' } } : row\n )\n );\n setBulkSuccess(\n `Sent ${readyRows.length} workspace invitation${readyRows.length === 1 ? '' : 's'}.`\n );\n } catch (error) {\n const message = formatInviteError(error);\n setRows((prev) =>\n prev.map((row) =>\n row.status.kind === 'sending' ? { ...row, status: { kind: 'failed', message } } : row\n )\n );\n setBulkError(message);\n } finally {\n setIsSubmittingBulk(false);\n }\n };\n\n const handleResendInvitation = (invitation: WorkspaceInvitation) => {\n setPendingFeedback(null);\n setInvitationActionId(invitation.id);\n resendInvitation(invitation.id, {\n onSuccess: () => {\n setPendingFeedback({\n tone: 'success',\n message: `Invitation resent to ${invitation.email}.`,\n });\n },\n onError: (error) => {\n setPendingFeedback({ tone: 'error', message: formatInviteError(error) });\n },\n onSettled: () => setInvitationActionId(null),\n });\n };\n\n const handleConfirmCancel = () => {\n if (!cancelTarget) {\n return;\n }\n\n setPendingFeedback(null);\n setInvitationActionId(cancelTarget.id);\n cancelInvitation(cancelTarget.id, {\n onSuccess: () => {\n setPendingFeedback({\n tone: 'success',\n message: `Invitation cancelled for ${cancelTarget.email}.`,\n });\n setCancelTarget(null);\n },\n onError: (error) => {\n setPendingFeedback({ tone: 'error', message: formatInviteError(error) });\n },\n onSettled: () => setInvitationActionId(null),\n });\n };\n\n return (\n <>\n <AlertDialog\n open={Boolean(cancelTarget)}\n onOpenChange={(open) => !open && setCancelTarget(null)}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Cancel pending invitation?</AlertDialogTitle>\n <AlertDialogDescription>\n {cancelTarget\n ? `This will revoke the pending invite for ${cancelTarget.email}. They will need a new invitation to join later.`\n : 'This will revoke the pending invitation.'}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={isCancellingInvitation}>Keep invitation</AlertDialogCancel>\n <AlertDialogAction onClick={handleConfirmCancel} disabled={isCancellingInvitation}>\n {isCancellingInvitation ? <Loader2 className=\"size-4 animate-spin\" /> : null}\n Cancel invitation\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n <div className=\"space-y-6 p-3 sm:p-6\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n <div>\n <Button\n variant=\"ghost\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}`))}\n className=\"-ml-2 mb-2 gap-2\"\n >\n <ArrowLeft size={18} />\n {tWithFallback(t, 'workspaceInvite.backToWorkspace', 'Back to Workspace')}\n </Button>\n <div className=\"flex items-center gap-2\">\n <UserPlus className=\"text-primary\" size={24} />\n <h1 className=\"text-2xl font-bold sm:text-3xl\">\n {tWithFallback(t, 'workspaceInvite.title', 'Invite Members')}\n </h1>\n </div>\n <p className=\"mt-2 text-sm text-text-secondary sm:text-base\">\n {workspace.name} —{' '}\n {tWithFallback(\n t,\n 'workspaceInvite.subtitle',\n 'Invite people into this workspace and optionally grant project access.'\n )}\n </p>\n </div>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>\n {tWithFallback(t, 'workspaceInvite.setupTitle', 'Access to grant on acceptance')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <WorkspaceSelectorSection\n selectedRoleIds={selectedRoleIds}\n onRoleToggle={handleRoleToggle}\n selectedProjectIds={selectedProjectIds}\n onProjectToggle={handleProjectToggle}\n availableRoles={availableRoles}\n availableProjects={availableProjects}\n isLoadingRoles={isLoadingRoles}\n isLoadingProjects={isLoadingProjects}\n disabled={isMutatingInvite}\n />\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Selection summary</CardTitle>\n </CardHeader>\n <CardContent className=\"grid gap-4 md:grid-cols-2\">\n <div className=\"space-y-2 rounded-lg border p-4\">\n <div className=\"flex items-center justify-between gap-2\">\n <p className=\"text-sm font-medium\">Roles</p>\n <Badge variant=\"secondary\">{selectedRoleNames.length}</Badge>\n </div>\n <p className=\"text-sm text-text-secondary\">\n {formatSelectionSummary(selectedRoleNames, 'No roles selected')}\n </p>\n </div>\n <div className=\"space-y-2 rounded-lg border p-4\">\n <div className=\"flex items-center justify-between gap-2\">\n <p className=\"text-sm font-medium\">Projects</p>\n <Badge variant=\"secondary\">{selectedProjectNames.length}</Badge>\n </div>\n <p className=\"text-sm text-text-secondary\">\n {formatSelectionSummary(\n selectedProjectNames,\n 'No project access will be granted automatically'\n )}\n </p>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>\n {tWithFallback(t, 'workspaceInvite.sendTitle', 'Send invitations')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <Tabs\n value={mode}\n onValueChange={(value) => setMode(value as InviteMode)}\n className=\"space-y-5\"\n >\n <TabsList>\n <TabsTrigger value=\"single\">\n {tWithFallback(t, 'workspaceInvite.modeSingle', 'Single')}\n </TabsTrigger>\n <TabsTrigger value=\"bulk\">\n {tWithFallback(t, 'workspaceInvite.modeBulk', 'Bulk CSV')}\n </TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"single\" className=\"space-y-4\">\n {singleError ? (\n <div className=\"flex items-center gap-2 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span>{singleError}</span>\n </div>\n ) : null}\n {singleSuccess ? (\n <div className=\"flex items-center gap-2 rounded-md border border-green-500/20 bg-green-500/10 p-3 text-sm text-green-700 dark:text-green-400\">\n <CheckCircle2 className=\"size-4 shrink-0\" />\n <span>{singleSuccess}</span>\n </div>\n ) : null}\n\n <form onSubmit={handleSingleInvite} className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label htmlFor=\"workspace-invite-email\" className=\"text-sm font-medium\">\n {tWithFallback(t, 'workspaceInvite.emailLabel', 'Email Address')}\n </label>\n <Input\n id=\"workspace-invite-email\"\n type=\"email\"\n placeholder=\"member@example.com\"\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n disabled={isCreatingInvitation}\n />\n </div>\n\n <div className=\"flex justify-end\">\n <Button type=\"submit\" disabled={isCreatingInvitation} className=\"gap-2\">\n {isCreatingInvitation ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Send className=\"size-4\" />\n )}\n {tWithFallback(t, 'workspaceInvite.sendSingleButton', 'Send Invitation')}\n </Button>\n </div>\n </form>\n </TabsContent>\n\n <TabsContent value=\"bulk\" className=\"space-y-5\">\n {bulkError ? (\n <div className=\"flex items-center gap-2 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\">\n <AlertCircle className=\"size-4 shrink-0\" />\n <span>{bulkError}</span>\n </div>\n ) : null}\n {bulkSuccess ? (\n <div className=\"flex items-center gap-2 rounded-md border border-green-500/20 bg-green-500/10 p-3 text-sm text-green-700 dark:text-green-400\">\n <CheckCircle2 className=\"size-4 shrink-0\" />\n <span>{bulkSuccess}</span>\n </div>\n ) : null}\n\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"space-y-1\">\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'workspaceInvite.bulkDescription',\n 'Upload a CSV with one email column. All valid rows will receive the currently selected roles and projects.'\n )}\n </p>\n <div className=\"rounded-md border bg-bg-sunken/30 px-3 py-2 text-xs text-text-secondary\">\n <p className=\"font-medium text-text-primary\">Sample format</p>\n <pre className=\"mt-1 whitespace-pre-wrap\">\n email{`\\n`}alice@example.com{`\\n`}bob@example.com\n </pre>\n </div>\n {(projectList?.total ?? 0) > availableProjects.length ? (\n <p className=\"text-xs text-text-secondary\">\n Showing the first {availableProjects.length} active projects. Narrow the\n workspace project list if you need to invite into more.\n </p>\n ) : null}\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={downloadWorkspaceBulkInviteTemplate}\n className=\"gap-2\"\n >\n <Download size={16} />\n {tWithFallback(t, 'workspaceInvite.downloadTemplate', 'Download template')}\n </Button>\n </div>\n\n <div\n onDragOver={(event) => {\n event.preventDefault();\n setDragActive(true);\n }}\n onDragLeave={() => setDragActive(false)}\n onDrop={(event) => {\n event.preventDefault();\n setDragActive(false);\n const file = event.dataTransfer.files?.[0];\n if (file) {\n handleFile(file);\n } else {\n setBulkError('Please upload a .csv file.');\n }\n }}\n className={`flex flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed p-8 transition-colors ${\n dragActive\n ? 'border-primary bg-action-primary-bg/5'\n : 'border-border-subtle bg-bg-surface'\n }`}\n >\n <Upload size={28} className=\"text-text-secondary\" />\n <div className=\"text-center\">\n <p className=\"text-sm font-medium\">\n {fileName || 'Drop a CSV file here or click to browse'}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n CSV files only. Required column: email.\n </p>\n </div>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\".csv,text/csv\"\n onChange={(event) => {\n const file = event.target.files?.[0];\n if (file) {\n handleFile(file);\n }\n event.target.value = '';\n }}\n className=\"hidden\"\n aria-label=\"Choose CSV file\"\n />\n <Button type=\"button\" onClick={() => fileInputRef.current?.click()}>\n {tWithFallback(t, 'workspaceInvite.chooseFile', 'Choose file')}\n </Button>\n </div>\n\n {rows.length > 0 ? (\n <>\n <div className=\"flex flex-wrap gap-4 text-xs text-text-secondary\">\n <span>Total rows: {rows.length}</span>\n <span>Ready: {readyRows.length}</span>\n <span>With errors: {invalidRows.length}</span>\n {invalidRows.length > 0 ? (\n <Badge variant=\"destructive\">Invalid rows will be skipped</Badge>\n ) : null}\n </div>\n {invalidRows.length > 0 ? (\n <div className=\"rounded-md border border-status-error-border/20 bg-status-error-bg/10 px-3 py-2 text-sm text-status-error-text\">\n {invalidRows.length} row{invalidRows.length === 1 ? '' : 's'} need\n attention. Only valid rows can be invited.\n </div>\n ) : null}\n <div className=\"overflow-hidden rounded-xl border\">\n <ResponsiveTable<PreviewRow>\n columns={previewColumns}\n data={rows}\n rowKey={(row) => `${row.rowNumber}-${row.email}`}\n />\n </div>\n </>\n ) : (\n <div className=\"rounded-xl border border-dashed p-6 text-center text-sm text-text-secondary\">\n Upload a CSV to preview invite rows here before sending.\n </div>\n )}\n\n <div className=\"flex justify-end\">\n <Button\n type=\"button\"\n onClick={handleBulkInvite}\n disabled={readyRows.length === 0 || isCreatingBulkInvites || isSubmittingBulk}\n className=\"gap-2\"\n >\n {isCreatingBulkInvites || isSubmittingBulk ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Send className=\"size-4\" />\n )}\n {tWithFallback(t, 'workspaceInvite.sendBulkButton', 'Send Bulk Invites')}\n </Button>\n </div>\n </TabsContent>\n </Tabs>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Mail className=\"size-5 text-primary\" />\n {tWithFallback(t, 'workspaceInvite.pendingTitle', 'Pending Invitations')}\n <Badge variant=\"secondary\">{pendingInvitations.length}</Badge>\n </CardTitle>\n </CardHeader>\n <CardContent>\n {pendingFeedback ? (\n <div\n className={`mb-4 flex items-center gap-2 rounded-md border p-3 text-sm ${\n pendingFeedback.tone === 'success'\n ? 'border-green-500/20 bg-green-500/10 text-green-700 dark:text-green-400'\n : 'border-status-error-border/20 bg-status-error-bg/10 text-status-error-text'\n }`}\n >\n {pendingFeedback.tone === 'success' ? (\n <CheckCircle2 className=\"size-4 shrink-0\" />\n ) : (\n <AlertCircle className=\"size-4 shrink-0\" />\n )}\n <span>{pendingFeedback.message}</span>\n </div>\n ) : null}\n {isLoadingInvitations ? (\n <div className=\"space-y-3\">\n <Skeleton className=\"h-20 w-full\" />\n <Skeleton className=\"h-20 w-full\" />\n </div>\n ) : invitationsError && pendingInvitations.length === 0 ? (\n <div\n role=\"alert\"\n className=\"space-y-3 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\"\n >\n <p>\n {tWithFallback(\n t,\n 'workspaceInvite.pendingLoadFailed',\n 'Failed to load pending invitations for this workspace.'\n )}\n </p>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"w-fit\"\n disabled={isFetchingInvitations}\n onClick={() => {\n void refetchInvitations();\n }}\n >\n {isFetchingInvitations\n ? tWithFallback(t, 'workspaceInvite.retrying', 'Retrying…')\n : tWithFallback(t, 'workspaceInvite.tryAgain', 'Try again')}\n </Button>\n </div>\n ) : pendingInvitations.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'workspaceInvite.pendingEmpty',\n 'No pending invitations for this workspace.'\n )}\n </p>\n ) : (\n <div className=\"space-y-3\">\n {invitationsError ? (\n <div\n role=\"alert\"\n className=\"flex flex-wrap items-center justify-between gap-3 rounded-md border border-status-error-border/20 bg-status-error-bg/10 p-3 text-sm text-status-error-text\"\n >\n <span>\n {tWithFallback(\n t,\n 'workspaceInvite.pendingStaleWarning',\n 'Pending invitations may be outdated. Try again to refresh this list.'\n )}\n </span>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"w-fit\"\n disabled={isFetchingInvitations}\n onClick={() => {\n void refetchInvitations();\n }}\n >\n {isFetchingInvitations\n ? tWithFallback(t, 'workspaceInvite.retrying', 'Retrying…')\n : tWithFallback(t, 'workspaceInvite.tryAgain', 'Try again')}\n </Button>\n </div>\n ) : null}\n {pendingInvitations.map((invitation) => {\n const roleNames = getRoleDisplayNames(invitation);\n const projectNames = getProjectDisplayNames(invitation);\n const isRunningAction =\n invitationActionId === invitation.id &&\n (isResendingInvitation || isCancellingInvitation);\n\n return (\n <div key={invitation.id} className=\"rounded-lg border p-4\">\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div className=\"space-y-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <p className=\"font-semibold\">{invitation.email}</p>\n <Badge variant=\"outline\">{invitation.status}</Badge>\n </div>\n <div className=\"grid gap-3 text-sm text-text-secondary sm:grid-cols-2\">\n <div>\n <p className=\"font-medium text-text-primary\">Received</p>\n <p>{formatDate(invitation.createdAt) ?? '—'}</p>\n </div>\n <div>\n <p className=\"font-medium text-text-primary\">Expires</p>\n <p>{formatDate(invitation.expiresAt) ?? '—'}</p>\n </div>\n </div>\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">Roles</p>\n <div className=\"flex flex-wrap gap-2\">\n {roleNames.length > 0 ? (\n roleNames.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 No roles selected\n </span>\n )}\n </div>\n </div>\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">Projects</p>\n <div className=\"flex flex-wrap gap-2\">\n {projectNames.length > 0 ? (\n projectNames.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 No project access will be granted automatically\n </span>\n )}\n </div>\n </div>\n </div>\n <div className=\"flex gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled={isResendingInvitation || isCancellingInvitation}\n onClick={() => handleResendInvitation(invitation)}\n className=\"gap-2\"\n >\n {isRunningAction && isResendingInvitation ? (\n <RefreshCw className=\"size-4 animate-spin\" />\n ) : (\n <RefreshCw className=\"size-4\" />\n )}\n Resend\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled={isResendingInvitation || isCancellingInvitation}\n onClick={() => setCancelTarget(invitation)}\n className=\"gap-2\"\n >\n {isRunningAction && isCancellingInvitation ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <XCircle className=\"size-4\" />\n )}\n Cancel\n </Button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </>\n );\n}\n\nexport function WorkspaceInvitePage() {\n const { workspaceId: routeWorkspaceId } = useParams<{ workspaceId: string }>();\n const { workspaceId: contextWorkspaceId } = useWorkspacesContext();\n const permissionScopeId = routeWorkspaceId || contextWorkspaceId || null;\n\n return (\n <PermissionProvider scopeId={permissionScopeId} gateway=\"workspace\" debug={false}>\n <WorkspaceInvitePageContent />\n </PermissionProvider>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"GroupsWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/GroupsWidget.tsx"],"sourcesContent":["import { Users, ArrowRight } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\n\nexport function GroupsWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const groups = workspaceData?.groups.items ?? [];\n const total = workspaceData?.groups.totalCount ?? 0;\n\n return (\n <WidgetWrapper\n title=\"Groups\"\n icon={<Users className=\"size-4\" />}\n size=\"small\"\n isLoading={isLoading}\n unavailable={!workspaceData}\n headerAction={\n <Button variant=\"ghost\" size=\"sm\" className=\"text-xs\" onClick={() => navigate('/groups')}>\n <ArrowRight className=\"size-3\" />\n </Button>\n }\n >\n {groups.length === 0 ? (\n <div className=\"text-center py-3\">\n <p className=\"text-xs text-text-secondary\">No groups yet</p>\n </div>\n ) : (\n <div className=\"space-y-1.5\">\n {groups.slice(0, 4).map((group) => (\n <button\n key={group.id}\n type=\"button\"\n className=\"w-full flex items-center gap-2 p-1.5 rounded hover:bg-accent/5 cursor-pointer transition-colors text-left\"\n onClick={() => navigate('/groups')}\n >\n <div className=\"size-7 rounded-full bg-accent-purple-subtle text-accent-purple flex items-center justify-center text-xs font-bold shrink-0\">\n {group.name.charAt(0).toUpperCase()}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{group.name}</p>\n {group.description && (\n <p className=\"text-[11px] text-text-secondary truncate\">{group.description}</p>\n )}\n </div>\n </button>\n ))}\n {total > 4 && (\n <p className=\"text-[11px] text-text-secondary text-center\">\n +{total - 4} more groups\n </p>\n )}\n </div>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;AAKA,SAAgB,EAAa,EAAE,kBAAe,cAAW,eAAyB;CAChF,IAAM,IAAS,GAAe,OAAO,SAAS,EAAE,EAC1C,IAAQ,GAAe,OAAO,cAAc;AAElD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;EAClC,MAAK;EACM;EACX,aAAa,CAAC;EACd,cACE,kBAAC,GAAD;GAAQ,SAAQ;GAAQ,MAAK;GAAK,WAAU;GAAU,eAAe,EAAS,UAAU;aACtF,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA;GAC1B,CAAA;YAGV,EAAO,WAAW,IACjB,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAAiB,CAAA;GACxD,CAAA,GAEN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,EAAO,MAAM,GAAG,EAAE,CAAC,KAAK,MACvB,kBAAC,UAAD;IAEE,MAAK;IACL,WAAU;IACV,eAAe,EAAS,UAAU;cAJpC,CAME,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAM,KAAK,OAAO,EAAE,CAAC,aAAa;KAC/B,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAkD,EAAM;MAAS,CAAA,EAC7E,EAAM,eACL,kBAAC,KAAD;MAAG,WAAU;gBAA4C,EAAM;MAAgB,CAAA,CAE7E;OACC;MAdF,EAAM,GAcJ,CACT,EACD,IAAQ,KACP,kBAAC,KAAD;IAAG,WAAU;cAAb;KAA2D;KACvD,IAAQ;KAAE;KACV;MAEF;;EAEM,CAAA"}
1
+ {"version":3,"file":"GroupsWidget.js","names":[],"sources":["../../../../src/pages/dashboard/widgets/GroupsWidget.tsx"],"sourcesContent":["import { Users, ArrowRight } from 'lucide-react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { WidgetWrapper } from '../WidgetWrapper';\nimport type { WidgetProps } from '../types';\n\nexport function GroupsWidget({ workspaceData, isLoading, navigate }: WidgetProps) {\n const groups = workspaceData?.groups.items ?? [];\n const total = workspaceData?.groups.totalCount ?? 0;\n\n return (\n <WidgetWrapper\n title=\"Groups\"\n icon={<Users className=\"size-4\" />}\n size=\"small\"\n isLoading={isLoading}\n unavailable={!workspaceData}\n headerAction={\n <Button variant=\"ghost\" size=\"sm\" className=\"text-xs\" onClick={() => navigate('/groups')}>\n <ArrowRight className=\"size-3\" />\n </Button>\n }\n >\n {groups.length === 0 ? (\n <div className=\"text-center py-3\">\n <p className=\"text-xs text-text-secondary\">No groups yet</p>\n </div>\n ) : (\n <div className=\"space-y-1.5\">\n {groups.slice(0, 4).map((group) => (\n <button\n key={group.id}\n type=\"button\"\n className=\"w-full flex items-center gap-2 p-1.5 rounded hover:bg-accent/5 cursor-pointer transition-colors text-left\"\n onClick={() => navigate('/groups')}\n >\n <div className=\"size-7 rounded-full bg-accent-purple-subtle text-accent-purple flex items-center justify-center text-xs font-bold shrink-0\">\n {group.name.charAt(0).toUpperCase()}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{group.name}</p>\n {group.description && (\n <p className=\"text-[11px] text-text-secondary truncate\">{group.description}</p>\n )}\n </div>\n </button>\n ))}\n {total > 4 && (\n <p className=\"text-[11px] text-text-secondary text-center\">+{total - 4} more groups</p>\n )}\n </div>\n )}\n </WidgetWrapper>\n );\n}\n"],"mappings":";;;;;AAKA,SAAgB,EAAa,EAAE,kBAAe,cAAW,eAAyB;CAChF,IAAM,IAAS,GAAe,OAAO,SAAS,EAAE,EAC1C,IAAQ,GAAe,OAAO,cAAc;AAElD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;EAClC,MAAK;EACM;EACX,aAAa,CAAC;EACd,cACE,kBAAC,GAAD;GAAQ,SAAQ;GAAQ,MAAK;GAAK,WAAU;GAAU,eAAe,EAAS,UAAU;aACtF,kBAAC,GAAD,EAAY,WAAU,UAAW,CAAA;GAC1B,CAAA;YAGV,EAAO,WAAW,IACjB,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAAiB,CAAA;GACxD,CAAA,GAEN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,EAAO,MAAM,GAAG,EAAE,CAAC,KAAK,MACvB,kBAAC,UAAD;IAEE,MAAK;IACL,WAAU;IACV,eAAe,EAAS,UAAU;cAJpC,CAME,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAM,KAAK,OAAO,EAAE,CAAC,aAAa;KAC/B,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAkD,EAAM;MAAS,CAAA,EAC7E,EAAM,eACL,kBAAC,KAAD;MAAG,WAAU;gBAA4C,EAAM;MAAgB,CAAA,CAE7E;OACC;MAdF,EAAM,GAcJ,CACT,EACD,IAAQ,KACP,kBAAC,KAAD;IAAG,WAAU;cAAb;KAA2D;KAAE,IAAQ;KAAE;KAAgB;MAErF;;EAEM,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-workspaces",
3
- "version": "2026.624.1",
3
+ "version": "2026.625.1",
4
4
  "description": "Workspaces microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [