@burdenoff/microfe-workspaces 2026.627.1 → 2026.711.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/components/ProjectCard.js +174 -155
  2. package/dist/components/ProjectCard.js.map +1 -1
  3. package/dist/components/RenameProjectDialog.js +82 -0
  4. package/dist/components/RenameProjectDialog.js.map +1 -0
  5. package/dist/generated/wspace-operations.js +11 -0
  6. package/dist/generated/wspace-operations.js.map +1 -1
  7. package/dist/pages/ProjectDetailPage.js +244 -226
  8. package/dist/pages/ProjectDetailPage.js.map +1 -1
  9. package/dist/pages/ProjectsListPage.js +16 -15
  10. package/dist/pages/ProjectsListPage.js.map +1 -1
  11. package/dist/pages/dashboard/DashboardGrid.js +60 -48
  12. package/dist/pages/dashboard/DashboardGrid.js.map +1 -1
  13. package/dist/pages/dashboard/DashboardPage.js +107 -82
  14. package/dist/pages/dashboard/DashboardPage.js.map +1 -1
  15. package/dist/pages/dashboard/WidgetWrapper.js +2 -2
  16. package/dist/pages/dashboard/WidgetWrapper.js.map +1 -1
  17. package/dist/pages/dashboard/config.js +82 -0
  18. package/dist/pages/dashboard/config.js.map +1 -1
  19. package/dist/pages/dashboard/widgets/QuickActionsWidget.js +66 -23
  20. package/dist/pages/dashboard/widgets/QuickActionsWidget.js.map +1 -1
  21. package/dist/pages/dashboard/widgets/TipsWidget.js +68 -30
  22. package/dist/pages/dashboard/widgets/TipsWidget.js.map +1 -1
  23. package/dist/pages/dashboard/widgets/WorkflowRunsWidget.js +139 -0
  24. package/dist/pages/dashboard/widgets/WorkflowRunsWidget.js.map +1 -0
  25. package/dist/pages/dashboard/widgets/WorkflowsOverviewWidget.js +91 -0
  26. package/dist/pages/dashboard/widgets/WorkflowsOverviewWidget.js.map +1 -0
  27. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectDetailPage.js","names":[],"sources":["../../src/pages/ProjectDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams, useNavigate } from 'react-router-dom';\nimport {\n ArrowLeft,\n FolderKanban,\n Users,\n UserPlus,\n Activity,\n Archive,\n MoreVertical,\n Clock,\n} from 'lucide-react';\nimport {\n Button,\n ShowMore,\n GlassCard,\n CardHeader,\n CardTitle,\n CardContent,\n Badge,\n Skeleton,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n DropdownMenuSeparator,\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n EntityIdModal,\n useEntityIdModal,\n IllustratedEmptyState,\n PagePurpose,\n NextSteps,\n} from '@burdenoff/fe-libs/ui';\nimport { type TagItem } from '@burdenoff/fe-libs/tag';\nimport { ProjectTagsWidget } from '../components/tags/ProjectTagsWidget';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { useProject, useProjectMembersCount } from '../hooks/useProjects';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport {\n useArchiveProject,\n useUnarchiveProject,\n useDeleteProject,\n useTagProject,\n useUntagProject,\n useUpdateProject,\n} from '../hooks/useProjectMutations';\nimport { formatDate } from '../utils/formatters';\nimport { nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport { getProjectDisplayTags, toProjectTag } from '../utils/projectTags';\nimport { getProjectStatusClasses } from '../utils/tokenHelpers';\nimport { useWorkspaceActivity } from '../hooks/useWorkspaceActivity';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { ProjectMemberPickerDialog } from '../components/ProjectMemberPickerDialog';\n\nexport function ProjectDetailPage() {\n const { t } = useI18n();\n const idModal = useEntityIdModal();\n\n const { workspaceId, projectId } = useParams<{ workspaceId: string; projectId: string }>();\n useWorkspacesPageView('workspaces.project-detail', {\n workspaceId,\n projectId,\n requireParams: true,\n });\n const navigate = useNavigate();\n const { basePath = '/workspaces' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { data: project, isLoading, error } = useProject(projectId || '', workspaceId);\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const [showAddMemberDialog, setShowAddMemberDialog] = useState(false);\n\n const { mutate: archiveProject, isPending: isArchiving } = useArchiveProject(workspaceId);\n const { mutate: unarchiveProject, isPending: isUnarchiving } = useUnarchiveProject(workspaceId);\n const { mutate: deleteProject, isPending: isDeleting } = useDeleteProject(workspaceId);\n const { mutateAsync: tagProject, isPending: isTaggingProject } = useTagProject(workspaceId);\n const { mutateAsync: untagProject, isPending: isUntaggingProject } = useUntagProject(workspaceId);\n const { mutateAsync: updateProject, isPending: isUpdatingProject } =\n useUpdateProject(workspaceId);\n const {\n canViewProject,\n canDeleteProject,\n canArchiveProject,\n canListProjectMembers,\n canAddProjectMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n const { data: projectMemberCount } = useProjectMembersCount(projectId || '', workspaceId);\n const displayTags = useMemo(\n () =>\n getProjectDisplayTags({\n tags: project?.tags ?? [],\n tagAssignments: project?.tagAssignments,\n }),\n [project?.tags, project?.tagAssignments]\n );\n const { data: workspaceActivity } = useWorkspaceActivity(workspaceId);\n const relatedActivityCount = (workspaceActivity ?? []).filter(\n (item) => item.resourceId === projectId || item.resourceType?.toLowerCase() === 'project'\n ).length;\n const widgetTags: TagItem[] = displayTags;\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <Skeleton className=\"h-64\" />\n </div>\n );\n }\n\n if (!canViewProject) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'projectDetail.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'projectDetail.noPermissionToViewProject',\n \"You don't have permission to view this project.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const handleBack = () => {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects`));\n };\n\n const handleArchive = () => {\n if (!project) return;\n if (project.archived) {\n unarchiveProject(project.id, {\n onError: (err) => {\n console.error('Failed to unarchive project:', err);\n },\n });\n } else {\n archiveProject(project.id, {\n onError: (err) => {\n console.error('Failed to archive project:', err);\n },\n });\n }\n };\n\n const handleDelete = () => {\n if (!project) return;\n void nativeImpact('medium');\n deleteProject(project.id, {\n onSuccess: () => {\n void nativeNotify('success');\n setShowDeleteDialog(false);\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects`));\n },\n onError: (err) => {\n void nativeNotify('error');\n console.error('Failed to delete project:', err);\n },\n });\n };\n\n const handleViewProjectMembers = () => {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects/${projectId}/members`));\n };\n\n const syncLegacyProjectTags = async (nextTags: TagItem[]) => {\n if (!project) {\n return;\n }\n\n const normalizedNextTags = nextTags.map((tag) => toProjectTag(tag));\n\n try {\n await updateProject({\n id: project.id,\n input: {\n tags: normalizedNextTags,\n },\n });\n } catch (err) {\n void nativeNotify('warning');\n console.error(\n 'Failed to sync legacy project tag mirror after updating structured project tags:',\n err\n );\n }\n };\n\n const handleAddTag = async (tag: TagItem) => {\n if (!project) {\n return;\n }\n\n try {\n await tagProject({\n entityId: project.id,\n tagId: tag.id,\n });\n\n await syncLegacyProjectTags([...displayTags, toProjectTag(tag)]);\n } catch (err) {\n void nativeNotify('error');\n throw err;\n }\n };\n\n const handleRemoveTag = async (tagId: string) => {\n if (!project) {\n return;\n }\n\n const assignmentId = project.tagAssignments?.find(\n (assignment) => assignment.tagId === tagId\n )?.id;\n try {\n await untagProject({\n entityId: project.id,\n tagId,\n assignmentId,\n });\n\n await syncLegacyProjectTags(\n displayTags.filter((tag) => tag.tagId !== tagId && tag.id !== tagId)\n );\n } catch (err) {\n void nativeNotify('error');\n throw err;\n }\n };\n\n if (isLoading) {\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <Skeleton className=\"h-10 w-32\" />\n <Skeleton className=\"h-10 w-1/3\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n </div>\n <Skeleton className=\"h-48\" />\n </div>\n );\n }\n\n if (error || !project) {\n return (\n <div className=\"space-y-4 p-6\">\n <Button variant=\"ghost\" onClick={handleBack} className=\"gap-2\">\n <ArrowLeft size={20} />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tWithFallback(t, 'projectDetail.projectNotFound', 'Project not found')}\n description={tWithFallback(\n t,\n 'projectDetail.projectNotFoundDescription',\n \"The project you're looking for doesn't exist or you don't have permission to view it.\"\n )}\n action={\n <Button onClick={handleBack}>\n <ArrowLeft size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\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 {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3\">\n <div>\n <Button variant=\"ghost\" onClick={handleBack} className=\"gap-2 mb-2 -ml-2\">\n <ArrowLeft size={20} />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-action-primary-bg/10\">\n <FolderKanban className=\"text-primary\" size={28} />\n </div>\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">{project.name}</h1>\n <p className=\"text-sm text-text-secondary font-mono\">{project.key}</p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 mt-3\">\n <Badge className={`border ${getProjectStatusClasses(project.status)}`}>\n {project.status}\n </Badge>\n {project.archived && (\n <Badge\n variant=\"outline\"\n className=\"border-workspace-archived-border text-workspace-archived-text\"\n >\n <Archive size={12} className=\"mr-1\" />\n {tWithFallback(t, 'projectDetail.archived', 'Archived')}\n </Badge>\n )}\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"icon\"\n aria-label={tWithFallback(\n t,\n 'projectDetail.openProjectActionsAriaLabel',\n 'Open project actions'\n )}\n >\n <MoreVertical size={20} />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {canArchiveProject && (\n <DropdownMenuItem onClick={handleArchive} disabled={isArchiving || isUnarchiving}>\n <Archive size={16} className=\"mr-2\" />\n {isArchiving || isUnarchiving\n ? tWithFallback(t, 'projectDetail.processing', 'Processing...')\n : project.archived\n ? tWithFallback(t, 'projectDetail.unarchive', 'Unarchive')\n : tWithFallback(t, 'projectDetail.archive', 'Archive')}\n </DropdownMenuItem>\n )}\n {canArchiveProject && canDeleteProject && <DropdownMenuSeparator />}\n {canDeleteProject && (\n <DropdownMenuItem\n onClick={() => setShowDeleteDialog(true)}\n className=\"text-status-error-text\"\n >\n {tWithFallback(t, 'projectDetail.deleteProject', 'Delete Project')}\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n\n <PagePurpose>\n {tWithFallback(\n t,\n 'projectDetail.purpose',\n 'This is a single project — a focused stream of work within its workspace. From here you can manage the people working on it, organise it with tags so it surfaces in filters and reports, and keep track of related activity. Use the menu in the top-right to archive or delete it when it wraps up.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"workspaces-project-detail\"\n steps={[\n ...(canAddProjectMember\n ? [\n {\n id: 'add-member',\n label: tWithFallback(\n t,\n 'projectDetail.stepAddMember',\n 'Add people to the project'\n ),\n description: tWithFallback(\n t,\n 'projectDetail.stepAddMemberDesc',\n 'Pull in the teammates who will actually work on this project.'\n ),\n icon: <UserPlus size={16} />,\n onClick: () => setShowAddMemberDialog(true),\n },\n ]\n : []),\n {\n id: 'tag-project',\n label: tWithFallback(t, 'projectDetail.stepTag', 'Tag the project'),\n description: tWithFallback(\n t,\n 'projectDetail.stepTagDesc',\n 'Add tags so this project shows up in the right filters and views.'\n ),\n icon: <FolderKanban size={16} />,\n onClick: () => {\n document\n .getElementById('project-tags-section')\n ?.scrollIntoView({ behavior: 'smooth', block: 'center' });\n },\n },\n ...(canListProjectMembers\n ? [\n {\n id: 'manage-members',\n label: tWithFallback(\n t,\n 'projectDetail.stepManageMembers',\n 'Review project members'\n ),\n description: tWithFallback(\n t,\n 'projectDetail.stepManageMembersDesc',\n 'See who has access and adjust their project roles.'\n ),\n icon: <Users size={16} />,\n onClick: handleViewProjectMembers,\n },\n ]\n : []),\n {\n id: 'review-activity',\n label: tWithFallback(t, 'projectDetail.stepActivity', 'Check recent activity'),\n description: tWithFallback(\n t,\n 'projectDetail.stepActivityDesc',\n 'Catch up on what has changed across the workspace.'\n ),\n icon: <Activity size={16} />,\n onClick: () => navigate(withCurrentSearch(`${basePath}/${workspaceId}/activity`)),\n },\n ]}\n />\n\n {/* Main + detail rail */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6\">\n {/* Main column */}\n <div className=\"lg:col-span-2 space-y-4 sm:space-y-6\">\n {/* Stat metrics */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <GlassCard treatment=\"metric\" glow>\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-sm font-medium text-text-secondary\">\n <Users size={18} className=\"text-text-muted\" />\n {tWithFallback(t, 'projectDetail.membersCardTitle', 'Members')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-3xl font-bold tabular-nums\">{projectMemberCount ?? 0}</p>\n <p className=\"text-sm text-text-muted\">\n {tWithFallback(t, 'projectDetail.projectMembers', 'Project members')}\n </p>\n\n {canAddProjectMember || canListProjectMembers ? (\n <div className=\"flex flex-wrap gap-2\">\n {canAddProjectMember ? (\n <Button size=\"sm\" onClick={() => setShowAddMemberDialog(true)}>\n <UserPlus size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.addProjectMember', 'Add Member')}\n </Button>\n ) : null}\n\n {canListProjectMembers ? (\n <Button variant=\"outline\" size=\"sm\" onClick={handleViewProjectMembers}>\n <Users size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.manageProjectMembers', 'Manage Members')}\n </Button>\n ) : null}\n </div>\n ) : null}\n </CardContent>\n </GlassCard>\n\n <GlassCard treatment=\"metric\">\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-sm font-medium text-text-secondary\">\n <Activity size={18} className=\"text-text-muted\" />\n {tWithFallback(t, 'projectDetail.activityCardTitle', 'Activity')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold tabular-nums\">{relatedActivityCount}</p>\n <p className=\"text-sm text-text-muted\">\n {tWithFallback(t, 'projectDetail.recentActivityItems', 'Recent activity items')}\n </p>\n </CardContent>\n </GlassCard>\n </div>\n\n <GlassCard treatment=\"plain\" id=\"project-tags-section\" className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>{tWithFallback(t, 'projectDetail.tagsTitle', 'Tags')}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-text-muted\">\n {tWithFallback(\n t,\n 'projectDetail.tagsDescription',\n 'Use universal tags to classify and filter this project across the workspace.'\n )}\n </p>\n <ProjectTagsWidget\n entityType=\"Project\"\n entityId={project.id}\n workspaceId={workspaceId}\n tags={widgetTags}\n onAdd={handleAddTag}\n onRemove={handleRemoveTag}\n className={\n isTaggingProject || isUntaggingProject || isUpdatingProject\n ? 'pointer-events-none opacity-75'\n : undefined\n }\n />\n </CardContent>\n </GlassCard>\n </div>\n\n {/* Detail rail */}\n <aside className=\"lg:sticky lg:top-6 lg:self-start\">\n {/* Project Information */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>\n {tWithFallback(t, 'projectDetail.projectInformation', 'Project Information')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 text-sm\">\n <div className=\"space-y-1\">\n <span className=\"text-text-muted flex items-center gap-2\">\n <FolderKanban size={14} />\n {tWithFallback(t, 'projectDetail.projectId', 'Project ID')}\n </span>\n <button\n type=\"button\"\n onClick={() =>\n idModal.show({\n id: project.id,\n name: project.name,\n type: 'Project',\n details: project.key ? [{ label: 'Key', value: project.key }] : undefined,\n })\n }\n className=\"inline-flex items-center gap-1.5 text-xs font-medium text-text-link hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] rounded\"\n >\n {tWithFallback(t, 'projectDetail.getId', 'Get ID')}\n </button>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <FolderKanban size={14} />\n {tWithFallback(t, 'projectDetail.projectKey', 'Project Key')}\n </span>\n <span className=\"font-medium\">{project.key}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Clock size={14} />\n {tWithFallback(t, 'projectDetail.created', 'Created')}\n </span>\n <span>{formatDate(project.createdAt)}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Clock size={14} />\n {tWithFallback(t, 'projectDetail.lastUpdated', 'Last Updated')}\n </span>\n <span>{formatDate(project.updatedAt)}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Users size={14} />\n {tWithFallback(t, 'projectDetail.workspaceId', 'Workspace ID')}\n </span>\n <span className=\"font-mono text-xs block\">{project.workspaceId}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Users size={14} />\n {tWithFallback(t, 'projectDetail.creatorId', 'Creator ID')}\n </span>\n <span className=\"font-mono text-xs block\">{project.creatorId}</span>\n </div>\n </div>\n\n {project.config && Object.keys(project.config).length > 0 && (\n <div className=\"pt-4 border-t border-border-seam\">\n <h4 className=\"text-sm font-medium mb-2\">\n {tWithFallback(t, 'projectDetail.configuration', 'Configuration')}\n </h4>\n <pre className=\"text-xs bg-bg-sunken p-3 rounded-lg overflow-auto\">\n {JSON.stringify(project.config, null, 2)}\n </pre>\n </div>\n )}\n\n {/* Quick Actions — progressive disclosure (secondary navigation) */}\n <ShowMore\n collapsedHeight={0}\n label={tWithFallback(t, 'projectDetail.quickActions', 'Quick Actions')}\n >\n <div className=\"flex flex-col gap-2 pt-2\">\n {canListProjectMembers ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"justify-start\"\n onClick={handleViewProjectMembers}\n >\n <Users size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.projectMembersPage', 'Project Members')}\n </Button>\n ) : null}\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"justify-start\"\n onClick={() =>\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/members`))\n }\n >\n <Users size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.workspaceMembers', 'Workspace Members')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"justify-start\"\n onClick={() =>\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/activity`))\n }\n >\n <Activity size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.workspaceActivity', 'Workspace Activity')}\n </Button>\n </div>\n </ShowMore>\n </CardContent>\n </GlassCard>\n </aside>\n </div>\n\n {/* Delete Confirmation Dialog */}\n <AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n {tWithFallback(t, 'projectDetail.deleteDialogTitle', 'Delete Project')}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {tWithFallback(\n t,\n 'projectDetail.deleteConfirmPrefix',\n 'Are you sure you want to delete \"'\n ) +\n project.name +\n tWithFallback(\n t,\n 'projectDetail.deleteConfirmSuffix',\n '\"? This action cannot be undone and all project data will be permanently removed.'\n )}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={isDeleting}>\n {tWithFallback(t, 'projectDetail.cancel', 'Cancel')}\n </AlertDialogCancel>\n <AlertDialogAction\n onClick={handleDelete}\n disabled={isDeleting}\n className=\"bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90\"\n >\n {isDeleting\n ? tWithFallback(t, 'projectDetail.deleting', 'Deleting...')\n : tWithFallback(t, 'projectDetail.deleteProject', 'Delete Project')}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n {workspaceId && projectId && showAddMemberDialog ? (\n <ProjectMemberPickerDialog\n open={showAddMemberDialog}\n onOpenChange={setShowAddMemberDialog}\n workspaceId={workspaceId}\n projectId={projectId}\n projectName={project.name}\n />\n ) : null}\n <EntityIdModal control={idModal} />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProjectDetailPage.js","names":[],"sources":["../../src/pages/ProjectDetailPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useParams, useNavigate } from 'react-router-dom';\nimport {\n ArrowLeft,\n FolderKanban,\n Users,\n UserPlus,\n Activity,\n Archive,\n MoreVertical,\n Clock,\n Pencil,\n} from 'lucide-react';\nimport {\n Button,\n ShowMore,\n GlassCard,\n CardHeader,\n CardTitle,\n CardContent,\n Badge,\n Skeleton,\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n DropdownMenuSeparator,\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n EntityIdModal,\n useEntityIdModal,\n IllustratedEmptyState,\n PagePurpose,\n NextSteps,\n} from '@burdenoff/fe-libs/ui';\nimport { type TagItem } from '@burdenoff/fe-libs/tag';\nimport { ProjectTagsWidget } from '../components/tags/ProjectTagsWidget';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { useProject, useProjectMembersCount } from '../hooks/useProjects';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport {\n useArchiveProject,\n useUnarchiveProject,\n useDeleteProject,\n useTagProject,\n useUntagProject,\n useUpdateProject,\n} from '../hooks/useProjectMutations';\nimport { formatDate } from '../utils/formatters';\nimport { nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport { getProjectDisplayTags, toProjectTag } from '../utils/projectTags';\nimport { getProjectStatusClasses } from '../utils/tokenHelpers';\nimport { useWorkspaceActivity } from '../hooks/useWorkspaceActivity';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { ProjectMemberPickerDialog } from '../components/ProjectMemberPickerDialog';\nimport { RenameProjectDialog } from '../components/RenameProjectDialog';\n\nexport function ProjectDetailPage() {\n const { t } = useI18n();\n const idModal = useEntityIdModal();\n\n const { workspaceId, projectId } = useParams<{ workspaceId: string; projectId: string }>();\n useWorkspacesPageView('workspaces.project-detail', {\n workspaceId,\n projectId,\n requireParams: true,\n });\n const navigate = useNavigate();\n const { basePath = '/workspaces' } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const { data: project, isLoading, error } = useProject(projectId || '', workspaceId);\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const [showAddMemberDialog, setShowAddMemberDialog] = useState(false);\n const [showRenameDialog, setShowRenameDialog] = useState(false);\n\n const { mutate: archiveProject, isPending: isArchiving } = useArchiveProject(workspaceId);\n const { mutate: unarchiveProject, isPending: isUnarchiving } = useUnarchiveProject(workspaceId);\n const { mutate: deleteProject, isPending: isDeleting } = useDeleteProject(workspaceId);\n const { mutateAsync: tagProject, isPending: isTaggingProject } = useTagProject(workspaceId);\n const { mutateAsync: untagProject, isPending: isUntaggingProject } = useUntagProject(workspaceId);\n const { mutateAsync: updateProject, isPending: isUpdatingProject } =\n useUpdateProject(workspaceId);\n const {\n canViewProject,\n canUpdateProject,\n canDeleteProject,\n canArchiveProject,\n canListProjectMembers,\n canAddProjectMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n const { data: projectMemberCount } = useProjectMembersCount(projectId || '', workspaceId);\n const displayTags = useMemo(\n () =>\n getProjectDisplayTags({\n tags: project?.tags ?? [],\n tagAssignments: project?.tagAssignments,\n }),\n [project?.tags, project?.tagAssignments]\n );\n const { data: workspaceActivity } = useWorkspaceActivity(workspaceId);\n const relatedActivityCount = (workspaceActivity ?? []).filter(\n (item) => item.resourceId === projectId || item.resourceType?.toLowerCase() === 'project'\n ).length;\n const widgetTags: TagItem[] = displayTags;\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <Skeleton className=\"h-64\" />\n </div>\n );\n }\n\n if (!canViewProject) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'projectDetail.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n 'projectDetail.noPermissionToViewProject',\n \"You don't have permission to view this project.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const handleBack = () => {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects`));\n };\n\n const handleArchive = () => {\n if (!project) return;\n if (project.archived) {\n unarchiveProject(project.id, {\n onError: (err) => {\n console.error('Failed to unarchive project:', err);\n },\n });\n } else {\n archiveProject(project.id, {\n onError: (err) => {\n console.error('Failed to archive project:', err);\n },\n });\n }\n };\n\n const handleDelete = () => {\n if (!project) return;\n void nativeImpact('medium');\n deleteProject(project.id, {\n onSuccess: () => {\n void nativeNotify('success');\n setShowDeleteDialog(false);\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects`));\n },\n onError: (err) => {\n void nativeNotify('error');\n console.error('Failed to delete project:', err);\n },\n });\n };\n\n const handleViewProjectMembers = () => {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects/${projectId}/members`));\n };\n\n const syncLegacyProjectTags = async (nextTags: TagItem[]) => {\n if (!project) {\n return;\n }\n\n const normalizedNextTags = nextTags.map((tag) => toProjectTag(tag));\n\n try {\n await updateProject({\n id: project.id,\n input: {\n tags: normalizedNextTags,\n },\n });\n } catch (err) {\n void nativeNotify('warning');\n console.error(\n 'Failed to sync legacy project tag mirror after updating structured project tags:',\n err\n );\n }\n };\n\n const handleAddTag = async (tag: TagItem) => {\n if (!project) {\n return;\n }\n\n try {\n await tagProject({\n entityId: project.id,\n tagId: tag.id,\n });\n\n await syncLegacyProjectTags([...displayTags, toProjectTag(tag)]);\n } catch (err) {\n void nativeNotify('error');\n throw err;\n }\n };\n\n const handleRemoveTag = async (tagId: string) => {\n if (!project) {\n return;\n }\n\n const assignmentId = project.tagAssignments?.find(\n (assignment) => assignment.tagId === tagId\n )?.id;\n try {\n await untagProject({\n entityId: project.id,\n tagId,\n assignmentId,\n });\n\n await syncLegacyProjectTags(\n displayTags.filter((tag) => tag.tagId !== tagId && tag.id !== tagId)\n );\n } catch (err) {\n void nativeNotify('error');\n throw err;\n }\n };\n\n if (isLoading) {\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n <Skeleton className=\"h-10 w-32\" />\n <Skeleton className=\"h-10 w-1/3\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n </div>\n <Skeleton className=\"h-48\" />\n </div>\n );\n }\n\n if (error || !project) {\n return (\n <div className=\"space-y-4 p-6\">\n <Button variant=\"ghost\" onClick={handleBack} className=\"gap-2\">\n <ArrowLeft size={20} />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title={tWithFallback(t, 'projectDetail.projectNotFound', 'Project not found')}\n description={tWithFallback(\n t,\n 'projectDetail.projectNotFoundDescription',\n \"The project you're looking for doesn't exist or you don't have permission to view it.\"\n )}\n action={\n <Button onClick={handleBack}>\n <ArrowLeft size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\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 {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3\">\n <div>\n <Button variant=\"ghost\" onClick={handleBack} className=\"gap-2 mb-2 -ml-2\">\n <ArrowLeft size={20} />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\n <div className=\"flex items-center gap-3\">\n <div className=\"p-3 rounded-lg bg-action-primary-bg/10\">\n <FolderKanban className=\"text-primary\" size={28} />\n </div>\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">{project.name}</h1>\n <p className=\"text-sm text-text-secondary font-mono\">{project.key}</p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 mt-3\">\n <Badge className={`border ${getProjectStatusClasses(project.status)}`}>\n {project.status}\n </Badge>\n {project.archived && (\n <Badge\n variant=\"outline\"\n className=\"border-workspace-archived-border text-workspace-archived-text\"\n >\n <Archive size={12} className=\"mr-1\" />\n {tWithFallback(t, 'projectDetail.archived', 'Archived')}\n </Badge>\n )}\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n variant=\"outline\"\n size=\"icon\"\n aria-label={tWithFallback(\n t,\n 'projectDetail.openProjectActionsAriaLabel',\n 'Open project actions'\n )}\n >\n <MoreVertical size={20} />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {canUpdateProject && (\n <DropdownMenuItem onClick={() => setShowRenameDialog(true)}>\n <Pencil size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.renameProject', 'Rename Project')}\n </DropdownMenuItem>\n )}\n {canUpdateProject && (canArchiveProject || canDeleteProject) && (\n <DropdownMenuSeparator />\n )}\n {canArchiveProject && (\n <DropdownMenuItem onClick={handleArchive} disabled={isArchiving || isUnarchiving}>\n <Archive size={16} className=\"mr-2\" />\n {isArchiving || isUnarchiving\n ? tWithFallback(t, 'projectDetail.processing', 'Processing...')\n : project.archived\n ? tWithFallback(t, 'projectDetail.unarchive', 'Unarchive')\n : tWithFallback(t, 'projectDetail.archive', 'Archive')}\n </DropdownMenuItem>\n )}\n {canArchiveProject && canDeleteProject && <DropdownMenuSeparator />}\n {canDeleteProject && (\n <DropdownMenuItem\n onClick={() => setShowDeleteDialog(true)}\n className=\"text-status-error-text\"\n >\n {tWithFallback(t, 'projectDetail.deleteProject', 'Delete Project')}\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n\n <PagePurpose>\n {tWithFallback(\n t,\n 'projectDetail.purpose',\n 'This is a single project — a focused stream of work within its workspace. From here you can manage the people working on it, organise it with tags so it surfaces in filters and reports, and keep track of related activity. Use the menu in the top-right to archive or delete it when it wraps up.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"workspaces-project-detail\"\n steps={[\n ...(canAddProjectMember\n ? [\n {\n id: 'add-member',\n label: tWithFallback(\n t,\n 'projectDetail.stepAddMember',\n 'Add people to the project'\n ),\n description: tWithFallback(\n t,\n 'projectDetail.stepAddMemberDesc',\n 'Pull in the teammates who will actually work on this project.'\n ),\n icon: <UserPlus size={16} />,\n onClick: () => setShowAddMemberDialog(true),\n },\n ]\n : []),\n {\n id: 'tag-project',\n label: tWithFallback(t, 'projectDetail.stepTag', 'Tag the project'),\n description: tWithFallback(\n t,\n 'projectDetail.stepTagDesc',\n 'Add tags so this project shows up in the right filters and views.'\n ),\n icon: <FolderKanban size={16} />,\n onClick: () => {\n document\n .getElementById('project-tags-section')\n ?.scrollIntoView({ behavior: 'smooth', block: 'center' });\n },\n },\n ...(canListProjectMembers\n ? [\n {\n id: 'manage-members',\n label: tWithFallback(\n t,\n 'projectDetail.stepManageMembers',\n 'Review project members'\n ),\n description: tWithFallback(\n t,\n 'projectDetail.stepManageMembersDesc',\n 'See who has access and adjust their project roles.'\n ),\n icon: <Users size={16} />,\n onClick: handleViewProjectMembers,\n },\n ]\n : []),\n {\n id: 'review-activity',\n label: tWithFallback(t, 'projectDetail.stepActivity', 'Check recent activity'),\n description: tWithFallback(\n t,\n 'projectDetail.stepActivityDesc',\n 'Catch up on what has changed across the workspace.'\n ),\n icon: <Activity size={16} />,\n onClick: () => navigate(withCurrentSearch(`${basePath}/${workspaceId}/activity`)),\n },\n ]}\n />\n\n {/* Main + detail rail */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6\">\n {/* Main column */}\n <div className=\"lg:col-span-2 space-y-4 sm:space-y-6\">\n {/* Stat metrics */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <GlassCard treatment=\"metric\" glow>\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-sm font-medium text-text-secondary\">\n <Users size={18} className=\"text-text-muted\" />\n {tWithFallback(t, 'projectDetail.membersCardTitle', 'Members')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-3xl font-bold tabular-nums\">{projectMemberCount ?? 0}</p>\n <p className=\"text-sm text-text-muted\">\n {tWithFallback(t, 'projectDetail.projectMembers', 'Project members')}\n </p>\n\n {canAddProjectMember || canListProjectMembers ? (\n <div className=\"flex flex-wrap gap-2\">\n {canAddProjectMember ? (\n <Button size=\"sm\" onClick={() => setShowAddMemberDialog(true)}>\n <UserPlus size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.addProjectMember', 'Add Member')}\n </Button>\n ) : null}\n\n {canListProjectMembers ? (\n <Button variant=\"outline\" size=\"sm\" onClick={handleViewProjectMembers}>\n <Users size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.manageProjectMembers', 'Manage Members')}\n </Button>\n ) : null}\n </div>\n ) : null}\n </CardContent>\n </GlassCard>\n\n <GlassCard treatment=\"metric\">\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-sm font-medium text-text-secondary\">\n <Activity size={18} className=\"text-text-muted\" />\n {tWithFallback(t, 'projectDetail.activityCardTitle', 'Activity')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold tabular-nums\">{relatedActivityCount}</p>\n <p className=\"text-sm text-text-muted\">\n {tWithFallback(t, 'projectDetail.recentActivityItems', 'Recent activity items')}\n </p>\n </CardContent>\n </GlassCard>\n </div>\n\n <GlassCard treatment=\"plain\" id=\"project-tags-section\" className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>{tWithFallback(t, 'projectDetail.tagsTitle', 'Tags')}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-text-muted\">\n {tWithFallback(\n t,\n 'projectDetail.tagsDescription',\n 'Use universal tags to classify and filter this project across the workspace.'\n )}\n </p>\n <ProjectTagsWidget\n entityType=\"Project\"\n entityId={project.id}\n workspaceId={workspaceId}\n tags={widgetTags}\n onAdd={handleAddTag}\n onRemove={handleRemoveTag}\n className={\n isTaggingProject || isUntaggingProject || isUpdatingProject\n ? 'pointer-events-none opacity-75'\n : undefined\n }\n />\n </CardContent>\n </GlassCard>\n </div>\n\n {/* Detail rail */}\n <aside className=\"lg:sticky lg:top-6 lg:self-start\">\n {/* Project Information */}\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>\n {tWithFallback(t, 'projectDetail.projectInformation', 'Project Information')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 text-sm\">\n <div className=\"space-y-1\">\n <span className=\"text-text-muted flex items-center gap-2\">\n <FolderKanban size={14} />\n {tWithFallback(t, 'projectDetail.projectId', 'Project ID')}\n </span>\n <button\n type=\"button\"\n onClick={() =>\n idModal.show({\n id: project.id,\n name: project.name,\n type: 'Project',\n details: project.key ? [{ label: 'Key', value: project.key }] : undefined,\n })\n }\n className=\"inline-flex items-center gap-1.5 text-xs font-medium text-text-link hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] rounded\"\n >\n {tWithFallback(t, 'projectDetail.getId', 'Get ID')}\n </button>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <FolderKanban size={14} />\n {tWithFallback(t, 'projectDetail.projectKey', 'Project Key')}\n </span>\n <span className=\"font-medium\">{project.key}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Clock size={14} />\n {tWithFallback(t, 'projectDetail.created', 'Created')}\n </span>\n <span>{formatDate(project.createdAt)}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Clock size={14} />\n {tWithFallback(t, 'projectDetail.lastUpdated', 'Last Updated')}\n </span>\n <span>{formatDate(project.updatedAt)}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Users size={14} />\n {tWithFallback(t, 'projectDetail.workspaceId', 'Workspace ID')}\n </span>\n <span className=\"font-mono text-xs block\">{project.workspaceId}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-text-secondary flex items-center gap-2\">\n <Users size={14} />\n {tWithFallback(t, 'projectDetail.creatorId', 'Creator ID')}\n </span>\n <span className=\"font-mono text-xs block\">{project.creatorId}</span>\n </div>\n </div>\n\n {project.config && Object.keys(project.config).length > 0 && (\n <div className=\"pt-4 border-t border-border-seam\">\n <h4 className=\"text-sm font-medium mb-2\">\n {tWithFallback(t, 'projectDetail.configuration', 'Configuration')}\n </h4>\n <pre className=\"text-xs bg-bg-sunken p-3 rounded-lg overflow-auto\">\n {JSON.stringify(project.config, null, 2)}\n </pre>\n </div>\n )}\n\n {/* Quick Actions — progressive disclosure (secondary navigation) */}\n <ShowMore\n collapsedHeight={0}\n label={tWithFallback(t, 'projectDetail.quickActions', 'Quick Actions')}\n >\n <div className=\"flex flex-col gap-2 pt-2\">\n {canListProjectMembers ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"justify-start\"\n onClick={handleViewProjectMembers}\n >\n <Users size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.projectMembersPage', 'Project Members')}\n </Button>\n ) : null}\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"justify-start\"\n onClick={() =>\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/members`))\n }\n >\n <Users size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.workspaceMembers', 'Workspace Members')}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"justify-start\"\n onClick={() =>\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/activity`))\n }\n >\n <Activity size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.workspaceActivity', 'Workspace Activity')}\n </Button>\n </div>\n </ShowMore>\n </CardContent>\n </GlassCard>\n </aside>\n </div>\n\n {/* Delete Confirmation Dialog */}\n <AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n {tWithFallback(t, 'projectDetail.deleteDialogTitle', 'Delete Project')}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {tWithFallback(\n t,\n 'projectDetail.deleteConfirmPrefix',\n 'Are you sure you want to delete \"'\n ) +\n project.name +\n tWithFallback(\n t,\n 'projectDetail.deleteConfirmSuffix',\n '\"? This action cannot be undone and all project data will be permanently removed.'\n )}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={isDeleting}>\n {tWithFallback(t, 'projectDetail.cancel', 'Cancel')}\n </AlertDialogCancel>\n <AlertDialogAction\n onClick={handleDelete}\n disabled={isDeleting}\n className=\"bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90\"\n >\n {isDeleting\n ? tWithFallback(t, 'projectDetail.deleting', 'Deleting...')\n : tWithFallback(t, 'projectDetail.deleteProject', 'Delete Project')}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n {workspaceId && projectId && showAddMemberDialog ? (\n <ProjectMemberPickerDialog\n open={showAddMemberDialog}\n onOpenChange={setShowAddMemberDialog}\n workspaceId={workspaceId}\n projectId={projectId}\n projectName={project.name}\n />\n ) : null}\n {showRenameDialog && (\n <RenameProjectDialog\n project={{ id: project.id, name: project.name, workspaceId: project.workspaceId }}\n open={showRenameDialog}\n onOpenChange={setShowRenameDialog}\n />\n )}\n <EntityIdModal control={idModal} />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -67,8 +67,8 @@ function D() {
67
67
  U?.hasMore,
68
68
  j,
69
69
  O
70
- ]), { mutateAsync: _e } = s(O), { mutateAsync: ve } = l(O), { mutateAsync: ye } = c(O), q = p(() => K?.items.filter((e) => N.has(e.id)) ?? [], [K?.items, N]), J = p(() => q.filter((e) => !e.archived), [q]), Y = p(() => q.filter((e) => e.archived), [q]), X = (K?.items.length ?? 0) > 0 && (K?.items ?? []).every((e) => N.has(e.id)), be = (K?.items ?? []).some((e) => N.has(e.id)) && !X, { canCreateProject: Z, canDeleteProject: xe, canArchiveProject: Q, isLoading: Se } = a();
71
- if (Se) return /* @__PURE__ */ _("div", {
70
+ ]), { mutateAsync: _e } = s(O), { mutateAsync: ve } = l(O), { mutateAsync: ye } = c(O), q = p(() => K?.items.filter((e) => N.has(e.id)) ?? [], [K?.items, N]), J = p(() => q.filter((e) => !e.archived), [q]), Y = p(() => q.filter((e) => e.archived), [q]), X = (K?.items.length ?? 0) > 0 && (K?.items ?? []).every((e) => N.has(e.id)), be = (K?.items ?? []).some((e) => N.has(e.id)) && !X, { canCreateProject: Z, canUpdateProject: xe, canDeleteProject: Se, canArchiveProject: Q, isLoading: Ce } = a();
71
+ if (Ce) return /* @__PURE__ */ _("div", {
72
72
  className: "space-y-4 p-6",
73
73
  children: [/* @__PURE__ */ g(E, { className: "h-10 w-1/3" }), /* @__PURE__ */ _("div", {
74
74
  className: "grid grid-cols-1 md:grid-cols-3 gap-4",
@@ -79,23 +79,23 @@ function D() {
79
79
  ]
80
80
  })]
81
81
  });
82
- let Ce = () => {
82
+ let we = () => {
83
83
  he({});
84
- }, we = (e) => {
84
+ }, Te = (e) => {
85
85
  if (e === "all") {
86
86
  let e = { ...j };
87
87
  delete e.status, M(e);
88
88
  } else M({ status: e });
89
- }, Te = (e) => {
89
+ }, Ee = (e) => {
90
90
  if (e === "all") {
91
91
  let e = { ...j };
92
92
  delete e.archived, M(e);
93
93
  } else M({ archived: e === "true" });
94
94
  }, $ = (e) => {
95
95
  z((t) => t.some((t) => t.tagId === e.tagId) ? t.filter((t) => t.tagId !== e.tagId) : [...t, e]);
96
- }, Ee = () => {
96
+ }, De = () => {
97
97
  L(""), z([]), V(!0), me();
98
- }, De = (e) => {
98
+ }, Oe = (e) => {
99
99
  (K?.items ?? []).forEach((t) => {
100
100
  e !== N.has(t.id) && P(t.id);
101
101
  });
@@ -133,7 +133,7 @@ function D() {
133
133
  children: /* @__PURE__ */ g(ne, { size: 16 })
134
134
  })]
135
135
  }), Z && /* @__PURE__ */ _(v, {
136
- onClick: Ce,
136
+ onClick: we,
137
137
  children: [/* @__PURE__ */ g(h, { className: "size-4 sm:mr-2" }), /* @__PURE__ */ g("span", {
138
138
  className: "hidden sm:inline",
139
139
  children: t(D, "projectsList.createProject", "Create Project")
@@ -159,7 +159,7 @@ function D() {
159
159
  }),
160
160
  /* @__PURE__ */ _(x, {
161
161
  value: j.status || "all",
162
- onValueChange: we,
162
+ onValueChange: Te,
163
163
  children: [/* @__PURE__ */ g(w, {
164
164
  className: "w-full sm:w-[180px]",
165
165
  children: /* @__PURE__ */ g(T, { placeholder: t(D, "projectsList.filterByStatus", "Filter by status") })
@@ -180,7 +180,7 @@ function D() {
180
180
  }),
181
181
  /* @__PURE__ */ _(x, {
182
182
  value: j.archived === void 0 ? "all" : j.archived ? "true" : "false",
183
- onValueChange: Te,
183
+ onValueChange: Ee,
184
184
  children: [/* @__PURE__ */ g(w, {
185
185
  className: "w-full sm:w-[180px]",
186
186
  children: /* @__PURE__ */ g(T, { placeholder: t(D, "projectsList.filterByArchive", "Filter by archive") })
@@ -201,7 +201,7 @@ function D() {
201
201
  }),
202
202
  (j.search || j.status || j.archived !== void 0 || R.length > 0) && /* @__PURE__ */ g(v, {
203
203
  variant: "outline",
204
- onClick: Ee,
204
+ onClick: De,
205
205
  children: t(D, "projectsList.clearFilters", "Clear Filters")
206
206
  })
207
207
  ]
@@ -288,7 +288,7 @@ function D() {
288
288
  title: t(D, "projectsList.noProjectsFound", "No projects found"),
289
289
  description: j.search ? t(D, "projectsList.adjustSearchOrFilters", "Try adjusting your search or filters") : t(D, "projectsList.createFirstProject", "Create your first project to get started organizing your work"),
290
290
  action: !j.search && Z ? /* @__PURE__ */ _(v, {
291
- onClick: Ce,
291
+ onClick: we,
292
292
  children: [/* @__PURE__ */ g(h, {
293
293
  size: 16,
294
294
  className: "mr-2"
@@ -308,7 +308,7 @@ function D() {
308
308
  ref: (e) => {
309
309
  e && (e.indeterminate = be && !X);
310
310
  },
311
- onChange: (e) => De(e.target.checked),
311
+ onChange: (e) => Oe(e.target.checked),
312
312
  "aria-label": t(D, "projectsList.selectAllAriaLabel", "Select all projects"),
313
313
  className: "size-4 rounded border-border-subtle text-primary"
314
314
  }), /* @__PURE__ */ g("span", { children: t(D, "projectsList.selectAllVisible", "Select all visible") })]
@@ -347,7 +347,7 @@ function D() {
347
347
  disabled: Y.length === 0,
348
348
  children: [t(D, "projectsList.unarchiveSelected", "Unarchive Selected"), Y.length > 0 ? ` (${Y.length})` : ""]
349
349
  }),
350
- O && xe && /* @__PURE__ */ _(v, {
350
+ O && Se && /* @__PURE__ */ _(v, {
351
351
  variant: "outline",
352
352
  size: "sm",
353
353
  onClick: async () => {
@@ -374,7 +374,8 @@ function D() {
374
374
  project: e,
375
375
  selected: N.has(e.id),
376
376
  canArchive: Q,
377
- canDelete: xe,
377
+ canDelete: Se,
378
+ canRename: xe,
378
379
  onTagClick: $,
379
380
  onToggleSelect: (t) => {
380
381
  t !== N.has(e.id) && P(e.id);
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectsListPage.js","names":[],"sources":["../../src/pages/ProjectsListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Plus, Grid3x3, List, Search } from 'lucide-react';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n Button,\n Input,\n Card,\n CardContent,\n GlassCard,\n PageContainer,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\nimport { useMyProjects } from '../hooks/useProjects';\nimport {\n useArchiveProject,\n useDeleteProject,\n useUnarchiveProject,\n} from '../hooks/useProjectMutations';\nimport { ProjectCard } from '../components/ProjectCard';\nimport { CreateProjectDialog } from '../components/CreateProjectDialog';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { tWithFallback } from '../utils/i18n';\nimport { getProjectDisplayTags } from '../utils/projectTags';\nimport type { ProjectTag, Workspace } from '../types';\n\nexport function ProjectsListPage() {\n const { t } = useI18n();\n\n // The URL path `:workspaceId` (when present) intentionally OVERRIDES the active\n // context — it scopes this page to a specific workspace for deep-links/drill-in.\n // The cross-workspace route (`/workspaces/projects`, no path param) leaves it\n // undefined so the list stays cross-workspace. The stale-path-after-context-switch\n // problem is handled by WorkspaceContextPathSync (it rewrites this segment when the\n // active context changes), so the override never points at a stale workspace.\n // (BOFF-2805 follow-up)\n const { workspaceId } = useParams<{ workspaceId: string }>();\n useWorkspacesPageView('workspaces.projects', { workspaceId, requireParams: true });\n const {\n viewMode,\n setViewMode,\n projectFilters,\n setProjectFilters,\n clearProjectFilters,\n selectedProjectIds,\n toggleProjectSelection,\n clearProjectSelection,\n openCreateProjectDialog,\n isCreateProjectDialogOpen,\n } = useWorkspacesStore();\n\n const [searchValue, setSearchValue] = useState(projectFilters.search || '');\n const [activeTagFilters, setActiveTagFilters] = useState<ProjectTag[]>([]);\n const [matchAllTagFilters, setMatchAllTagFilters] = useState(true);\n const debouncedSearch = useDebounce(searchValue, 500);\n\n // Update filters when debounced search changes\n useEffect(() => {\n setProjectFilters({ search: debouncedSearch });\n }, [debouncedSearch, setProjectFilters]);\n\n useEffect(() => {\n if (activeTagFilters.length === 0) {\n setProjectFilters({\n tagIds: undefined,\n tagHierarchyPaths: undefined,\n matchAllTags: undefined,\n includeDescendants: undefined,\n });\n return;\n }\n\n setProjectFilters({\n tagIds: activeTagFilters.map((tag) => tag.tagId),\n tagHierarchyPaths: activeTagFilters\n .map((tag) => tag.hierarchyPath)\n .filter((tagPath): tagPath is string => typeof tagPath === 'string' && tagPath.length > 0),\n matchAllTags: matchAllTagFilters,\n includeDescendants: true,\n });\n }, [activeTagFilters, matchAllTagFilters, setProjectFilters]);\n\n useEffect(() => {\n return () => clearProjectSelection();\n }, [clearProjectSelection]);\n\n // Membership-scoped query: backend filters by actorId, so no client-side\n // permission gate is needed. Page is shared by `/workspaces/projects` and\n // `/workspaces/:workspaceId/projects`; the latter narrows the list below.\n const PROJECTS_PAGE_SIZE = 500;\n const {\n data: myData,\n isLoading,\n error,\n } = useMyProjects({\n limit: PROJECTS_PAGE_SIZE,\n offset: 0,\n });\n\n const data = useMemo(() => {\n const all = myData?.items ?? [];\n const q = (projectFilters.search ?? '').trim().toLowerCase();\n const tagIds = projectFilters.tagIds ?? [];\n const matchAllTags = projectFilters.matchAllTags ?? true;\n\n const matchesWorkspace = (project: (typeof all)[number]) =>\n !workspaceId || project.workspaceId === workspaceId;\n const matchesSearch = (project: (typeof all)[number]) =>\n !q || project.name.toLowerCase().includes(q) || project.key.toLowerCase().includes(q);\n const matchesStatus = (project: (typeof all)[number]) =>\n !projectFilters.status || project.status === projectFilters.status;\n const matchesArchived = (project: (typeof all)[number]) =>\n typeof projectFilters.archived === 'undefined' ||\n project.archived === projectFilters.archived;\n const matchesTags = (project: (typeof all)[number]) => {\n if (tagIds.length === 0) return true;\n const projectTagIds = new Set(getProjectDisplayTags(project).map((t) => t.tagId));\n return matchAllTags\n ? tagIds.every((id) => projectTagIds.has(id))\n : tagIds.some((id) => projectTagIds.has(id));\n };\n\n const filtered = all.filter(\n (project) =>\n matchesWorkspace(project) &&\n matchesSearch(project) &&\n matchesStatus(project) &&\n matchesArchived(project) &&\n matchesTags(project)\n );\n\n // Surface the real backend totals so the \"Showing X of Y\" footer can flag\n // truncation. For narrowed views (workspace/tag filter), the backend total\n // doesn't reflect the post-filter count, so we report the filtered size as\n // total. But we MUST preserve `hasMore` from upstream — if the upstream\n // query was capped (hasMore=true), matches outside the first page are\n // silently hidden, and the footer needs to keep flagging that.\n const apiTotal = myData?.total ?? filtered.length;\n const apiHasMore = myData?.hasMore ?? false;\n const isNarrowed = !!workspaceId || tagIds.length > 0;\n return {\n items: filtered,\n total: isNarrowed ? filtered.length : apiTotal,\n hasMore: apiHasMore,\n };\n }, [myData?.items, myData?.total, myData?.hasMore, projectFilters, workspaceId]);\n\n const { mutateAsync: archiveProject } = useArchiveProject(workspaceId);\n const { mutateAsync: unarchiveProject } = useUnarchiveProject(workspaceId);\n const { mutateAsync: deleteProject } = useDeleteProject(workspaceId);\n\n const selectedProjects = useMemo(\n () => data?.items.filter((project) => selectedProjectIds.has(project.id)) ?? [],\n [data?.items, selectedProjectIds]\n );\n\n const archivableProjects = useMemo(\n () => selectedProjects.filter((project) => !project.archived),\n [selectedProjects]\n );\n\n const unarchivableProjects = useMemo(\n () => selectedProjects.filter((project) => project.archived),\n [selectedProjects]\n );\n\n const allVisibleSelected =\n (data?.items.length ?? 0) > 0 &&\n (data?.items ?? []).every((project) => selectedProjectIds.has(project.id));\n const partiallySelected =\n (data?.items ?? []).some((project) => selectedProjectIds.has(project.id)) &&\n !allVisibleSelected;\n\n const {\n canCreateProject,\n canDeleteProject,\n canArchiveProject,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n </div>\n </div>\n );\n }\n\n // No client-side list gate: this page calls `myProjects` which is\n // membership-scoped — the resolver filters by actorId on the backend, so the\n // data set returned IS the access list. If the user is in zero projects, the\n // page renders the empty state below. Per-action checks (canCreateProject,\n // canDeleteProject, canArchiveProject) remain in place to hide buttons the\n // user isn't authorized for on their workspace.\n\n const handleCreateProject = () => {\n openCreateProjectDialog({} as Workspace);\n };\n\n const handleStatusFilter = (value: string) => {\n if (value === 'all') {\n const nextFilters = { ...projectFilters };\n delete nextFilters.status;\n setProjectFilters(nextFilters);\n } else {\n setProjectFilters({ status: value as 'ACTIVE' | 'INACTIVE' | 'ARCHIVED' });\n }\n };\n\n const handleArchivedFilter = (value: string) => {\n if (value === 'all') {\n const nextFilters = { ...projectFilters };\n delete nextFilters.archived;\n setProjectFilters(nextFilters);\n } else {\n setProjectFilters({ archived: value === 'true' });\n }\n };\n\n const toggleTagFilter = (tag: ProjectTag) => {\n setActiveTagFilters((current) => {\n const exists = current.some((entry) => entry.tagId === tag.tagId);\n if (exists) {\n return current.filter((entry) => entry.tagId !== tag.tagId);\n }\n\n return [...current, tag];\n });\n };\n\n const clearAllProjectFilters = () => {\n setSearchValue('');\n setActiveTagFilters([]);\n setMatchAllTagFilters(true);\n clearProjectFilters();\n };\n\n const toggleAllProjects = (checked: boolean) => {\n (data?.items ?? []).forEach((project) => {\n const isSelected = selectedProjectIds.has(project.id);\n if (checked !== isSelected) {\n toggleProjectSelection(project.id);\n }\n });\n };\n\n const handleBulkArchive = async () => {\n if (archivableProjects.length === 0) return;\n await Promise.allSettled(archivableProjects.map((project) => archiveProject(project.id)));\n clearProjectSelection();\n };\n\n const handleBulkUnarchive = async () => {\n if (unarchivableProjects.length === 0) return;\n await Promise.allSettled(unarchivableProjects.map((project) => unarchiveProject(project.id)));\n clearProjectSelection();\n };\n\n const handleBulkDelete = async () => {\n if (selectedProjects.length === 0) return;\n void nativeImpact('medium');\n const message =\n tWithFallback(t, 'projectsList.bulkDeleteConfirmPrefix', 'Delete ') +\n selectedProjects.length +\n ' ' +\n tWithFallback(t, 'projectsList.bulkDeleteConfirmProject', 'selected project') +\n (selectedProjects.length === 1\n ? ''\n : tWithFallback(t, 'projectsList.bulkDeleteConfirmPluralSuffix', 's')) +\n tWithFallback(t, 'projectsList.bulkDeleteConfirmSuffix', '? This cannot be undone.');\n const native = await nativeConfirm({\n title: 'Delete projects',\n message,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) return;\n\n await Promise.allSettled(selectedProjects.map((project) => deleteProject(project.id)));\n clearProjectSelection();\n };\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'projectsList.pageTitle', 'Projects')}\n </h1>\n <p className=\"text-text-secondary\">\n {tWithFallback(t, 'projectsList.pageSubtitle', 'Manage and organize your projects')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {/* View mode toggle */}\n <div className=\"flex items-center gap-1 border rounded-lg p-1\">\n <Button\n variant={viewMode === 'grid' ? 'secondary' : 'ghost'}\n size=\"sm\"\n onClick={() => setViewMode('grid')}\n className=\"size-8 p-0\"\n aria-label={tWithFallback(\n t,\n 'projectsList.gridViewAriaLabel',\n 'Show projects in grid view'\n )}\n >\n <Grid3x3 size={16} />\n </Button>\n <Button\n variant={viewMode === 'list' ? 'secondary' : 'ghost'}\n size=\"sm\"\n onClick={() => setViewMode('list')}\n className=\"size-8 p-0\"\n aria-label={tWithFallback(\n t,\n 'projectsList.listViewAriaLabel',\n 'Show projects in list view'\n )}\n >\n <List size={16} />\n </Button>\n </div>\n {canCreateProject && (\n <Button onClick={handleCreateProject}>\n <Plus className=\"size-4 sm:mr-2\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, 'projectsList.createProject', 'Create Project')}\n </span>\n </Button>\n )}\n </div>\n </div>\n\n <PagePurpose>\n {tWithFallback(\n t,\n 'projectsList.purpose',\n 'Projects are the individual streams of work inside your workspaces — a launch, a client engagement, a feature. Each carries its own members, tags and activity. Use search, status and tag filters to find the one you need, or create a new project to start tracking work.'\n )}\n </PagePurpose>\n\n {/* Filters & Search */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"relative flex-1\">\n <Search\n className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-text-secondary\"\n size={18}\n />\n <Input\n placeholder={tWithFallback(t, 'projectsList.searchPlaceholder', 'Search projects...')}\n value={searchValue}\n onChange={(e) => setSearchValue(e.target.value)}\n className=\"pl-[3.25rem]\"\n />\n </div>\n <Select value={projectFilters.status || 'all'} onValueChange={handleStatusFilter}>\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.filterByStatus', 'Filter by status')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.statusAll', 'All Statuses')}\n </SelectItem>\n <SelectItem value=\"ACTIVE\">\n {tWithFallback(t, 'projectsList.statusActive', 'Active')}\n </SelectItem>\n <SelectItem value=\"INACTIVE\">\n {tWithFallback(t, 'projectsList.statusInactive', 'Inactive')}\n </SelectItem>\n </SelectContent>\n </Select>\n <Select\n value={\n projectFilters.archived === undefined\n ? 'all'\n : projectFilters.archived\n ? 'true'\n : 'false'\n }\n onValueChange={handleArchivedFilter}\n >\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.filterByArchive', 'Filter by archive')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.archiveAll', 'All Projects')}\n </SelectItem>\n <SelectItem value=\"false\">\n {tWithFallback(t, 'projectsList.archiveActiveProjects', 'Active Projects')}\n </SelectItem>\n <SelectItem value=\"true\">\n {tWithFallback(t, 'projectsList.archiveArchived', 'Archived')}\n </SelectItem>\n </SelectContent>\n </Select>\n {(projectFilters.search ||\n projectFilters.status ||\n projectFilters.archived !== undefined ||\n activeTagFilters.length > 0) && (\n <Button variant=\"outline\" onClick={clearAllProjectFilters}>\n {tWithFallback(t, 'projectsList.clearFilters', 'Clear Filters')}\n </Button>\n )}\n </div>\n\n {activeTagFilters.length > 0 && (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface px-4 py-3\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <p className=\"text-sm font-medium\">\n {tWithFallback(t, 'projectsList.activeTagFilters', 'Active tag filters')}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {tWithFallback(\n t,\n 'projectsList.activeTagFiltersDescription',\n 'Click a project tag to add or remove it from the current filter set.'\n )}\n </p>\n </div>\n <Select\n value={matchAllTagFilters ? 'all' : 'any'}\n onValueChange={(value) => setMatchAllTagFilters(value === 'all')}\n >\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.tagMatchMode', 'Tag match mode')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.tagMatchAll', 'Match all tags')}\n </SelectItem>\n <SelectItem value=\"any\">\n {tWithFallback(t, 'projectsList.tagMatchAny', 'Match any tag')}\n </SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n {activeTagFilters.map((tag) => (\n <TagBadge\n key={tag.tagId}\n id={tag.tagId}\n label={tag.label}\n color={tag.color ?? undefined}\n icon={tag.icon ?? undefined}\n description={tag.description ?? undefined}\n removable\n onRemove={() => toggleTagFilter(tag)}\n size=\"sm\"\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Error State */}\n {error && (\n <GlassCard treatment=\"plain\" className=\"border-status-error-border\">\n <CardContent className=\"pt-6\">\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <p className=\"text-status-error-text font-semibold mb-2\">\n {tWithFallback(t, 'projectsList.errorLoading', 'Error loading projects')}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {error instanceof Error\n ? error.message\n : tWithFallback(t, 'projectsList.unknownError', 'An unknown error occurred')}\n </p>\n </div>\n </CardContent>\n </GlassCard>\n )}\n\n {/* Loading State */}\n {isLoading && (\n <div className=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4\">\n {Array.from({ length: 6 }).map((_, i) => (\n <Card key={i} className=\"border-border-seam\">\n <CardContent className=\"pt-6\">\n <div className=\"space-y-3\">\n <Skeleton className=\"size-12 rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-1/2\" />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n\n {/* Empty State */}\n {!isLoading && !error && data?.items.length === 0 && (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <CardContent className=\"pt-6\">\n <IllustratedEmptyState\n illustration={projectFilters.search ? 'empty-search' : 'empty-data'}\n title={tWithFallback(t, 'projectsList.noProjectsFound', 'No projects found')}\n description={\n projectFilters.search\n ? tWithFallback(\n t,\n 'projectsList.adjustSearchOrFilters',\n 'Try adjusting your search or filters'\n )\n : tWithFallback(\n t,\n 'projectsList.createFirstProject',\n 'Create your first project to get started organizing your work'\n )\n }\n action={\n !projectFilters.search && canCreateProject ? (\n <Button onClick={handleCreateProject}>\n <Plus size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectsList.createProject', 'Create Project')}\n </Button>\n ) : undefined\n }\n />\n </CardContent>\n </GlassCard>\n )}\n\n {/* Projects Grid */}\n {!isLoading && !error && data && data.items.length > 0 && (\n <>\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={allVisibleSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelected && !allVisibleSelected;\n }\n }}\n onChange={(event) => toggleAllProjects(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'projectsList.selectAllAriaLabel',\n 'Select all projects'\n )}\n className=\"size-4 rounded border-border-subtle text-primary\"\n />\n <span>\n {tWithFallback(t, 'projectsList.selectAllVisible', 'Select all visible')}\n </span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">\n {selectedProjectIds.size} {tWithFallback(t, 'projectsList.selected', 'selected')}\n </span>\n {selectedProjectIds.size > 0 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearProjectSelection}>\n {tWithFallback(t, 'projectsList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {/* Bulk mutations are bound to a single workspaceId via the\n archive/unarchive/delete hooks. On the cross-workspace\n /workspaces/projects route (workspaceId is undefined),\n hide them — selections can span workspaces and the bound\n hook would fail authorization for projects outside the\n caller's context. The per-card actions in ProjectCard\n still work because they resolve per-project. */}\n {workspaceId && canArchiveProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkArchive}\n disabled={archivableProjects.length === 0}\n >\n {tWithFallback(t, 'projectsList.archiveSelected', 'Archive Selected')}\n {archivableProjects.length > 0 ? ` (${archivableProjects.length})` : ''}\n </Button>\n )}\n {workspaceId && canArchiveProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkUnarchive}\n disabled={unarchivableProjects.length === 0}\n >\n {tWithFallback(t, 'projectsList.unarchiveSelected', 'Unarchive Selected')}\n {unarchivableProjects.length > 0 ? ` (${unarchivableProjects.length})` : ''}\n </Button>\n )}\n {workspaceId && canDeleteProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkDelete}\n disabled={selectedProjects.length === 0}\n className=\"text-status-error-text\"\n >\n {tWithFallback(t, 'projectsList.deleteSelected', 'Delete Selected')}\n {selectedProjects.length > 0 ? ` (${selectedProjects.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n\n <div\n className={\n viewMode === 'list'\n ? 'space-y-3'\n : 'grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4'\n }\n >\n {data.items.map((project) => (\n <ProjectCard\n key={project.id}\n project={project}\n selected={selectedProjectIds.has(project.id)}\n canArchive={canArchiveProject}\n canDelete={canDeleteProject}\n onTagClick={toggleTagFilter}\n onToggleSelect={(checked) => {\n const isSelected = selectedProjectIds.has(project.id);\n if (checked !== isSelected) {\n toggleProjectSelection(project.id);\n }\n }}\n />\n ))}\n </div>\n\n {/* Pagination Info */}\n {data.total > data.items.length && (\n <div className=\"text-center text-sm text-text-secondary\">\n {tWithFallback(t, 'projectsList.showingPrefix', 'Showing')} {data.items.length}{' '}\n {tWithFallback(t, 'projectsList.showingOf', 'of')} {data.total}{' '}\n {tWithFallback(t, 'projectsList.showingProjects', 'projects')}\n </div>\n )}\n </>\n )}\n\n {/* Create Project Dialog */}\n {isCreateProjectDialogOpen && <CreateProjectDialog workspaceId={workspaceId} />}\n </div>\n </PageContainer>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProjectsListPage.js","names":[],"sources":["../../src/pages/ProjectsListPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Plus, Grid3x3, List, Search } from 'lucide-react';\nimport { TagBadge } from '@burdenoff/fe-libs/tag';\nimport {\n Button,\n Input,\n Card,\n CardContent,\n GlassCard,\n PageContainer,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Skeleton,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useDebounce } from '@burdenoff/fe-libs/shared/hooks';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useWorkspacesStore } from '../store/workspacesStore';\nimport { nativeConfirm, nativeImpact } from '../utils/nativeBridge';\nimport { useMyProjects } from '../hooks/useProjects';\nimport {\n useArchiveProject,\n useDeleteProject,\n useUnarchiveProject,\n} from '../hooks/useProjectMutations';\nimport { ProjectCard } from '../components/ProjectCard';\nimport { CreateProjectDialog } from '../components/CreateProjectDialog';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport { tWithFallback } from '../utils/i18n';\nimport { getProjectDisplayTags } from '../utils/projectTags';\nimport type { ProjectTag, Workspace } from '../types';\n\nexport function ProjectsListPage() {\n const { t } = useI18n();\n\n // The URL path `:workspaceId` (when present) intentionally OVERRIDES the active\n // context — it scopes this page to a specific workspace for deep-links/drill-in.\n // The cross-workspace route (`/workspaces/projects`, no path param) leaves it\n // undefined so the list stays cross-workspace. The stale-path-after-context-switch\n // problem is handled by WorkspaceContextPathSync (it rewrites this segment when the\n // active context changes), so the override never points at a stale workspace.\n // (BOFF-2805 follow-up)\n const { workspaceId } = useParams<{ workspaceId: string }>();\n useWorkspacesPageView('workspaces.projects', { workspaceId, requireParams: true });\n const {\n viewMode,\n setViewMode,\n projectFilters,\n setProjectFilters,\n clearProjectFilters,\n selectedProjectIds,\n toggleProjectSelection,\n clearProjectSelection,\n openCreateProjectDialog,\n isCreateProjectDialogOpen,\n } = useWorkspacesStore();\n\n const [searchValue, setSearchValue] = useState(projectFilters.search || '');\n const [activeTagFilters, setActiveTagFilters] = useState<ProjectTag[]>([]);\n const [matchAllTagFilters, setMatchAllTagFilters] = useState(true);\n const debouncedSearch = useDebounce(searchValue, 500);\n\n // Update filters when debounced search changes\n useEffect(() => {\n setProjectFilters({ search: debouncedSearch });\n }, [debouncedSearch, setProjectFilters]);\n\n useEffect(() => {\n if (activeTagFilters.length === 0) {\n setProjectFilters({\n tagIds: undefined,\n tagHierarchyPaths: undefined,\n matchAllTags: undefined,\n includeDescendants: undefined,\n });\n return;\n }\n\n setProjectFilters({\n tagIds: activeTagFilters.map((tag) => tag.tagId),\n tagHierarchyPaths: activeTagFilters\n .map((tag) => tag.hierarchyPath)\n .filter((tagPath): tagPath is string => typeof tagPath === 'string' && tagPath.length > 0),\n matchAllTags: matchAllTagFilters,\n includeDescendants: true,\n });\n }, [activeTagFilters, matchAllTagFilters, setProjectFilters]);\n\n useEffect(() => {\n return () => clearProjectSelection();\n }, [clearProjectSelection]);\n\n // Membership-scoped query: backend filters by actorId, so no client-side\n // permission gate is needed. Page is shared by `/workspaces/projects` and\n // `/workspaces/:workspaceId/projects`; the latter narrows the list below.\n const PROJECTS_PAGE_SIZE = 500;\n const {\n data: myData,\n isLoading,\n error,\n } = useMyProjects({\n limit: PROJECTS_PAGE_SIZE,\n offset: 0,\n });\n\n const data = useMemo(() => {\n const all = myData?.items ?? [];\n const q = (projectFilters.search ?? '').trim().toLowerCase();\n const tagIds = projectFilters.tagIds ?? [];\n const matchAllTags = projectFilters.matchAllTags ?? true;\n\n const matchesWorkspace = (project: (typeof all)[number]) =>\n !workspaceId || project.workspaceId === workspaceId;\n const matchesSearch = (project: (typeof all)[number]) =>\n !q || project.name.toLowerCase().includes(q) || project.key.toLowerCase().includes(q);\n const matchesStatus = (project: (typeof all)[number]) =>\n !projectFilters.status || project.status === projectFilters.status;\n const matchesArchived = (project: (typeof all)[number]) =>\n typeof projectFilters.archived === 'undefined' ||\n project.archived === projectFilters.archived;\n const matchesTags = (project: (typeof all)[number]) => {\n if (tagIds.length === 0) return true;\n const projectTagIds = new Set(getProjectDisplayTags(project).map((t) => t.tagId));\n return matchAllTags\n ? tagIds.every((id) => projectTagIds.has(id))\n : tagIds.some((id) => projectTagIds.has(id));\n };\n\n const filtered = all.filter(\n (project) =>\n matchesWorkspace(project) &&\n matchesSearch(project) &&\n matchesStatus(project) &&\n matchesArchived(project) &&\n matchesTags(project)\n );\n\n // Surface the real backend totals so the \"Showing X of Y\" footer can flag\n // truncation. For narrowed views (workspace/tag filter), the backend total\n // doesn't reflect the post-filter count, so we report the filtered size as\n // total. But we MUST preserve `hasMore` from upstream — if the upstream\n // query was capped (hasMore=true), matches outside the first page are\n // silently hidden, and the footer needs to keep flagging that.\n const apiTotal = myData?.total ?? filtered.length;\n const apiHasMore = myData?.hasMore ?? false;\n const isNarrowed = !!workspaceId || tagIds.length > 0;\n return {\n items: filtered,\n total: isNarrowed ? filtered.length : apiTotal,\n hasMore: apiHasMore,\n };\n }, [myData?.items, myData?.total, myData?.hasMore, projectFilters, workspaceId]);\n\n const { mutateAsync: archiveProject } = useArchiveProject(workspaceId);\n const { mutateAsync: unarchiveProject } = useUnarchiveProject(workspaceId);\n const { mutateAsync: deleteProject } = useDeleteProject(workspaceId);\n\n const selectedProjects = useMemo(\n () => data?.items.filter((project) => selectedProjectIds.has(project.id)) ?? [],\n [data?.items, selectedProjectIds]\n );\n\n const archivableProjects = useMemo(\n () => selectedProjects.filter((project) => !project.archived),\n [selectedProjects]\n );\n\n const unarchivableProjects = useMemo(\n () => selectedProjects.filter((project) => project.archived),\n [selectedProjects]\n );\n\n const allVisibleSelected =\n (data?.items.length ?? 0) > 0 &&\n (data?.items ?? []).every((project) => selectedProjectIds.has(project.id));\n const partiallySelected =\n (data?.items ?? []).some((project) => selectedProjectIds.has(project.id)) &&\n !allVisibleSelected;\n\n const {\n canCreateProject,\n canUpdateProject,\n canDeleteProject,\n canArchiveProject,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-1/3\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n <Skeleton className=\"h-32\" />\n </div>\n </div>\n );\n }\n\n // No client-side list gate: this page calls `myProjects` which is\n // membership-scoped — the resolver filters by actorId on the backend, so the\n // data set returned IS the access list. If the user is in zero projects, the\n // page renders the empty state below. Per-action checks (canCreateProject,\n // canDeleteProject, canArchiveProject) remain in place to hide buttons the\n // user isn't authorized for on their workspace.\n\n const handleCreateProject = () => {\n openCreateProjectDialog({} as Workspace);\n };\n\n const handleStatusFilter = (value: string) => {\n if (value === 'all') {\n const nextFilters = { ...projectFilters };\n delete nextFilters.status;\n setProjectFilters(nextFilters);\n } else {\n setProjectFilters({ status: value as 'ACTIVE' | 'INACTIVE' | 'ARCHIVED' });\n }\n };\n\n const handleArchivedFilter = (value: string) => {\n if (value === 'all') {\n const nextFilters = { ...projectFilters };\n delete nextFilters.archived;\n setProjectFilters(nextFilters);\n } else {\n setProjectFilters({ archived: value === 'true' });\n }\n };\n\n const toggleTagFilter = (tag: ProjectTag) => {\n setActiveTagFilters((current) => {\n const exists = current.some((entry) => entry.tagId === tag.tagId);\n if (exists) {\n return current.filter((entry) => entry.tagId !== tag.tagId);\n }\n\n return [...current, tag];\n });\n };\n\n const clearAllProjectFilters = () => {\n setSearchValue('');\n setActiveTagFilters([]);\n setMatchAllTagFilters(true);\n clearProjectFilters();\n };\n\n const toggleAllProjects = (checked: boolean) => {\n (data?.items ?? []).forEach((project) => {\n const isSelected = selectedProjectIds.has(project.id);\n if (checked !== isSelected) {\n toggleProjectSelection(project.id);\n }\n });\n };\n\n const handleBulkArchive = async () => {\n if (archivableProjects.length === 0) return;\n await Promise.allSettled(archivableProjects.map((project) => archiveProject(project.id)));\n clearProjectSelection();\n };\n\n const handleBulkUnarchive = async () => {\n if (unarchivableProjects.length === 0) return;\n await Promise.allSettled(unarchivableProjects.map((project) => unarchiveProject(project.id)));\n clearProjectSelection();\n };\n\n const handleBulkDelete = async () => {\n if (selectedProjects.length === 0) return;\n void nativeImpact('medium');\n const message =\n tWithFallback(t, 'projectsList.bulkDeleteConfirmPrefix', 'Delete ') +\n selectedProjects.length +\n ' ' +\n tWithFallback(t, 'projectsList.bulkDeleteConfirmProject', 'selected project') +\n (selectedProjects.length === 1\n ? ''\n : tWithFallback(t, 'projectsList.bulkDeleteConfirmPluralSuffix', 's')) +\n tWithFallback(t, 'projectsList.bulkDeleteConfirmSuffix', '? This cannot be undone.');\n const native = await nativeConfirm({\n title: 'Delete projects',\n message,\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) return;\n\n await Promise.allSettled(selectedProjects.map((project) => deleteProject(project.id)));\n clearProjectSelection();\n };\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl sm:text-3xl font-bold\">\n {tWithFallback(t, 'projectsList.pageTitle', 'Projects')}\n </h1>\n <p className=\"text-text-secondary\">\n {tWithFallback(t, 'projectsList.pageSubtitle', 'Manage and organize your projects')}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {/* View mode toggle */}\n <div className=\"flex items-center gap-1 border rounded-lg p-1\">\n <Button\n variant={viewMode === 'grid' ? 'secondary' : 'ghost'}\n size=\"sm\"\n onClick={() => setViewMode('grid')}\n className=\"size-8 p-0\"\n aria-label={tWithFallback(\n t,\n 'projectsList.gridViewAriaLabel',\n 'Show projects in grid view'\n )}\n >\n <Grid3x3 size={16} />\n </Button>\n <Button\n variant={viewMode === 'list' ? 'secondary' : 'ghost'}\n size=\"sm\"\n onClick={() => setViewMode('list')}\n className=\"size-8 p-0\"\n aria-label={tWithFallback(\n t,\n 'projectsList.listViewAriaLabel',\n 'Show projects in list view'\n )}\n >\n <List size={16} />\n </Button>\n </div>\n {canCreateProject && (\n <Button onClick={handleCreateProject}>\n <Plus className=\"size-4 sm:mr-2\" />\n <span className=\"hidden sm:inline\">\n {tWithFallback(t, 'projectsList.createProject', 'Create Project')}\n </span>\n </Button>\n )}\n </div>\n </div>\n\n <PagePurpose>\n {tWithFallback(\n t,\n 'projectsList.purpose',\n 'Projects are the individual streams of work inside your workspaces — a launch, a client engagement, a feature. Each carries its own members, tags and activity. Use search, status and tag filters to find the one you need, or create a new project to start tracking work.'\n )}\n </PagePurpose>\n\n {/* Filters & Search */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"relative flex-1\">\n <Search\n className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-text-secondary\"\n size={18}\n />\n <Input\n placeholder={tWithFallback(t, 'projectsList.searchPlaceholder', 'Search projects...')}\n value={searchValue}\n onChange={(e) => setSearchValue(e.target.value)}\n className=\"pl-[3.25rem]\"\n />\n </div>\n <Select value={projectFilters.status || 'all'} onValueChange={handleStatusFilter}>\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.filterByStatus', 'Filter by status')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.statusAll', 'All Statuses')}\n </SelectItem>\n <SelectItem value=\"ACTIVE\">\n {tWithFallback(t, 'projectsList.statusActive', 'Active')}\n </SelectItem>\n <SelectItem value=\"INACTIVE\">\n {tWithFallback(t, 'projectsList.statusInactive', 'Inactive')}\n </SelectItem>\n </SelectContent>\n </Select>\n <Select\n value={\n projectFilters.archived === undefined\n ? 'all'\n : projectFilters.archived\n ? 'true'\n : 'false'\n }\n onValueChange={handleArchivedFilter}\n >\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.filterByArchive', 'Filter by archive')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.archiveAll', 'All Projects')}\n </SelectItem>\n <SelectItem value=\"false\">\n {tWithFallback(t, 'projectsList.archiveActiveProjects', 'Active Projects')}\n </SelectItem>\n <SelectItem value=\"true\">\n {tWithFallback(t, 'projectsList.archiveArchived', 'Archived')}\n </SelectItem>\n </SelectContent>\n </Select>\n {(projectFilters.search ||\n projectFilters.status ||\n projectFilters.archived !== undefined ||\n activeTagFilters.length > 0) && (\n <Button variant=\"outline\" onClick={clearAllProjectFilters}>\n {tWithFallback(t, 'projectsList.clearFilters', 'Clear Filters')}\n </Button>\n )}\n </div>\n\n {activeTagFilters.length > 0 && (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface px-4 py-3\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <p className=\"text-sm font-medium\">\n {tWithFallback(t, 'projectsList.activeTagFilters', 'Active tag filters')}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {tWithFallback(\n t,\n 'projectsList.activeTagFiltersDescription',\n 'Click a project tag to add or remove it from the current filter set.'\n )}\n </p>\n </div>\n <Select\n value={matchAllTagFilters ? 'all' : 'any'}\n onValueChange={(value) => setMatchAllTagFilters(value === 'all')}\n >\n <SelectTrigger className=\"w-full sm:w-[180px]\">\n <SelectValue\n placeholder={tWithFallback(t, 'projectsList.tagMatchMode', 'Tag match mode')}\n />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">\n {tWithFallback(t, 'projectsList.tagMatchAll', 'Match all tags')}\n </SelectItem>\n <SelectItem value=\"any\">\n {tWithFallback(t, 'projectsList.tagMatchAny', 'Match any tag')}\n </SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n {activeTagFilters.map((tag) => (\n <TagBadge\n key={tag.tagId}\n id={tag.tagId}\n label={tag.label}\n color={tag.color ?? undefined}\n icon={tag.icon ?? undefined}\n description={tag.description ?? undefined}\n removable\n onRemove={() => toggleTagFilter(tag)}\n size=\"sm\"\n />\n ))}\n </div>\n </div>\n )}\n\n {/* Error State */}\n {error && (\n <GlassCard treatment=\"plain\" className=\"border-status-error-border\">\n <CardContent className=\"pt-6\">\n <div className=\"flex flex-col items-center justify-center py-8 text-center\">\n <p className=\"text-status-error-text font-semibold mb-2\">\n {tWithFallback(t, 'projectsList.errorLoading', 'Error loading projects')}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {error instanceof Error\n ? error.message\n : tWithFallback(t, 'projectsList.unknownError', 'An unknown error occurred')}\n </p>\n </div>\n </CardContent>\n </GlassCard>\n )}\n\n {/* Loading State */}\n {isLoading && (\n <div className=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4\">\n {Array.from({ length: 6 }).map((_, i) => (\n <Card key={i} className=\"border-border-seam\">\n <CardContent className=\"pt-6\">\n <div className=\"space-y-3\">\n <Skeleton className=\"size-12 rounded-lg\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-1/2\" />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n\n {/* Empty State */}\n {!isLoading && !error && data?.items.length === 0 && (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <CardContent className=\"pt-6\">\n <IllustratedEmptyState\n illustration={projectFilters.search ? 'empty-search' : 'empty-data'}\n title={tWithFallback(t, 'projectsList.noProjectsFound', 'No projects found')}\n description={\n projectFilters.search\n ? tWithFallback(\n t,\n 'projectsList.adjustSearchOrFilters',\n 'Try adjusting your search or filters'\n )\n : tWithFallback(\n t,\n 'projectsList.createFirstProject',\n 'Create your first project to get started organizing your work'\n )\n }\n action={\n !projectFilters.search && canCreateProject ? (\n <Button onClick={handleCreateProject}>\n <Plus size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectsList.createProject', 'Create Project')}\n </Button>\n ) : undefined\n }\n />\n </CardContent>\n </GlassCard>\n )}\n\n {/* Projects Grid */}\n {!isLoading && !error && data && data.items.length > 0 && (\n <>\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={allVisibleSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelected && !allVisibleSelected;\n }\n }}\n onChange={(event) => toggleAllProjects(event.target.checked)}\n aria-label={tWithFallback(\n t,\n 'projectsList.selectAllAriaLabel',\n 'Select all projects'\n )}\n className=\"size-4 rounded border-border-subtle text-primary\"\n />\n <span>\n {tWithFallback(t, 'projectsList.selectAllVisible', 'Select all visible')}\n </span>\n </label>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">\n {selectedProjectIds.size} {tWithFallback(t, 'projectsList.selected', 'selected')}\n </span>\n {selectedProjectIds.size > 0 && (\n <Button variant=\"ghost\" size=\"sm\" onClick={clearProjectSelection}>\n {tWithFallback(t, 'projectsList.clearSelection', 'Clear selection')}\n </Button>\n )}\n {/* Bulk mutations are bound to a single workspaceId via the\n archive/unarchive/delete hooks. On the cross-workspace\n /workspaces/projects route (workspaceId is undefined),\n hide them — selections can span workspaces and the bound\n hook would fail authorization for projects outside the\n caller's context. The per-card actions in ProjectCard\n still work because they resolve per-project. */}\n {workspaceId && canArchiveProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkArchive}\n disabled={archivableProjects.length === 0}\n >\n {tWithFallback(t, 'projectsList.archiveSelected', 'Archive Selected')}\n {archivableProjects.length > 0 ? ` (${archivableProjects.length})` : ''}\n </Button>\n )}\n {workspaceId && canArchiveProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkUnarchive}\n disabled={unarchivableProjects.length === 0}\n >\n {tWithFallback(t, 'projectsList.unarchiveSelected', 'Unarchive Selected')}\n {unarchivableProjects.length > 0 ? ` (${unarchivableProjects.length})` : ''}\n </Button>\n )}\n {workspaceId && canDeleteProject && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleBulkDelete}\n disabled={selectedProjects.length === 0}\n className=\"text-status-error-text\"\n >\n {tWithFallback(t, 'projectsList.deleteSelected', 'Delete Selected')}\n {selectedProjects.length > 0 ? ` (${selectedProjects.length})` : ''}\n </Button>\n )}\n </div>\n </div>\n\n <div\n className={\n viewMode === 'list'\n ? 'space-y-3'\n : 'grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4'\n }\n >\n {data.items.map((project) => (\n <ProjectCard\n key={project.id}\n project={project}\n selected={selectedProjectIds.has(project.id)}\n canArchive={canArchiveProject}\n canDelete={canDeleteProject}\n canRename={canUpdateProject}\n onTagClick={toggleTagFilter}\n onToggleSelect={(checked) => {\n const isSelected = selectedProjectIds.has(project.id);\n if (checked !== isSelected) {\n toggleProjectSelection(project.id);\n }\n }}\n />\n ))}\n </div>\n\n {/* Pagination Info */}\n {data.total > data.items.length && (\n <div className=\"text-center text-sm text-text-secondary\">\n {tWithFallback(t, 'projectsList.showingPrefix', 'Showing')} {data.items.length}{' '}\n {tWithFallback(t, 'projectsList.showingOf', 'of')} {data.total}{' '}\n {tWithFallback(t, 'projectsList.showingProjects', 'projects')}\n </div>\n )}\n </>\n )}\n\n {/* Create Project Dialog */}\n {isCreateProjectDialogOpen && <CreateProjectDialog workspaceId={workspaceId} />}\n </div>\n </PageContainer>\n );\n}\n"],"mappings":"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"}