@burdenoff/microfe-workspaces 2026.623.1 → 2026.624.2

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 (73) hide show
  1. package/dist/components/CreateWorkspaceDialog.js +5 -5
  2. package/dist/components/CreateWorkspaceDialog.js.map +1 -1
  3. package/dist/components/InviteMemberDialog.js +11 -11
  4. package/dist/components/InviteMemberDialog.js.map +1 -1
  5. package/dist/components/ProjectCard.js +5 -5
  6. package/dist/components/ProjectCard.js.map +1 -1
  7. package/dist/components/ProjectMemberPickerDialog.js +14 -14
  8. package/dist/components/ProjectMemberPickerDialog.js.map +1 -1
  9. package/dist/components/WorkspaceCard.js +3 -3
  10. package/dist/components/WorkspaceCard.js.map +1 -1
  11. package/dist/components/WorkspacesErrorBoundary.js +9 -9
  12. package/dist/components/WorkspacesErrorBoundary.js.map +1 -1
  13. package/dist/components/tags/ProjectTagsWidget.js +8 -8
  14. package/dist/components/tags/ProjectTagsWidget.js.map +1 -1
  15. package/dist/pages/ActivityListPage.js +12 -12
  16. package/dist/pages/ActivityListPage.js.map +1 -1
  17. package/dist/pages/CreateWorkspacePage.js +24 -24
  18. package/dist/pages/CreateWorkspacePage.js.map +1 -1
  19. package/dist/pages/InviteAcceptancePage.js +46 -46
  20. package/dist/pages/InviteAcceptancePage.js.map +1 -1
  21. package/dist/pages/MembersListPage.js +27 -27
  22. package/dist/pages/MembersListPage.js.map +1 -1
  23. package/dist/pages/PendingInvitesPage.js +23 -23
  24. package/dist/pages/PendingInvitesPage.js.map +1 -1
  25. package/dist/pages/ProjectDetailPage.js +20 -20
  26. package/dist/pages/ProjectDetailPage.js.map +1 -1
  27. package/dist/pages/ProjectMembersPage.js +15 -15
  28. package/dist/pages/ProjectMembersPage.js.map +1 -1
  29. package/dist/pages/ProjectsListPage.js +16 -16
  30. package/dist/pages/ProjectsListPage.js.map +1 -1
  31. package/dist/pages/WorkspaceDetailPage.js +16 -16
  32. package/dist/pages/WorkspaceDetailPage.js.map +1 -1
  33. package/dist/pages/WorkspaceInvitePage.js +369 -384
  34. package/dist/pages/WorkspaceInvitePage.js.map +1 -1
  35. package/dist/pages/WorkspaceSettingsPage.js +2 -2
  36. package/dist/pages/WorkspaceSettingsPage.js.map +1 -1
  37. package/dist/pages/WorkspacesListPage.js +8 -8
  38. package/dist/pages/WorkspacesListPage.js.map +1 -1
  39. package/dist/pages/dashboard/DashboardCustomizer.js +3 -3
  40. package/dist/pages/dashboard/DashboardCustomizer.js.map +1 -1
  41. package/dist/pages/dashboard/DashboardGrid.js +2 -2
  42. package/dist/pages/dashboard/DashboardGrid.js.map +1 -1
  43. package/dist/pages/dashboard/DashboardPage.js +5 -5
  44. package/dist/pages/dashboard/DashboardPage.js.map +1 -1
  45. package/dist/pages/dashboard/WidgetWrapper.js +6 -6
  46. package/dist/pages/dashboard/WidgetWrapper.js.map +1 -1
  47. package/dist/pages/dashboard/widgets/AgentsWidget.js +6 -6
  48. package/dist/pages/dashboard/widgets/AgentsWidget.js.map +1 -1
  49. package/dist/pages/dashboard/widgets/GroupsWidget.js +3 -3
  50. package/dist/pages/dashboard/widgets/GroupsWidget.js.map +1 -1
  51. package/dist/pages/dashboard/widgets/MembersWidget.js +3 -3
  52. package/dist/pages/dashboard/widgets/MembersWidget.js.map +1 -1
  53. package/dist/pages/dashboard/widgets/ProjectsWidget.js +2 -2
  54. package/dist/pages/dashboard/widgets/ProjectsWidget.js.map +1 -1
  55. package/dist/pages/dashboard/widgets/RecentActivityWidget.js +3 -3
  56. package/dist/pages/dashboard/widgets/RecentActivityWidget.js.map +1 -1
  57. package/dist/pages/dashboard/widgets/SessionsWidget.js +4 -4
  58. package/dist/pages/dashboard/widgets/SessionsWidget.js.map +1 -1
  59. package/dist/pages/dashboard/widgets/TagCloudWidget.js +2 -2
  60. package/dist/pages/dashboard/widgets/TagCloudWidget.js.map +1 -1
  61. package/dist/pages/dashboard/widgets/TipsWidget.js +3 -3
  62. package/dist/pages/dashboard/widgets/TipsWidget.js.map +1 -1
  63. package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js +2 -2
  64. package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js.map +1 -1
  65. package/dist/pages/dashboard/widgets/VibesWidget.js +2 -2
  66. package/dist/pages/dashboard/widgets/VibesWidget.js.map +1 -1
  67. package/dist/pages/dashboard/widgets/WelcomeWidget.js +3 -3
  68. package/dist/pages/dashboard/widgets/WelcomeWidget.js.map +1 -1
  69. package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js +4 -4
  70. package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js.map +1 -1
  71. package/dist/pages/dashboard/widgets/WorkspacesWidget.js +2 -2
  72. package/dist/pages/dashboard/widgets/WorkspacesWidget.js.map +1 -1
  73. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"CreateWorkspacePage.js","names":[],"sources":["../../src/pages/CreateWorkspacePage.tsx"],"sourcesContent":["import { useEffect, useMemo } from 'react';\nimport { ArrowLeft, AlertCircle } from 'lucide-react';\nimport { Link, useNavigate } from 'react-router-dom';\nimport {\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useForm, z } from '@burdenoff/fe-libs/form';\nimport { AccessDenied, UpgradeInlineBanner } from '@burdenoff/fe-libs/shared/components';\nimport { useUpgradePrompt } from '@burdenoff/fe-libs/shared/hooks';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useCreateWorkspace } from '../hooks/useWorkspaceMutations';\nimport { useMyTenants, useMyOrganizations } from '../hooks/useTenants';\nimport { useOrgsWithPermission } from '../hooks/useOrgsWithPermission';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { generateSlug } from '../utils/slug';\nimport { tWithFallback } from '../utils/i18n';\n\ntype CreateWorkspaceFormData = {\n name: string;\n slug: string;\n tenantId: string;\n organizationId: string;\n};\n\nexport function CreateWorkspacePage() {\n useWorkspacesPageView('workspaces.create');\n const { t } = useI18n();\n const {\n isFree,\n approachingLimits,\n plan: planSnapshot,\n } = useUpgradePrompt({ resource: 'workspaces' });\n const workspacesUsage = approachingLimits[0];\n const atWorkspaceCap =\n !!workspacesUsage &&\n typeof workspacesUsage.limit === 'number' &&\n workspacesUsage.current >= workspacesUsage.limit;\n const showUpgradeBanner = isFree && !!workspacesUsage;\n const createWorkspaceSchema = useMemo(\n () =>\n z.object({\n name: z\n .string()\n .min(\n 2,\n tWithFallback(\n t,\n 'createWorkspace.validation.nameMin',\n 'Name must be at least 2 characters'\n )\n )\n .max(100),\n slug: z\n .string()\n .min(\n 2,\n tWithFallback(\n t,\n 'createWorkspace.validation.slugMin',\n 'Slug must be at least 2 characters'\n )\n )\n .max(50)\n .regex(\n /^[a-z0-9-]+$/,\n tWithFallback(\n t,\n 'createWorkspace.validation.slugFormat',\n 'Slug can only contain lowercase letters, numbers, and hyphens'\n )\n ),\n tenantId: z\n .string()\n .min(\n 1,\n tWithFallback(t, 'createWorkspace.validation.tenantRequired', 'Please select a tenant')\n ),\n organizationId: z\n .string()\n .min(\n 1,\n tWithFallback(\n t,\n 'createWorkspace.validation.organizationRequired',\n 'Please select an organization'\n )\n ),\n }),\n [t]\n );\n\n const navigate = useNavigate();\n const { basePath = '/' } = useWorkspacesContext();\n const { mutate: createWorkspace, isPending } = useCreateWorkspace();\n const { data: tenantsData, isLoading: isLoadingTenants, error: tenantsError } = useMyTenants();\n const {\n data: organizationsData,\n isLoading: isLoadingOrganizations,\n error: organizationsError,\n } = useMyOrganizations();\n\n const tenants = useMemo(() => tenantsData?.items ?? [], [tenantsData]);\n const allOrganizations = useMemo(() => organizationsData?.items ?? [], [organizationsData]);\n\n const defaultTenantId = useMemo(() => {\n const sharedTenant = tenants.find(\n (tenant) => tenant.type === 'SHARED' || tenant.slug === 'burdenoff-shared'\n );\n return sharedTenant?.id ?? tenants[0]?.id ?? '';\n }, [tenants]);\n\n const form = useForm<CreateWorkspaceFormData>({\n schema: createWorkspaceSchema,\n defaultValues: {\n name: '',\n slug: '',\n tenantId: '',\n organizationId: '',\n },\n });\n\n const selectedTenantId = form.watch('tenantId');\n const workspaceName = form.watch('name');\n\n // Check workspace:create permission across ALL the user's orgs up front\n // (bulk-checked in a single rbac round-trip). This is the authoritative\n // gate — only orgs where the user can actually create a workspace make\n // it into the dropdown, and the page can decisively render the \"no\n // permissions anywhere\" state once the check resolves.\n const allOrgIds = useMemo(() => allOrganizations.map((o) => o.id), [allOrganizations]);\n const {\n allowedIds: workspaceCreateAllowedOrgIds,\n loading: isCheckingOrgPermissions,\n isError: orgPermissionsHadError,\n } = useOrgsWithPermission(allOrgIds, 'workspace', 'create');\n\n // Filter organizations by selected tenant AND permission. When the rbac\n // permission lookup errored we deliberately skip the permission filter\n // (see `orgPermissionsHadError` block below) — the gateway @rbac\n // directive on `createWorkspace` is the authoritative gate, so it's\n // safer to let the user pick from the full tenant list and hit a 403 on\n // submit than to lock them out of a flow they actually have access to.\n const tenantOrganizations = useMemo(() => {\n if (!selectedTenantId) return [];\n return allOrganizations.filter((org) => org.tenantId === selectedTenantId);\n }, [selectedTenantId, allOrganizations]);\n const filteredOrganizations = useMemo(() => {\n if (orgPermissionsHadError) return tenantOrganizations;\n return tenantOrganizations.filter((org) => workspaceCreateAllowedOrgIds.has(org.id));\n }, [tenantOrganizations, workspaceCreateAllowedOrgIds, orgPermissionsHadError]);\n\n // Auto-generate slug from name (clear when name is empty)\n useEffect(() => {\n form.setValue('slug', workspaceName ? generateSlug(workspaceName) : '');\n }, [workspaceName, form]);\n\n // Reset organizationId when tenant changes\n useEffect(() => {\n form.setValue('organizationId', '');\n }, [selectedTenantId, form]);\n\n useEffect(() => {\n if (!form.getValues('tenantId') && defaultTenantId) {\n form.setValue('tenantId', defaultTenantId, {\n shouldDirty: false,\n shouldTouch: false,\n });\n }\n }, [defaultTenantId, form]);\n\n useEffect(() => {\n const currentOrganizationId = form.getValues('organizationId');\n const hasCurrentOrganization = filteredOrganizations.some(\n (organization) => organization.id === currentOrganizationId\n );\n\n if (!hasCurrentOrganization && filteredOrganizations[0]?.id) {\n form.setValue('organizationId', filteredOrganizations[0].id, {\n shouldDirty: false,\n shouldTouch: false,\n });\n }\n }, [filteredOrganizations, form]);\n\n const onSubmit = (data: CreateWorkspaceFormData) => {\n createWorkspace(\n {\n name: data.name,\n slug: data.slug,\n tenantId: data.tenantId,\n organizationId: data.organizationId,\n },\n {\n onSuccess: (newWorkspace) => {\n // Navigate to the new workspace\n navigate(`${basePath}/${newWorkspace.id}`);\n },\n }\n );\n };\n\n // `workspace:create` is an org-scoped permission. Page-level `PermissionGate`\n // resolved at the outer shell scope and incorrectly denied users who *do*\n // have the permission inside at least one of their orgs. The authoritative\n // gate now lives on the org dropdown (only orgs where the user has\n // workspace:create are listed via useOrgsWithPermission) and on the gateway\n // @rbac directive enforcing the createWorkspace mutation. We still want a\n // clear \"you can't create anywhere\" empty state — once tenants and org\n // permission lookups have settled, if no org is eligible we render it.\n // The AccessDenied empty-state should ONLY render when we know with\n // certainty that the user has no creatable org. That requires:\n // 1. tenants + organizations queries both succeeded (otherwise an\n // empty `allOrganizations` is a data-fetch failure, not an authz\n // failure — masking it as \"permission denied\" hides the real\n // error and offers the user no path to retry),\n // 2. the rbac permission lookup also succeeded (errors there fall\n // through to the unfiltered form via the warning banner below),\n // 3. and even then, only when the user has at least one org but none\n // of them allow workspace:create.\n // If `allOrganizations` is empty because the user genuinely belongs to\n // no orgs, the inner \"no organizations found for this tenant\" message\n // is a better surface than an authz-denied page.\n const hasAnyCreatableOrg = workspaceCreateAllowedOrgIds.size > 0;\n const stillResolvingPermissions =\n isLoadingTenants || isLoadingOrganizations || isCheckingOrgPermissions;\n const orgDataLoadedSuccessfully =\n !tenantsError && !organizationsError && allOrganizations.length > 0;\n if (\n !stillResolvingPermissions &&\n !orgPermissionsHadError &&\n orgDataLoadedSuccessfully &&\n !hasAnyCreatableOrg\n ) {\n return (\n <div className=\"p-6\">\n <Link\n to={`${basePath}/list`}\n className=\"inline-flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors mb-6\"\n >\n <ArrowLeft size={20} />\n {tWithFallback(t, 'createWorkspace.backToWorkspaces', 'Back to Workspaces')}\n </Link>\n <AccessDenied\n variant=\"card\"\n title={tWithFallback(t, 'createWorkspace.permissionRequiredTitle', 'Permission Required')}\n description={tWithFallback(\n t,\n 'createWorkspace.permissionRequiredDescription',\n \"You don't have permission to create workspaces in any of your organizations.\"\n )}\n details={tWithFallback(\n t,\n 'createWorkspace.permissionRequiredDetails',\n 'Required: workspace:create'\n )}\n primaryAction={{\n label: tWithFallback(t, 'createWorkspace.goBack', 'Go Back'),\n onClick: () => navigate(`${basePath}/list`),\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Back link */}\n <Link\n to={`${basePath}/list`}\n className=\"inline-flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors\"\n >\n <ArrowLeft size={20} />\n {tWithFallback(t, 'createWorkspace.backToWorkspaces', 'Back to Workspaces')}\n </Link>\n\n {showUpgradeBanner && (\n <div className=\"max-w-2xl mx-auto w-full\">\n <UpgradeInlineBanner\n resource=\"workspaces\"\n current={workspacesUsage.current}\n limit={workspacesUsage.limit ?? undefined}\n severity={atWorkspaceCap ? 'critical' : undefined}\n message={\n atWorkspaceCap\n ? tWithFallback(\n t,\n 'createWorkspace.upgradeCapMessage',\n `You've used all workspaces in the ${planSnapshot?.planName ?? 'free'} plan.`\n )\n : tWithFallback(\n t,\n 'createWorkspace.upgradeApproachingMessage',\n 'Upgrade to create more workspaces.'\n )\n }\n />\n </div>\n )}\n\n {/* Form card */}\n <Card className=\"max-w-2xl mx-auto\">\n <CardHeader>\n <CardTitle>{tWithFallback(t, 'createWorkspace.title', 'Create New Workspace')}</CardTitle>\n <CardDescription>\n {tWithFallback(t, 'createWorkspace.subtitle', 'Set up a new workspace for your team')}\n </CardDescription>\n </CardHeader>\n <CardContent>\n {orgPermissionsHadError && (\n <div\n role=\"alert\"\n className=\"mb-4 flex items-start gap-2 rounded-md border border-warning/30 bg-warning/10 p-3 text-sm text-foreground\"\n >\n <AlertCircle size={16} className=\"mt-0.5 shrink-0 text-warning\" />\n <span>\n {tWithFallback(\n t,\n 'createWorkspace.permissionCheckUnavailable',\n \"We couldn't verify your workspace-create permissions just now, so the organization list isn't filtered. If you submit and don't have access, the request will be rejected and you can retry.\"\n )}\n </span>\n </div>\n )}\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-6\">\n <div className=\"space-y-2\">\n <label htmlFor=\"workspace-name\" className=\"text-sm font-medium text-foreground\">\n {tWithFallback(t, 'createWorkspace.nameLabel', 'Workspace Name *')}\n </label>\n <input\n id=\"workspace-name\"\n placeholder={tWithFallback(t, 'createWorkspace.namePlaceholder', 'My Workspace')}\n className=\"h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground\"\n {...form.register('name')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {tWithFallback(\n t,\n 'createWorkspace.nameHint',\n 'A descriptive name for your workspace'\n )}\n </p>\n {form.formState.errors.name?.message && (\n <p className=\"text-sm text-destructive\">{form.formState.errors.name.message}</p>\n )}\n </div>\n\n <div className=\"space-y-2\">\n <label htmlFor=\"workspace-slug\" className=\"text-sm font-medium text-foreground\">\n {tWithFallback(t, 'createWorkspace.slugLabel', 'Slug *')}\n </label>\n <input\n id=\"workspace-slug\"\n placeholder={tWithFallback(t, 'createWorkspace.slugPlaceholder', 'my-workspace')}\n className=\"h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground\"\n {...form.register('slug')}\n />\n <p className=\"text-xs text-muted-foreground\">\n {tWithFallback(\n t,\n 'createWorkspace.slugHint',\n 'URL-friendly identifier (auto-generated, but you can edit it)'\n )}\n </p>\n {form.formState.errors.slug?.message && (\n <p className=\"text-sm text-destructive\">{form.formState.errors.slug.message}</p>\n )}\n </div>\n\n {/* Tenant picker — shown only when the user has more than one\n tenant to choose between. With a single shared tenant there is\n nothing to choose, so it is hidden and auto-selected. The\n loading / error / empty branches still render (length !== 1). */}\n {tenants.length !== 1 ? (\n <div className=\"space-y-2\">\n <label htmlFor=\"workspace-tenant\" className=\"text-sm font-medium text-foreground\">\n {tWithFallback(t, 'createWorkspace.tenantLabel', 'Tenant *')}\n </label>\n {isLoadingTenants ? (\n <Skeleton className=\"h-10 w-full\" />\n ) : tenantsError ? (\n <div className=\"flex items-center gap-2 text-sm text-destructive\">\n <AlertCircle size={16} />\n {tWithFallback(\n t,\n 'createWorkspace.failedToLoadTenants',\n 'Failed to load tenants'\n )}\n </div>\n ) : tenants.length === 0 ? (\n <div className=\"text-sm text-muted-foreground\">\n {tWithFallback(\n t,\n 'createWorkspace.noTenantsAvailable',\n 'No tenants available. Contact your administrator.'\n )}\n </div>\n ) : (\n <select\n id=\"workspace-tenant\"\n className=\"h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground\"\n {...form.register('tenantId')}\n >\n <option value=\"\">\n {tWithFallback(\n t,\n 'createWorkspace.selectTenantPlaceholder',\n 'Select a tenant'\n )}\n </option>\n {tenants.map((tenant) => (\n <option key={tenant.id} value={tenant.id}>\n {tenant.name}\n </option>\n ))}\n </select>\n )}\n <p className=\"text-xs text-muted-foreground\">\n {tWithFallback(\n t,\n 'createWorkspace.tenantHint',\n 'Select which tenant this workspace belongs to'\n )}\n </p>\n {form.formState.errors.tenantId?.message && (\n <p className=\"text-sm text-destructive\">\n {form.formState.errors.tenantId.message}\n </p>\n )}\n </div>\n ) : null}\n\n <div className=\"space-y-2\">\n <label\n htmlFor=\"workspace-organization\"\n className=\"text-sm font-medium text-foreground\"\n >\n {tWithFallback(t, 'createWorkspace.organizationLabel', 'Organization *')}\n </label>\n {isLoadingOrganizations || isCheckingOrgPermissions ? (\n // While the permission check is in flight,\n // `workspaceCreateAllowedOrgIds` is empty so\n // `filteredOrganizations` is also empty. Render the same\n // skeleton we use for the org-load itself instead of the\n // \"no organizations found\" empty-state — otherwise a slow\n // rbac response briefly looks like an authorization\n // failure for users who DO have workspace:create.\n <Skeleton className=\"h-10 w-full\" />\n ) : organizationsError ? (\n <div className=\"flex items-center gap-2 text-sm text-destructive\">\n <AlertCircle size={16} />\n {tWithFallback(\n t,\n 'createWorkspace.failedToLoadOrganizations',\n 'Failed to load organizations'\n )}\n </div>\n ) : !selectedTenantId ? (\n <div className=\"text-sm text-muted-foreground\">\n {tWithFallback(\n t,\n 'createWorkspace.selectTenantFirst',\n 'Please select a tenant first'\n )}\n </div>\n ) : filteredOrganizations.length === 0 ? (\n <div className=\"text-sm text-muted-foreground\">\n {tWithFallback(\n t,\n 'createWorkspace.noOrganizationsFound',\n 'No organizations found for this tenant'\n )}\n </div>\n ) : (\n <select\n id=\"workspace-organization\"\n className=\"h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground\"\n disabled={!selectedTenantId}\n {...form.register('organizationId')}\n >\n <option value=\"\">\n {tWithFallback(\n t,\n 'createWorkspace.selectOrganizationPlaceholder',\n 'Select an organization'\n )}\n </option>\n {filteredOrganizations.map((org) => (\n <option key={org.id} value={org.id}>\n {org.name}\n </option>\n ))}\n </select>\n )}\n <p className=\"text-xs text-muted-foreground\">\n {tWithFallback(\n t,\n 'createWorkspace.organizationHint',\n 'Select which organization owns this workspace'\n )}\n </p>\n {form.formState.errors.organizationId?.message && (\n <p className=\"text-sm text-destructive\">\n {form.formState.errors.organizationId.message}\n </p>\n )}\n </div>\n\n <div className=\"flex justify-end gap-3 pt-4\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => navigate(`${basePath}/list`)}\n disabled={isPending}\n >\n {tWithFallback(t, 'createWorkspace.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"submit\"\n disabled={\n isPending ||\n isLoadingTenants ||\n isLoadingOrganizations ||\n isCheckingOrgPermissions ||\n atWorkspaceCap\n }\n title={\n atWorkspaceCap\n ? tWithFallback(\n t,\n 'createWorkspace.disabledAtCap',\n 'Workspace limit reached — upgrade your plan to create more.'\n )\n : undefined\n }\n >\n {isPending\n ? tWithFallback(t, 'createWorkspace.creating', 'Creating...')\n : tWithFallback(t, 'createWorkspace.submit', 'Create Workspace')}\n </Button>\n </div>\n </form>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CreateWorkspacePage.js","names":[],"sources":["../../src/pages/CreateWorkspacePage.tsx"],"sourcesContent":["import { useEffect, useMemo } from 'react';\nimport { ArrowLeft, AlertCircle } from 'lucide-react';\nimport { Link, useNavigate } from 'react-router-dom';\nimport {\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useForm, z } from '@burdenoff/fe-libs/form';\nimport { AccessDenied, UpgradeInlineBanner } from '@burdenoff/fe-libs/shared/components';\nimport { useUpgradePrompt } from '@burdenoff/fe-libs/shared/hooks';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useCreateWorkspace } from '../hooks/useWorkspaceMutations';\nimport { useMyTenants, useMyOrganizations } from '../hooks/useTenants';\nimport { useOrgsWithPermission } from '../hooks/useOrgsWithPermission';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { generateSlug } from '../utils/slug';\nimport { tWithFallback } from '../utils/i18n';\n\ntype CreateWorkspaceFormData = {\n name: string;\n slug: string;\n tenantId: string;\n organizationId: string;\n};\n\nexport function CreateWorkspacePage() {\n useWorkspacesPageView('workspaces.create');\n const { t } = useI18n();\n const {\n isFree,\n approachingLimits,\n plan: planSnapshot,\n } = useUpgradePrompt({ resource: 'workspaces' });\n const workspacesUsage = approachingLimits[0];\n const atWorkspaceCap =\n !!workspacesUsage &&\n typeof workspacesUsage.limit === 'number' &&\n workspacesUsage.current >= workspacesUsage.limit;\n const showUpgradeBanner = isFree && !!workspacesUsage;\n const createWorkspaceSchema = useMemo(\n () =>\n z.object({\n name: z\n .string()\n .min(\n 2,\n tWithFallback(\n t,\n 'createWorkspace.validation.nameMin',\n 'Name must be at least 2 characters'\n )\n )\n .max(100),\n slug: z\n .string()\n .min(\n 2,\n tWithFallback(\n t,\n 'createWorkspace.validation.slugMin',\n 'Slug must be at least 2 characters'\n )\n )\n .max(50)\n .regex(\n /^[a-z0-9-]+$/,\n tWithFallback(\n t,\n 'createWorkspace.validation.slugFormat',\n 'Slug can only contain lowercase letters, numbers, and hyphens'\n )\n ),\n tenantId: z\n .string()\n .min(\n 1,\n tWithFallback(t, 'createWorkspace.validation.tenantRequired', 'Please select a tenant')\n ),\n organizationId: z\n .string()\n .min(\n 1,\n tWithFallback(\n t,\n 'createWorkspace.validation.organizationRequired',\n 'Please select an organization'\n )\n ),\n }),\n [t]\n );\n\n const navigate = useNavigate();\n const { basePath = '/' } = useWorkspacesContext();\n const { mutate: createWorkspace, isPending } = useCreateWorkspace();\n const { data: tenantsData, isLoading: isLoadingTenants, error: tenantsError } = useMyTenants();\n const {\n data: organizationsData,\n isLoading: isLoadingOrganizations,\n error: organizationsError,\n } = useMyOrganizations();\n\n const tenants = useMemo(() => tenantsData?.items ?? [], [tenantsData]);\n const allOrganizations = useMemo(() => organizationsData?.items ?? [], [organizationsData]);\n\n const defaultTenantId = useMemo(() => {\n const sharedTenant = tenants.find(\n (tenant) => tenant.type === 'SHARED' || tenant.slug === 'burdenoff-shared'\n );\n return sharedTenant?.id ?? tenants[0]?.id ?? '';\n }, [tenants]);\n\n const form = useForm<CreateWorkspaceFormData>({\n schema: createWorkspaceSchema,\n defaultValues: {\n name: '',\n slug: '',\n tenantId: '',\n organizationId: '',\n },\n });\n\n const selectedTenantId = form.watch('tenantId');\n const workspaceName = form.watch('name');\n\n // Check workspace:create permission across ALL the user's orgs up front\n // (bulk-checked in a single rbac round-trip). This is the authoritative\n // gate — only orgs where the user can actually create a workspace make\n // it into the dropdown, and the page can decisively render the \"no\n // permissions anywhere\" state once the check resolves.\n const allOrgIds = useMemo(() => allOrganizations.map((o) => o.id), [allOrganizations]);\n const {\n allowedIds: workspaceCreateAllowedOrgIds,\n loading: isCheckingOrgPermissions,\n isError: orgPermissionsHadError,\n } = useOrgsWithPermission(allOrgIds, 'workspace', 'create');\n\n // Filter organizations by selected tenant AND permission. When the rbac\n // permission lookup errored we deliberately skip the permission filter\n // (see `orgPermissionsHadError` block below) — the gateway @rbac\n // directive on `createWorkspace` is the authoritative gate, so it's\n // safer to let the user pick from the full tenant list and hit a 403 on\n // submit than to lock them out of a flow they actually have access to.\n const tenantOrganizations = useMemo(() => {\n if (!selectedTenantId) return [];\n return allOrganizations.filter((org) => org.tenantId === selectedTenantId);\n }, [selectedTenantId, allOrganizations]);\n const filteredOrganizations = useMemo(() => {\n if (orgPermissionsHadError) return tenantOrganizations;\n return tenantOrganizations.filter((org) => workspaceCreateAllowedOrgIds.has(org.id));\n }, [tenantOrganizations, workspaceCreateAllowedOrgIds, orgPermissionsHadError]);\n\n // Auto-generate slug from name (clear when name is empty)\n useEffect(() => {\n form.setValue('slug', workspaceName ? generateSlug(workspaceName) : '');\n }, [workspaceName, form]);\n\n // Reset organizationId when tenant changes\n useEffect(() => {\n form.setValue('organizationId', '');\n }, [selectedTenantId, form]);\n\n useEffect(() => {\n if (!form.getValues('tenantId') && defaultTenantId) {\n form.setValue('tenantId', defaultTenantId, {\n shouldDirty: false,\n shouldTouch: false,\n });\n }\n }, [defaultTenantId, form]);\n\n useEffect(() => {\n const currentOrganizationId = form.getValues('organizationId');\n const hasCurrentOrganization = filteredOrganizations.some(\n (organization) => organization.id === currentOrganizationId\n );\n\n if (!hasCurrentOrganization && filteredOrganizations[0]?.id) {\n form.setValue('organizationId', filteredOrganizations[0].id, {\n shouldDirty: false,\n shouldTouch: false,\n });\n }\n }, [filteredOrganizations, form]);\n\n const onSubmit = (data: CreateWorkspaceFormData) => {\n createWorkspace(\n {\n name: data.name,\n slug: data.slug,\n tenantId: data.tenantId,\n organizationId: data.organizationId,\n },\n {\n onSuccess: (newWorkspace) => {\n // Navigate to the new workspace\n navigate(`${basePath}/${newWorkspace.id}`);\n },\n }\n );\n };\n\n // `workspace:create` is an org-scoped permission. Page-level `PermissionGate`\n // resolved at the outer shell scope and incorrectly denied users who *do*\n // have the permission inside at least one of their orgs. The authoritative\n // gate now lives on the org dropdown (only orgs where the user has\n // workspace:create are listed via useOrgsWithPermission) and on the gateway\n // @rbac directive enforcing the createWorkspace mutation. We still want a\n // clear \"you can't create anywhere\" empty state — once tenants and org\n // permission lookups have settled, if no org is eligible we render it.\n // The AccessDenied empty-state should ONLY render when we know with\n // certainty that the user has no creatable org. That requires:\n // 1. tenants + organizations queries both succeeded (otherwise an\n // empty `allOrganizations` is a data-fetch failure, not an authz\n // failure — masking it as \"permission denied\" hides the real\n // error and offers the user no path to retry),\n // 2. the rbac permission lookup also succeeded (errors there fall\n // through to the unfiltered form via the warning banner below),\n // 3. and even then, only when the user has at least one org but none\n // of them allow workspace:create.\n // If `allOrganizations` is empty because the user genuinely belongs to\n // no orgs, the inner \"no organizations found for this tenant\" message\n // is a better surface than an authz-denied page.\n const hasAnyCreatableOrg = workspaceCreateAllowedOrgIds.size > 0;\n const stillResolvingPermissions =\n isLoadingTenants || isLoadingOrganizations || isCheckingOrgPermissions;\n const orgDataLoadedSuccessfully =\n !tenantsError && !organizationsError && allOrganizations.length > 0;\n if (\n !stillResolvingPermissions &&\n !orgPermissionsHadError &&\n orgDataLoadedSuccessfully &&\n !hasAnyCreatableOrg\n ) {\n return (\n <div className=\"p-6\">\n <Link\n to={`${basePath}/list`}\n className=\"inline-flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors mb-6\"\n >\n <ArrowLeft size={20} />\n {tWithFallback(t, 'createWorkspace.backToWorkspaces', 'Back to Workspaces')}\n </Link>\n <AccessDenied\n variant=\"card\"\n title={tWithFallback(t, 'createWorkspace.permissionRequiredTitle', 'Permission Required')}\n description={tWithFallback(\n t,\n 'createWorkspace.permissionRequiredDescription',\n \"You don't have permission to create workspaces in any of your organizations.\"\n )}\n details={tWithFallback(\n t,\n 'createWorkspace.permissionRequiredDetails',\n 'Required: workspace:create'\n )}\n primaryAction={{\n label: tWithFallback(t, 'createWorkspace.goBack', 'Go Back'),\n onClick: () => navigate(`${basePath}/list`),\n }}\n />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Back link */}\n <Link\n to={`${basePath}/list`}\n className=\"inline-flex items-center gap-2 text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft size={20} />\n {tWithFallback(t, 'createWorkspace.backToWorkspaces', 'Back to Workspaces')}\n </Link>\n\n {showUpgradeBanner && (\n <div className=\"max-w-2xl mx-auto w-full\">\n <UpgradeInlineBanner\n resource=\"workspaces\"\n current={workspacesUsage.current}\n limit={workspacesUsage.limit ?? undefined}\n severity={atWorkspaceCap ? 'critical' : undefined}\n message={\n atWorkspaceCap\n ? tWithFallback(\n t,\n 'createWorkspace.upgradeCapMessage',\n `You've used all workspaces in the ${planSnapshot?.planName ?? 'free'} plan.`\n )\n : tWithFallback(\n t,\n 'createWorkspace.upgradeApproachingMessage',\n 'Upgrade to create more workspaces.'\n )\n }\n />\n </div>\n )}\n\n {/* Form card */}\n <Card className=\"max-w-2xl mx-auto\">\n <CardHeader>\n <CardTitle>{tWithFallback(t, 'createWorkspace.title', 'Create New Workspace')}</CardTitle>\n <CardDescription>\n {tWithFallback(t, 'createWorkspace.subtitle', 'Set up a new workspace for your team')}\n </CardDescription>\n </CardHeader>\n <CardContent>\n {orgPermissionsHadError && (\n <div\n role=\"alert\"\n className=\"mb-4 flex items-start gap-2 rounded-md border border-warning/30 bg-warning/10 p-3 text-sm text-text-primary\"\n >\n <AlertCircle size={16} className=\"mt-0.5 shrink-0 text-warning\" />\n <span>\n {tWithFallback(\n t,\n 'createWorkspace.permissionCheckUnavailable',\n \"We couldn't verify your workspace-create permissions just now, so the organization list isn't filtered. If you submit and don't have access, the request will be rejected and you can retry.\"\n )}\n </span>\n </div>\n )}\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-6\">\n <div className=\"space-y-2\">\n <label htmlFor=\"workspace-name\" className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, 'createWorkspace.nameLabel', 'Workspace Name *')}\n </label>\n <input\n id=\"workspace-name\"\n placeholder={tWithFallback(t, 'createWorkspace.namePlaceholder', 'My Workspace')}\n className=\"h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n {...form.register('name')}\n />\n <p className=\"text-xs text-text-secondary\">\n {tWithFallback(\n t,\n 'createWorkspace.nameHint',\n 'A descriptive name for your workspace'\n )}\n </p>\n {form.formState.errors.name?.message && (\n <p className=\"text-sm text-status-error-text\">{form.formState.errors.name.message}</p>\n )}\n </div>\n\n <div className=\"space-y-2\">\n <label htmlFor=\"workspace-slug\" className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, 'createWorkspace.slugLabel', 'Slug *')}\n </label>\n <input\n id=\"workspace-slug\"\n placeholder={tWithFallback(t, 'createWorkspace.slugPlaceholder', 'my-workspace')}\n className=\"h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n {...form.register('slug')}\n />\n <p className=\"text-xs text-text-secondary\">\n {tWithFallback(\n t,\n 'createWorkspace.slugHint',\n 'URL-friendly identifier (auto-generated, but you can edit it)'\n )}\n </p>\n {form.formState.errors.slug?.message && (\n <p className=\"text-sm text-status-error-text\">{form.formState.errors.slug.message}</p>\n )}\n </div>\n\n {/* Tenant picker — shown only when the user has more than one\n tenant to choose between. With a single shared tenant there is\n nothing to choose, so it is hidden and auto-selected. The\n loading / error / empty branches still render (length !== 1). */}\n {tenants.length !== 1 ? (\n <div className=\"space-y-2\">\n <label htmlFor=\"workspace-tenant\" className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, 'createWorkspace.tenantLabel', 'Tenant *')}\n </label>\n {isLoadingTenants ? (\n <Skeleton className=\"h-10 w-full\" />\n ) : tenantsError ? (\n <div className=\"flex items-center gap-2 text-sm text-status-error-text\">\n <AlertCircle size={16} />\n {tWithFallback(\n t,\n 'createWorkspace.failedToLoadTenants',\n 'Failed to load tenants'\n )}\n </div>\n ) : tenants.length === 0 ? (\n <div className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'createWorkspace.noTenantsAvailable',\n 'No tenants available. Contact your administrator.'\n )}\n </div>\n ) : (\n <select\n id=\"workspace-tenant\"\n className=\"h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n {...form.register('tenantId')}\n >\n <option value=\"\">\n {tWithFallback(\n t,\n 'createWorkspace.selectTenantPlaceholder',\n 'Select a tenant'\n )}\n </option>\n {tenants.map((tenant) => (\n <option key={tenant.id} value={tenant.id}>\n {tenant.name}\n </option>\n ))}\n </select>\n )}\n <p className=\"text-xs text-text-secondary\">\n {tWithFallback(\n t,\n 'createWorkspace.tenantHint',\n 'Select which tenant this workspace belongs to'\n )}\n </p>\n {form.formState.errors.tenantId?.message && (\n <p className=\"text-sm text-status-error-text\">\n {form.formState.errors.tenantId.message}\n </p>\n )}\n </div>\n ) : null}\n\n <div className=\"space-y-2\">\n <label\n htmlFor=\"workspace-organization\"\n className=\"text-sm font-medium text-text-primary\"\n >\n {tWithFallback(t, 'createWorkspace.organizationLabel', 'Organization *')}\n </label>\n {isLoadingOrganizations || isCheckingOrgPermissions ? (\n // While the permission check is in flight,\n // `workspaceCreateAllowedOrgIds` is empty so\n // `filteredOrganizations` is also empty. Render the same\n // skeleton we use for the org-load itself instead of the\n // \"no organizations found\" empty-state — otherwise a slow\n // rbac response briefly looks like an authorization\n // failure for users who DO have workspace:create.\n <Skeleton className=\"h-10 w-full\" />\n ) : organizationsError ? (\n <div className=\"flex items-center gap-2 text-sm text-status-error-text\">\n <AlertCircle size={16} />\n {tWithFallback(\n t,\n 'createWorkspace.failedToLoadOrganizations',\n 'Failed to load organizations'\n )}\n </div>\n ) : !selectedTenantId ? (\n <div className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'createWorkspace.selectTenantFirst',\n 'Please select a tenant first'\n )}\n </div>\n ) : filteredOrganizations.length === 0 ? (\n <div className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'createWorkspace.noOrganizationsFound',\n 'No organizations found for this tenant'\n )}\n </div>\n ) : (\n <select\n id=\"workspace-organization\"\n className=\"h-10 w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n disabled={!selectedTenantId}\n {...form.register('organizationId')}\n >\n <option value=\"\">\n {tWithFallback(\n t,\n 'createWorkspace.selectOrganizationPlaceholder',\n 'Select an organization'\n )}\n </option>\n {filteredOrganizations.map((org) => (\n <option key={org.id} value={org.id}>\n {org.name}\n </option>\n ))}\n </select>\n )}\n <p className=\"text-xs text-text-secondary\">\n {tWithFallback(\n t,\n 'createWorkspace.organizationHint',\n 'Select which organization owns this workspace'\n )}\n </p>\n {form.formState.errors.organizationId?.message && (\n <p className=\"text-sm text-status-error-text\">\n {form.formState.errors.organizationId.message}\n </p>\n )}\n </div>\n\n <div className=\"flex justify-end gap-3 pt-4\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => navigate(`${basePath}/list`)}\n disabled={isPending}\n >\n {tWithFallback(t, 'createWorkspace.cancel', 'Cancel')}\n </Button>\n <Button\n type=\"submit\"\n disabled={\n isPending ||\n isLoadingTenants ||\n isLoadingOrganizations ||\n isCheckingOrgPermissions ||\n atWorkspaceCap\n }\n title={\n atWorkspaceCap\n ? tWithFallback(\n t,\n 'createWorkspace.disabledAtCap',\n 'Workspace limit reached — upgrade your plan to create more.'\n )\n : undefined\n }\n >\n {isPending\n ? tWithFallback(t, 'createWorkspace.creating', 'Creating...')\n : tWithFallback(t, 'createWorkspace.submit', 'Create Workspace')}\n </Button>\n </div>\n </form>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -24,13 +24,13 @@ function M() {
24
24
  });
25
25
  };
26
26
  if (!H) return /* @__PURE__ */ v("div", {
27
- className: "min-h-screen flex items-center justify-center bg-muted/30 p-4",
27
+ className: "min-h-screen flex items-center justify-center bg-bg-sunken/30 p-4",
28
28
  children: /* @__PURE__ */ y(S, {
29
29
  className: "max-w-xl w-full",
30
30
  children: [/* @__PURE__ */ v(T, { children: /* @__PURE__ */ y("div", {
31
31
  className: "flex items-center gap-3",
32
32
  children: [/* @__PURE__ */ v("div", {
33
- className: "rounded-lg bg-primary/10 p-3",
33
+ className: "rounded-lg bg-action-primary-bg/10 p-3",
34
34
  children: /* @__PURE__ */ v(m, {
35
35
  className: "text-primary",
36
36
  size: 24
@@ -39,7 +39,7 @@ function M() {
39
39
  }) }), /* @__PURE__ */ y(C, {
40
40
  className: "space-y-4",
41
41
  children: [/* @__PURE__ */ v("p", {
42
- className: "text-sm text-muted-foreground",
42
+ className: "text-sm text-text-secondary",
43
43
  children: t(l, "inviteAcceptance.signInHint", "Use the same email address that received the invitation, then come back here to accept or reject it.")
44
44
  }), /* @__PURE__ */ y("div", {
45
45
  className: "flex flex-col gap-3 sm:flex-row",
@@ -80,13 +80,13 @@ function M() {
80
80
  case "EXPIRED": return /* @__PURE__ */ v(R, { invitation: W });
81
81
  case "CANCELLED": return /* @__PURE__ */ v(z, { invitation: W });
82
82
  case "PENDING": return W.expiresAt && new Date(W.expiresAt) < /* @__PURE__ */ new Date() ? /* @__PURE__ */ v(R, { invitation: W }) : /* @__PURE__ */ v("div", {
83
- className: "min-h-screen flex items-center justify-center p-4 bg-muted/30",
83
+ className: "min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30",
84
84
  children: /* @__PURE__ */ y(S, {
85
85
  className: "max-w-2xl w-full",
86
86
  children: [/* @__PURE__ */ v(T, { children: /* @__PURE__ */ y("div", {
87
87
  className: "flex items-center gap-3",
88
88
  children: [/* @__PURE__ */ v("div", {
89
- className: "p-3 rounded-lg bg-primary/10",
89
+ className: "p-3 rounded-lg bg-action-primary-bg/10",
90
90
  children: /* @__PURE__ */ v(m, {
91
91
  className: "text-primary",
92
92
  size: 24
@@ -98,10 +98,10 @@ function M() {
98
98
  }) }), /* @__PURE__ */ y(C, {
99
99
  className: "space-y-6",
100
100
  children: [/* @__PURE__ */ y("div", {
101
- className: "space-y-4 p-4 rounded-lg bg-muted/50",
101
+ className: "space-y-4 p-4 rounded-lg bg-bg-sunken/50",
102
102
  children: [
103
103
  /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
104
- className: "text-sm font-medium text-muted-foreground",
104
+ className: "text-sm font-medium text-text-secondary",
105
105
  children: t(l, "inviteAcceptance.emailAddressLabel", "Email Address")
106
106
  }), /* @__PURE__ */ v("p", {
107
107
  className: "text-lg font-medium mt-1",
@@ -109,7 +109,7 @@ function M() {
109
109
  })] }),
110
110
  /* @__PURE__ */ y("div", { children: [
111
111
  /* @__PURE__ */ v("p", {
112
- className: "text-sm font-medium text-muted-foreground",
112
+ className: "text-sm font-medium text-text-secondary",
113
113
  children: t(l, "inviteAcceptance.workspaceLabel", "Workspace")
114
114
  }),
115
115
  /* @__PURE__ */ v("p", {
@@ -117,18 +117,18 @@ function M() {
117
117
  children: ee
118
118
  }),
119
119
  W.workspaceId ? /* @__PURE__ */ v("p", {
120
- className: "mt-1 font-mono text-xs text-muted-foreground break-all",
120
+ className: "mt-1 font-mono text-xs text-text-secondary break-all",
121
121
  children: W.workspaceId
122
122
  }) : null
123
123
  ] }),
124
124
  W.expiresAt && /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
125
- className: "text-sm font-medium text-muted-foreground",
125
+ className: "text-sm font-medium text-text-secondary",
126
126
  children: t(l, "inviteAcceptance.expiresLabel", "Expires")
127
127
  }), /* @__PURE__ */ y("p", {
128
128
  className: "text-lg font-medium mt-1 flex items-center gap-2",
129
129
  children: [/* @__PURE__ */ v(f, {
130
130
  size: 16,
131
- className: "text-muted-foreground"
131
+ className: "text-text-secondary"
132
132
  }), new Date(W.expiresAt).toLocaleDateString("en-US", {
133
133
  year: "numeric",
134
134
  month: "long",
@@ -138,7 +138,7 @@ function M() {
138
138
  })]
139
139
  })] }),
140
140
  Q.length > 0 && /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
141
- className: "text-sm font-medium text-muted-foreground mb-2",
141
+ className: "text-sm font-medium text-text-secondary mb-2",
142
142
  children: t(l, "inviteAcceptance.rolesLabel", "Roles")
143
143
  }), /* @__PURE__ */ v("div", {
144
144
  className: "flex flex-wrap gap-2",
@@ -148,7 +148,7 @@ function M() {
148
148
  }, `${W.id}-${W.roleIds[t] ?? e}`))
149
149
  })] }),
150
150
  /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
151
- className: "text-sm font-medium text-muted-foreground mb-2",
151
+ className: "text-sm font-medium text-text-secondary mb-2",
152
152
  children: t(l, "inviteAcceptance.projectsLabel", "Projects")
153
153
  }), /* @__PURE__ */ v("div", {
154
154
  className: "flex flex-wrap gap-2",
@@ -156,15 +156,15 @@ function M() {
156
156
  variant: "outline",
157
157
  children: e
158
158
  }, `${W.id}-project-${e}-${t}`)) : /* @__PURE__ */ v("p", {
159
- className: "text-sm text-muted-foreground",
159
+ className: "text-sm text-text-secondary",
160
160
  children: t(l, "inviteAcceptance.noProjects", "No project memberships will be granted automatically with this invitation.")
161
161
  })
162
162
  })] }),
163
163
  /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
164
- className: "text-sm font-medium text-muted-foreground",
164
+ className: "text-sm font-medium text-text-secondary",
165
165
  children: t(l, "inviteAcceptance.invitationIdLabel", "Invitation ID")
166
166
  }), /* @__PURE__ */ v("p", {
167
- className: "text-sm font-mono mt-1 text-muted-foreground",
167
+ className: "text-sm font-mono mt-1 text-text-secondary",
168
168
  children: W.id
169
169
  })] })
170
170
  ]
@@ -193,7 +193,7 @@ function M() {
193
193
  }
194
194
  function N() {
195
195
  return /* @__PURE__ */ v("div", {
196
- className: "min-h-screen flex items-center justify-center p-4 bg-muted/30",
196
+ className: "min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30",
197
197
  children: /* @__PURE__ */ y(S, {
198
198
  className: "max-w-2xl w-full",
199
199
  children: [/* @__PURE__ */ v(T, { children: /* @__PURE__ */ y("div", {
@@ -205,7 +205,7 @@ function N() {
205
205
  }) }), /* @__PURE__ */ y(C, {
206
206
  className: "space-y-6",
207
207
  children: [/* @__PURE__ */ y("div", {
208
- className: "space-y-4 p-4 rounded-lg bg-muted/50",
208
+ className: "space-y-4 p-4 rounded-lg bg-bg-sunken/50",
209
209
  children: [/* @__PURE__ */ y("div", {
210
210
  className: "space-y-2",
211
211
  children: [/* @__PURE__ */ v(D, { className: "h-4 w-24" }), /* @__PURE__ */ v(D, { className: "h-6 w-48" })]
@@ -224,15 +224,15 @@ function N() {
224
224
  function P({ error: e, onRetry: n }) {
225
225
  let { t: r } = j(), i = k();
226
226
  return /* @__PURE__ */ v("div", {
227
- className: "min-h-screen flex items-center justify-center p-4 bg-muted/30",
227
+ className: "min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30",
228
228
  children: /* @__PURE__ */ y(S, {
229
229
  className: "max-w-2xl w-full",
230
230
  children: [/* @__PURE__ */ v(T, { children: /* @__PURE__ */ y("div", {
231
231
  className: "flex items-center gap-3",
232
232
  children: [/* @__PURE__ */ v("div", {
233
- className: "p-3 rounded-lg bg-destructive/10",
233
+ className: "p-3 rounded-lg bg-status-error-bg/10",
234
234
  children: /* @__PURE__ */ v(l, {
235
- className: "text-destructive",
235
+ className: "text-status-error-text",
236
236
  size: 24
237
237
  })
238
238
  }), /* @__PURE__ */ y("div", {
@@ -242,12 +242,12 @@ function P({ error: e, onRetry: n }) {
242
242
  }) }), /* @__PURE__ */ y(C, {
243
243
  className: "space-y-4",
244
244
  children: [/* @__PURE__ */ y("div", {
245
- className: "flex gap-3 p-4 rounded-lg border border-destructive/50 bg-destructive/10",
246
- children: [/* @__PURE__ */ v(l, { className: "size-5 text-destructive flex-shrink-0 mt-0.5" }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
247
- className: "font-medium text-destructive",
245
+ className: "flex gap-3 p-4 rounded-lg border border-status-error-border/50 bg-status-error-bg/10",
246
+ children: [/* @__PURE__ */ v(l, { className: "size-5 text-status-error-text flex-shrink-0 mt-0.5" }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
247
+ className: "font-medium text-status-error-text",
248
248
  children: t(r, "inviteAcceptance.errorDetailsLabel", "Error Details")
249
249
  }), /* @__PURE__ */ v("p", {
250
- className: "text-sm mt-1 text-destructive/90",
250
+ className: "text-sm mt-1 text-status-error-text/90",
251
251
  children: e.message
252
252
  })] })]
253
253
  }), /* @__PURE__ */ y("div", {
@@ -270,15 +270,15 @@ function P({ error: e, onRetry: n }) {
270
270
  function F() {
271
271
  let { t: e } = j(), n = k();
272
272
  return /* @__PURE__ */ v("div", {
273
- className: "min-h-screen flex items-center justify-center p-4 bg-muted/30",
273
+ className: "min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30",
274
274
  children: /* @__PURE__ */ y(S, {
275
275
  className: "max-w-2xl w-full",
276
276
  children: [/* @__PURE__ */ v(T, { children: /* @__PURE__ */ y("div", {
277
277
  className: "flex items-center gap-3",
278
278
  children: [/* @__PURE__ */ v("div", {
279
- className: "p-3 rounded-lg bg-muted",
279
+ className: "p-3 rounded-lg bg-bg-sunken",
280
280
  children: /* @__PURE__ */ v(l, {
281
- className: "text-muted-foreground",
281
+ className: "text-text-secondary",
282
282
  size: 24
283
283
  })
284
284
  }), /* @__PURE__ */ y("div", {
@@ -288,12 +288,12 @@ function F() {
288
288
  }) }), /* @__PURE__ */ y(C, {
289
289
  className: "space-y-4",
290
290
  children: [/* @__PURE__ */ y("div", {
291
- className: "flex gap-3 p-4 rounded-lg border border-border bg-muted/50",
292
- children: [/* @__PURE__ */ v(l, { className: "size-5 text-muted-foreground flex-shrink-0 mt-0.5" }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
291
+ className: "flex gap-3 p-4 rounded-lg border border-border-subtle bg-bg-sunken/50",
292
+ children: [/* @__PURE__ */ v(l, { className: "size-5 text-text-secondary flex-shrink-0 mt-0.5" }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
293
293
  className: "font-medium",
294
294
  children: t(e, "inviteAcceptance.invalidLinkHeading", "Invalid Invitation Link")
295
295
  }), /* @__PURE__ */ v("p", {
296
- className: "text-sm mt-1 text-muted-foreground",
296
+ className: "text-sm mt-1 text-text-secondary",
297
297
  children: t(e, "inviteAcceptance.invalidLinkBody", "The invitation you're trying to access doesn't exist. It may have been deleted or the link is incorrect.")
298
298
  })] })]
299
299
  }), /* @__PURE__ */ y(x, {
@@ -308,7 +308,7 @@ function F() {
308
308
  function I({ invitation: e }) {
309
309
  let { t: n } = j(), r = k();
310
310
  return /* @__PURE__ */ v("div", {
311
- className: "min-h-screen flex items-center justify-center p-4 bg-muted/30",
311
+ className: "min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30",
312
312
  children: /* @__PURE__ */ y(S, {
313
313
  className: "max-w-2xl w-full",
314
314
  children: [/* @__PURE__ */ v(T, { children: /* @__PURE__ */ y("div", {
@@ -353,15 +353,15 @@ function I({ invitation: e }) {
353
353
  function L({ invitation: e }) {
354
354
  let { t: n } = j(), r = k();
355
355
  return /* @__PURE__ */ v("div", {
356
- className: "min-h-screen flex items-center justify-center p-4 bg-muted/30",
356
+ className: "min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30",
357
357
  children: /* @__PURE__ */ y(S, {
358
358
  className: "max-w-2xl w-full",
359
359
  children: [/* @__PURE__ */ v(T, { children: /* @__PURE__ */ y("div", {
360
360
  className: "flex items-center gap-3",
361
361
  children: [/* @__PURE__ */ v("div", {
362
- className: "p-3 rounded-lg bg-muted",
362
+ className: "p-3 rounded-lg bg-bg-sunken",
363
363
  children: /* @__PURE__ */ v(g, {
364
- className: "text-muted-foreground",
364
+ className: "text-text-secondary",
365
365
  size: 24
366
366
  })
367
367
  }), /* @__PURE__ */ y("div", {
@@ -371,12 +371,12 @@ function L({ invitation: e }) {
371
371
  }) }), /* @__PURE__ */ y(C, {
372
372
  className: "space-y-4",
373
373
  children: [/* @__PURE__ */ y("div", {
374
- className: "flex gap-3 p-4 rounded-lg border border-border bg-muted/50",
375
- children: [/* @__PURE__ */ v(g, { className: "size-5 text-muted-foreground flex-shrink-0 mt-0.5" }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
374
+ className: "flex gap-3 p-4 rounded-lg border border-border-subtle bg-bg-sunken/50",
375
+ children: [/* @__PURE__ */ v(g, { className: "size-5 text-text-secondary flex-shrink-0 mt-0.5" }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
376
376
  className: "font-medium",
377
377
  children: t(n, "inviteAcceptance.invitationDeclined", "Invitation Declined")
378
378
  }), /* @__PURE__ */ y("p", {
379
- className: "text-sm mt-1 text-muted-foreground",
379
+ className: "text-sm mt-1 text-text-secondary",
380
380
  children: [
381
381
  t(n, "inviteAcceptance.rejectedPrefix", "This invitation to "),
382
382
  /* @__PURE__ */ v("span", {
@@ -398,7 +398,7 @@ function L({ invitation: e }) {
398
398
  function R({ invitation: e }) {
399
399
  let { t: n } = j(), r = k();
400
400
  return /* @__PURE__ */ v("div", {
401
- className: "min-h-screen flex items-center justify-center p-4 bg-muted/30",
401
+ className: "min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30",
402
402
  children: /* @__PURE__ */ y(S, {
403
403
  className: "max-w-2xl w-full",
404
404
  children: [/* @__PURE__ */ v(T, { children: /* @__PURE__ */ y("div", {
@@ -456,15 +456,15 @@ function R({ invitation: e }) {
456
456
  function z({ invitation: e }) {
457
457
  let { t: n } = j(), r = k();
458
458
  return /* @__PURE__ */ v("div", {
459
- className: "min-h-screen flex items-center justify-center p-4 bg-muted/30",
459
+ className: "min-h-screen flex items-center justify-center p-4 bg-bg-sunken/30",
460
460
  children: /* @__PURE__ */ y(S, {
461
461
  className: "max-w-2xl w-full",
462
462
  children: [/* @__PURE__ */ v(T, { children: /* @__PURE__ */ y("div", {
463
463
  className: "flex items-center gap-3",
464
464
  children: [/* @__PURE__ */ v("div", {
465
- className: "p-3 rounded-lg bg-destructive/10",
465
+ className: "p-3 rounded-lg bg-status-error-bg/10",
466
466
  children: /* @__PURE__ */ v(u, {
467
- className: "text-destructive",
467
+ className: "text-status-error-text",
468
468
  size: 24
469
469
  })
470
470
  }), /* @__PURE__ */ y("div", {
@@ -474,12 +474,12 @@ function z({ invitation: e }) {
474
474
  }) }), /* @__PURE__ */ y(C, {
475
475
  className: "space-y-4",
476
476
  children: [/* @__PURE__ */ y("div", {
477
- className: "flex gap-3 p-4 rounded-lg border border-destructive/50 bg-destructive/10",
478
- children: [/* @__PURE__ */ v(u, { className: "size-5 text-destructive flex-shrink-0 mt-0.5" }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
479
- className: "font-medium text-destructive",
477
+ className: "flex gap-3 p-4 rounded-lg border border-status-error-border/50 bg-status-error-bg/10",
478
+ children: [/* @__PURE__ */ v(u, { className: "size-5 text-status-error-text flex-shrink-0 mt-0.5" }), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
479
+ className: "font-medium text-status-error-text",
480
480
  children: t(n, "inviteAcceptance.cancelledHeading", "Invitation No Longer Available")
481
481
  }), /* @__PURE__ */ y("p", {
482
- className: "text-sm mt-1 text-destructive/90",
482
+ className: "text-sm mt-1 text-status-error-text/90",
483
483
  children: [
484
484
  t(n, "inviteAcceptance.cancelledPrefix", "This invitation to "),
485
485
  /* @__PURE__ */ v("span", {