@burdenoff/microfe-workspaces 2026.531.2 → 2026.531.4

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 (44) hide show
  1. package/dist/WorkspacesRoutes.js +36 -17
  2. package/dist/WorkspacesRoutes.js.map +1 -1
  3. package/dist/components/InviteMemberDialog.js +204 -117
  4. package/dist/components/InviteMemberDialog.js.map +1 -1
  5. package/dist/components/MemberListItem.js +2 -1
  6. package/dist/components/MemberListItem.js.map +1 -1
  7. package/dist/components/ProjectMemberPickerDialog.js +211 -0
  8. package/dist/components/ProjectMemberPickerDialog.js.map +1 -0
  9. package/dist/hooks/index.js +1 -1
  10. package/dist/hooks/useMemberMutations.js +13 -13
  11. package/dist/hooks/useMemberMutations.js.map +1 -1
  12. package/dist/hooks/useProjectMemberMutations.js +104 -0
  13. package/dist/hooks/useProjectMemberMutations.js.map +1 -0
  14. package/dist/hooks/useProjectMembers.js +42 -0
  15. package/dist/hooks/useProjectMembers.js.map +1 -0
  16. package/dist/hooks/useWorkspaceMembers.js +60 -24
  17. package/dist/hooks/useWorkspaceMembers.js.map +1 -1
  18. package/dist/hooks/useWorkspacePermissions.js +4 -0
  19. package/dist/hooks/useWorkspacePermissions.js.map +1 -1
  20. package/dist/hooks/useWorkspacesEventEmitter.js.map +1 -1
  21. package/dist/index.js +16 -15
  22. package/dist/pages/ActivityListPage.js +5 -5
  23. package/dist/pages/InviteAcceptancePage.js +211 -207
  24. package/dist/pages/InviteAcceptancePage.js.map +1 -1
  25. package/dist/pages/MembersListPage.js +210 -208
  26. package/dist/pages/MembersListPage.js.map +1 -1
  27. package/dist/pages/PendingInvitesPage.js +124 -120
  28. package/dist/pages/PendingInvitesPage.js.map +1 -1
  29. package/dist/pages/ProjectDetailPage.js +249 -213
  30. package/dist/pages/ProjectDetailPage.js.map +1 -1
  31. package/dist/pages/ProjectMembersPage.js +265 -0
  32. package/dist/pages/ProjectMembersPage.js.map +1 -0
  33. package/dist/pages/ProjectsListPage.js +9 -9
  34. package/dist/pages/WorkspaceDetailPage.js +119 -111
  35. package/dist/pages/WorkspaceDetailPage.js.map +1 -1
  36. package/dist/pages/WorkspaceInvitePage.js +790 -0
  37. package/dist/pages/WorkspaceInvitePage.js.map +1 -0
  38. package/dist/pages/WorkspacesListPage.js +115 -100
  39. package/dist/pages/WorkspacesListPage.js.map +1 -1
  40. package/dist/utils/invitationDisplay.js +25 -0
  41. package/dist/utils/invitationDisplay.js.map +1 -0
  42. package/dist/utils/workspaceBulkInviteCsv.js +42 -0
  43. package/dist/utils/workspaceBulkInviteCsv.js.map +1 -0
  44. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectDetailPage.js","names":[],"sources":["../../src/pages/ProjectDetailPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useParams, useNavigate } from 'react-router-dom';\nimport {\n ArrowLeft,\n FolderKanban,\n Users,\n Activity,\n Archive,\n MoreVertical,\n Clock,\n} from 'lucide-react';\nimport {\n Button,\n Card,\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} 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';\n\nexport function ProjectDetailPage() {\n const { t } = useI18n();\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 [projectTags, setProjectTags] = useState(() => getProjectDisplayTags(project));\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 { 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\n const {\n canViewProject,\n canDeleteProject,\n canArchiveProject,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n useEffect(() => {\n setProjectTags(displayTags);\n }, [displayTags]);\n\n const widgetTags = useMemo<TagItem[]>(() => projectTags, [projectTags]);\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-muted-foreground\">\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 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 const previousTags = projectTags;\n const nextTags = [...projectTags, toProjectTag(tag)];\n setProjectTags(nextTags);\n\n try {\n await tagProject({\n entityId: project.id,\n tagId: tag.id,\n });\n } catch (err) {\n setProjectTags(previousTags);\n throw err;\n }\n\n await syncLegacyProjectTags(nextTags);\n };\n\n const handleRemoveTag = async (tagId: string) => {\n if (!project) {\n return;\n }\n\n const previousTags = projectTags;\n const nextTags = projectTags.filter((tag) => tag.tagId !== tagId && tag.id !== tagId);\n setProjectTags(nextTags);\n\n try {\n const assignmentId = project.tagAssignments?.find(\n (assignment) => assignment.tagId === tagId\n )?.id;\n await untagProject({\n entityId: project.id,\n tagId,\n assignmentId,\n });\n } catch (err) {\n setProjectTags(previousTags);\n throw err;\n }\n\n await syncLegacyProjectTags(nextTags);\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 <div className=\"text-center py-12\">\n <div className=\"rounded-full bg-muted p-6 mb-4 inline-block\">\n <FolderKanban size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'projectDetail.projectNotFound', 'Project not found')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm mx-auto mb-4\">\n {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 </p>\n <Button onClick={handleBack}>\n <ArrowLeft size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\n </div>\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-primary/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-muted-foreground 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-destructive\"\n >\n {tWithFallback(t, 'projectDetail.deleteProject', 'Delete Project')}\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n\n {/* Stats Cards */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Users size={20} className=\"text-muted-foreground\" />\n {tWithFallback(t, 'projectDetail.membersCardTitle', 'Members')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold\">{projectMemberCount ?? 0}</p>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(t, 'projectDetail.projectMembers', 'Project members')}\n </p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Activity size={20} className=\"text-muted-foreground\" />\n {tWithFallback(t, 'projectDetail.activityCardTitle', 'Activity')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold\">{relatedActivityCount}</p>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(t, 'projectDetail.recentActivityItems', 'Recent activity items')}\n </p>\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>{tWithFallback(t, 'projectDetail.tagsTitle', 'Tags')}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\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 </Card>\n\n {/* Project Information */}\n <Card>\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 sm:grid-cols-2 gap-4 text-sm\">\n <div className=\"space-y-1\">\n <span className=\"text-muted-foreground flex items-center gap-2\">\n <FolderKanban size={14} />\n {tWithFallback(t, 'projectDetail.projectId', 'Project ID')}\n </span>\n <span className=\"font-mono text-xs block\">{project.id}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-muted-foreground 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-muted-foreground 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-muted-foreground 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-muted-foreground 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-muted-foreground 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\">\n <h4 className=\"text-sm font-medium mb-2\">\n {tWithFallback(t, 'projectDetail.configuration', 'Configuration')}\n </h4>\n <pre className=\"text-xs bg-muted p-3 rounded-lg overflow-auto\">\n {JSON.stringify(project.config, null, 2)}\n </pre>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Quick Actions */}\n <Card>\n <CardHeader>\n <CardTitle>{tWithFallback(t, 'projectDetail.quickActions', 'Quick Actions')}</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"flex flex-wrap gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/members`))}\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 onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/activity`))}\n >\n <Activity size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.workspaceActivity', 'Workspace Activity')}\n </Button>\n </div>\n </CardContent>\n </Card>\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-destructive text-destructive-foreground hover:bg-destructive/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 </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} from 'lucide-react';\nimport {\n Button,\n Card,\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} 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\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-muted-foreground\">\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 <div className=\"text-center py-12\">\n <div className=\"rounded-full bg-muted p-6 mb-4 inline-block\">\n <FolderKanban size={48} className=\"text-muted-foreground\" />\n </div>\n <h3 className=\"text-lg font-semibold mb-2\">\n {tWithFallback(t, 'projectDetail.projectNotFound', 'Project not found')}\n </h3>\n <p className=\"text-muted-foreground max-w-sm mx-auto mb-4\">\n {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 </p>\n <Button onClick={handleBack}>\n <ArrowLeft size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.backToProjects', 'Back to Projects')}\n </Button>\n </div>\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-primary/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-muted-foreground 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-destructive\"\n >\n {tWithFallback(t, 'projectDetail.deleteProject', 'Delete Project')}\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n </div>\n\n {/* Stats Cards */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Users size={20} className=\"text-muted-foreground\" />\n {tWithFallback(t, 'projectDetail.membersCardTitle', 'Members')}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-3xl font-bold\">{projectMemberCount ?? 0}</p>\n <p className=\"text-sm text-muted-foreground\">\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 </Card>\n\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Activity size={20} className=\"text-muted-foreground\" />\n {tWithFallback(t, 'projectDetail.activityCardTitle', 'Activity')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-3xl font-bold\">{relatedActivityCount}</p>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(t, 'projectDetail.recentActivityItems', 'Recent activity items')}\n </p>\n </CardContent>\n </Card>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>{tWithFallback(t, 'projectDetail.tagsTitle', 'Tags')}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\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 </Card>\n\n {/* Project Information */}\n <Card>\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 sm:grid-cols-2 gap-4 text-sm\">\n <div className=\"space-y-1\">\n <span className=\"text-muted-foreground flex items-center gap-2\">\n <FolderKanban size={14} />\n {tWithFallback(t, 'projectDetail.projectId', 'Project ID')}\n </span>\n <span className=\"font-mono text-xs block\">{project.id}</span>\n </div>\n\n <div className=\"space-y-1\">\n <span className=\"text-muted-foreground 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-muted-foreground 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-muted-foreground 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-muted-foreground 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-muted-foreground 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\">\n <h4 className=\"text-sm font-medium mb-2\">\n {tWithFallback(t, 'projectDetail.configuration', 'Configuration')}\n </h4>\n <pre className=\"text-xs bg-muted p-3 rounded-lg overflow-auto\">\n {JSON.stringify(project.config, null, 2)}\n </pre>\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Quick Actions */}\n <Card>\n <CardHeader>\n <CardTitle>{tWithFallback(t, 'projectDetail.quickActions', 'Quick Actions')}</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"flex flex-wrap gap-2\">\n {canListProjectMembers ? (\n <Button variant=\"outline\" size=\"sm\" onClick={handleViewProjectMembers}>\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 onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/members`))}\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 onClick={() => navigate(withCurrentSearch(`${basePath}/${workspaceId}/activity`))}\n >\n <Activity size={16} className=\"mr-2\" />\n {tWithFallback(t, 'projectDetail.workspaceActivity', 'Workspace Activity')}\n </Button>\n </div>\n </CardContent>\n </Card>\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-destructive text-destructive-foreground hover:bg-destructive/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 </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,265 @@
1
+ import { useWorkspacesContext as e } from "../providers/WorkspacesProvider.js";
2
+ import { useScopedNavigation as t } from "../hooks/useScopedNavigation.js";
3
+ import { tWithFallback as n } from "../utils/i18n.js";
4
+ import { useWorkspacesPageView as r } from "../hooks/useWorkspacesPageView.js";
5
+ import { nativeConfirm as i, nativeImpact as ee, nativeNotify as a } from "../utils/nativeBridge.js";
6
+ import { useWorkspacePermissions as o } from "../hooks/useWorkspacePermissions.js";
7
+ import { useProject as s } from "../hooks/useProjects.js";
8
+ import { formatRelativeTime as c } from "../utils/formatters.js";
9
+ import { useRemoveProjectMember as te } from "../hooks/useProjectMemberMutations.js";
10
+ import { useInfiniteProjectMembers as ne } from "../hooks/useProjectMembers.js";
11
+ import { ProjectMemberPickerDialog as re } from "../components/ProjectMemberPickerDialog.js";
12
+ import { useMemo as l, useState as u } from "react";
13
+ import { ArrowLeft as ie, Search as ae, UserMinus as oe, UserPlus as d, Users as se } from "lucide-react";
14
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
15
+ import { Badge as m, Button as h, Input as g, PageContainer as _, Skeleton as v } from "@burdenoff/fe-libs/ui";
16
+ import { useNavigate as y, useParams as b } from "react-router-dom";
17
+ import { useI18n as x } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
18
+ import { ActorIdentity as S, useActorProfiles as C } from "@burdenoff/fe-libs/shared";
19
+ //#region src/pages/ProjectMembersPage.tsx
20
+ var ce = 100;
21
+ function w(e, t) {
22
+ let n = e[t.userId];
23
+ return n ? {
24
+ name: n.displayName,
25
+ email: n.email ?? void 0
26
+ } : {
27
+ name: t.userId,
28
+ email: void 0
29
+ };
30
+ }
31
+ function T() {
32
+ let { t: T } = x(), le = y(), { workspaceId: E, projectId: D } = b(), { basePath: ue = "/workspaces", apiGatewayUrl: de, authToken: O, organizationId: k } = e(), { withCurrentSearch: A } = t(), [j, M] = u(""), [N, P] = u(!1), [F, I] = u(null), [L, R] = u(null);
33
+ r("workspaces.project-members", {
34
+ workspaceId: E,
35
+ projectId: D,
36
+ requireParams: !0
37
+ });
38
+ let { data: z, isLoading: B, error: V } = s(D || "", E), { data: H, isLoading: fe, error: U, hasNextPage: W, fetchNextPage: G, isFetchingNextPage: K } = ne(D || "", E, ce), { mutate: pe, isPending: q } = te(E), { canViewProject: me, canListProjectMembers: he, canAddProjectMember: J, canRemoveProjectMember: ge, isLoading: Y } = o(), X = l(() => H?.pages.flatMap((e) => e.items) ?? [], [H?.pages]), Z = H?.pages[0]?.total ?? X.length, Q = C(l(() => X.map((e) => ({
39
+ actorId: e.userId,
40
+ actorType: "user"
41
+ })), [X]), {
42
+ apiGatewayUrl: de,
43
+ authToken: O,
44
+ orgId: k,
45
+ workspaceId: E
46
+ }), $ = l(() => {
47
+ let e = j.trim().toLowerCase();
48
+ return e ? X.filter((t) => {
49
+ let n = w(Q, t);
50
+ return n.name.toLowerCase().includes(e) || n.email?.toLowerCase().includes(e) || t.userId.toLowerCase().includes(e);
51
+ }) : X;
52
+ }, [
53
+ Q,
54
+ X,
55
+ j
56
+ ]);
57
+ if (Y) return /* @__PURE__ */ p("div", {
58
+ className: "space-y-4 p-6",
59
+ children: [
60
+ /* @__PURE__ */ f(v, { className: "h-10 w-40" }),
61
+ /* @__PURE__ */ f(v, { className: "h-10 w-64" }),
62
+ /* @__PURE__ */ f(v, { className: "h-40 w-full" })
63
+ ]
64
+ });
65
+ if (!me || !he) return /* @__PURE__ */ f("div", {
66
+ className: "flex min-h-[400px] items-center justify-center",
67
+ children: /* @__PURE__ */ p("div", {
68
+ className: "space-y-2 text-center",
69
+ children: [/* @__PURE__ */ f("h2", {
70
+ className: "text-lg font-semibold",
71
+ children: n(T, "projectMembers.accessDenied", "Access Denied")
72
+ }), /* @__PURE__ */ f("p", {
73
+ className: "text-sm text-muted-foreground",
74
+ children: n(T, "projectMembers.noPermission", "You don't have permission to manage project members.")
75
+ })]
76
+ })
77
+ });
78
+ let _e = () => {
79
+ le(A(`${ue}/${E}/projects/${D}`));
80
+ }, ve = async (e) => {
81
+ let t = w(Q, e);
82
+ ee("medium"), R(null);
83
+ let r = `${n(T, "projectMembers.removeConfirmPrefix", "Remove")} ${t.name} ${n(T, "projectMembers.removeConfirmSuffix", "from this project?")}`, o = await i({
84
+ title: n(T, "projectMembers.removeConfirmTitle", "Remove project member"),
85
+ message: r,
86
+ okButtonTitle: n(T, "projectMembers.removeAction", "Remove"),
87
+ cancelButtonTitle: n(T, "projectMembers.cancel", "Cancel")
88
+ });
89
+ (o === null ? window.confirm(r) : o) && (I(e.userId), pe({
90
+ projectId: e.projectId,
91
+ userId: e.userId
92
+ }, {
93
+ onSuccess: () => {
94
+ a("success"), I(null);
95
+ },
96
+ onError: () => {
97
+ a("error"), R(n(T, "projectMembers.removeError", "Failed to remove project member. Please try again.")), I(null);
98
+ }
99
+ }));
100
+ };
101
+ return /* @__PURE__ */ f(_, {
102
+ padded: !1,
103
+ children: /* @__PURE__ */ p("div", {
104
+ className: "space-y-4 p-3 sm:space-y-6 sm:p-6",
105
+ children: [
106
+ /* @__PURE__ */ p("div", {
107
+ className: "flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between",
108
+ children: [/* @__PURE__ */ p("div", { children: [
109
+ /* @__PURE__ */ p(h, {
110
+ variant: "ghost",
111
+ onClick: _e,
112
+ className: "mb-2 -ml-2 gap-2",
113
+ children: [/* @__PURE__ */ f(ie, { size: 20 }), n(T, "projectMembers.backToProject", "Back to Project")]
114
+ }),
115
+ /* @__PURE__ */ f("h1", {
116
+ className: "text-2xl font-bold sm:text-3xl",
117
+ children: n(T, "projectMembers.pageTitle", "Project Members")
118
+ }),
119
+ /* @__PURE__ */ f("p", {
120
+ className: "text-sm text-muted-foreground sm:text-base",
121
+ children: z?.name ? `${z.name} · ${n(T, "projectMembers.pageSubtitle", "Only existing workspace members can be added to this project.")}` : n(T, "projectMembers.pageSubtitle", "Only existing workspace members can be added to this project.")
122
+ })
123
+ ] }), /* @__PURE__ */ p("div", {
124
+ className: "flex items-center gap-2",
125
+ children: [/* @__PURE__ */ f(m, {
126
+ variant: "secondary",
127
+ children: Z
128
+ }), J ? /* @__PURE__ */ p(h, {
129
+ onClick: () => P(!0),
130
+ disabled: !E || !D,
131
+ children: [/* @__PURE__ */ f(d, { className: "mr-2 size-4" }), n(T, "projectMembers.addMember", "Add Member")]
132
+ }) : null]
133
+ })]
134
+ }),
135
+ /* @__PURE__ */ p("div", {
136
+ className: "relative",
137
+ children: [/* @__PURE__ */ f(ae, { className: "absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" }), /* @__PURE__ */ f(g, {
138
+ value: j,
139
+ onChange: (e) => M(e.target.value),
140
+ placeholder: n(T, "projectMembers.searchPlaceholder", "Search project members"),
141
+ className: "pl-9"
142
+ })]
143
+ }),
144
+ L ? /* @__PURE__ */ f("div", {
145
+ className: "rounded-lg border border-destructive/20 bg-destructive/10 p-4 text-sm text-destructive",
146
+ children: L
147
+ }) : null,
148
+ B || fe ? /* @__PURE__ */ f("div", {
149
+ className: "space-y-4",
150
+ children: Array.from({ length: 4 }).map((e, t) => /* @__PURE__ */ p("div", {
151
+ className: "rounded-lg border bg-card p-4",
152
+ children: [/* @__PURE__ */ f(v, { className: "h-5 w-40" }), /* @__PURE__ */ f(v, { className: "mt-2 h-4 w-56" })]
153
+ }, t))
154
+ }) : V || U ? /* @__PURE__ */ f("div", {
155
+ className: "rounded-lg border border-destructive/20 bg-destructive/10 p-4 text-sm text-destructive",
156
+ children: U instanceof Error && U.message || V instanceof Error && V.message || n(T, "projectMembers.loadError", "Failed to load project members")
157
+ }) : $.length === 0 ? /* @__PURE__ */ p("div", {
158
+ className: "rounded-lg border bg-card p-10 text-center",
159
+ children: [
160
+ /* @__PURE__ */ f(se, { className: "mx-auto mb-4 size-12 text-muted-foreground" }),
161
+ /* @__PURE__ */ f("h2", {
162
+ className: "text-xl font-semibold",
163
+ children: j ? n(T, "projectMembers.noResultsTitle", "No matching members found") : n(T, "projectMembers.emptyTitle", "No project members yet")
164
+ }),
165
+ /* @__PURE__ */ f("p", {
166
+ className: "mt-2 text-muted-foreground",
167
+ children: j ? n(T, "projectMembers.noResultsSubtitle", "Try a different search to find a project member.") : n(T, "projectMembers.emptySubtitle", "Add workspace members to this project so they can collaborate here.")
168
+ }),
169
+ W ? /* @__PURE__ */ f("div", {
170
+ className: "mt-4",
171
+ children: /* @__PURE__ */ f(h, {
172
+ variant: "outline",
173
+ onClick: () => void G(),
174
+ disabled: K,
175
+ children: K ? "Loading more…" : "Load More Members"
176
+ })
177
+ }) : null,
178
+ !j && J ? /* @__PURE__ */ f("div", {
179
+ className: "mt-4",
180
+ children: /* @__PURE__ */ p(h, {
181
+ onClick: () => P(!0),
182
+ children: [/* @__PURE__ */ f(d, { className: "mr-2 size-4" }), n(T, "projectMembers.addFirstMember", "Add First Member")]
183
+ })
184
+ }) : null
185
+ ]
186
+ }) : /* @__PURE__ */ p("div", {
187
+ className: "space-y-3",
188
+ children: [$.map((e) => {
189
+ let t = Q[e.userId], r = w(Q, e), i = q && F === e.userId;
190
+ return /* @__PURE__ */ p("div", {
191
+ className: "flex items-center justify-between gap-4 rounded-lg border bg-card p-4",
192
+ children: [/* @__PURE__ */ p("div", {
193
+ className: "min-w-0 flex-1",
194
+ children: [t ? /* @__PURE__ */ f(S, {
195
+ profile: t,
196
+ variant: "cell"
197
+ }) : /* @__PURE__ */ p("div", {
198
+ className: "min-w-0",
199
+ children: [/* @__PURE__ */ f("p", {
200
+ className: "truncate font-medium",
201
+ children: r.name
202
+ }), r.email ? /* @__PURE__ */ f("p", {
203
+ className: "truncate text-sm text-muted-foreground",
204
+ children: r.email
205
+ }) : /* @__PURE__ */ f("p", {
206
+ className: "truncate text-xs text-muted-foreground",
207
+ children: e.userId
208
+ })]
209
+ }), /* @__PURE__ */ p("div", {
210
+ className: "mt-1 flex flex-wrap items-center gap-2",
211
+ children: [/* @__PURE__ */ f(m, {
212
+ variant: "secondary",
213
+ className: "text-xs",
214
+ children: n(T, "projectMembers.memberBadge", "Project member")
215
+ }), /* @__PURE__ */ p("span", {
216
+ className: "text-xs text-muted-foreground",
217
+ children: [
218
+ n(T, "projectMembers.addedLabel", "Added"),
219
+ " ",
220
+ c(e.createdAt)
221
+ ]
222
+ })]
223
+ })]
224
+ }), ge ? /* @__PURE__ */ p(h, {
225
+ variant: "outline",
226
+ size: "sm",
227
+ onClick: () => void ve(e),
228
+ disabled: q,
229
+ children: [/* @__PURE__ */ f(oe, { className: "mr-2 size-4" }), i ? n(T, "projectMembers.removing", "Removing...") : n(T, "projectMembers.removeAction", "Remove")]
230
+ }) : null]
231
+ }, e.id);
232
+ }), W ? /* @__PURE__ */ p("div", {
233
+ className: "flex flex-col items-center gap-2 rounded-lg border border-dashed bg-muted/20 p-4 text-center",
234
+ children: [/* @__PURE__ */ p("p", {
235
+ className: "text-sm text-muted-foreground",
236
+ children: [
237
+ "Showing ",
238
+ X.length,
239
+ " of ",
240
+ Z,
241
+ " project members."
242
+ ]
243
+ }), /* @__PURE__ */ f(h, {
244
+ variant: "outline",
245
+ onClick: () => void G(),
246
+ disabled: K,
247
+ children: K ? "Loading more…" : "Load More Members"
248
+ })]
249
+ }) : null]
250
+ }),
251
+ E && D && N ? /* @__PURE__ */ f(re, {
252
+ open: N,
253
+ onOpenChange: P,
254
+ workspaceId: E,
255
+ projectId: D,
256
+ projectName: z?.name
257
+ }) : null
258
+ ]
259
+ })
260
+ });
261
+ }
262
+ //#endregion
263
+ export { T as ProjectMembersPage };
264
+
265
+ //# sourceMappingURL=ProjectMembersPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectMembersPage.js","names":[],"sources":["../../src/pages/ProjectMembersPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { ArrowLeft, Search, UserMinus, UserPlus, Users } from 'lucide-react';\nimport {\n ActorIdentity,\n useActorProfiles,\n type ActorInput,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport { Badge, Button, Input, PageContainer, Skeleton } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ProjectMemberPickerDialog } from '../components/ProjectMemberPickerDialog';\nimport { useInfiniteProjectMembers } from '../hooks/useProjectMembers';\nimport { useProject } from '../hooks/useProjects';\nimport { useRemoveProjectMember } from '../hooks/useProjectMemberMutations';\nimport { useScopedNavigation } from '../hooks/useScopedNavigation';\nimport { useWorkspacePermissions } from '../hooks/useWorkspacePermissions';\nimport { useWorkspacesContext } from '../providers/WorkspacesProvider';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { tWithFallback } from '../utils/i18n';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport { useWorkspacesPageView } from '../hooks/useWorkspacesPageView';\nimport type { ProjectMember } from '../types';\n\nconst PROJECT_MEMBER_PAGE_SIZE = 100;\n\nfunction getMemberFallback(profileMap: ActorProfileMap, member: ProjectMember) {\n const profile = profileMap[member.userId];\n if (profile) {\n return {\n name: profile.displayName,\n email: profile.email ?? undefined,\n };\n }\n\n return {\n name: member.userId,\n email: undefined,\n };\n}\n\nexport function ProjectMembersPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { workspaceId, projectId } = useParams<{ workspaceId: string; projectId: string }>();\n const {\n basePath = '/workspaces',\n apiGatewayUrl,\n authToken,\n organizationId,\n } = useWorkspacesContext();\n const { withCurrentSearch } = useScopedNavigation();\n const [searchValue, setSearchValue] = useState('');\n const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);\n const [removingUserId, setRemovingUserId] = useState<string | null>(null);\n const [removeError, setRemoveError] = useState<string | null>(null);\n\n useWorkspacesPageView('workspaces.project-members', {\n workspaceId,\n projectId,\n requireParams: true,\n });\n\n const {\n data: project,\n isLoading: isLoadingProject,\n error: projectError,\n } = useProject(projectId || '', workspaceId);\n const {\n data: projectMembersPages,\n isLoading: isLoadingMembers,\n error: membersError,\n hasNextPage: hasMoreProjectMembers,\n fetchNextPage: fetchNextProjectMembersPage,\n isFetchingNextPage: isFetchingNextProjectMembersPage,\n } = useInfiniteProjectMembers(projectId || '', workspaceId, PROJECT_MEMBER_PAGE_SIZE);\n const { mutate: removeProjectMember, isPending: isRemovingProjectMember } =\n useRemoveProjectMember(workspaceId);\n\n const {\n canViewProject,\n canListProjectMembers,\n canAddProjectMember,\n canRemoveProjectMember,\n isLoading: permissionsLoading,\n } = useWorkspacePermissions();\n\n const projectMembers = useMemo(\n () => projectMembersPages?.pages.flatMap((page) => page.items) ?? [],\n [projectMembersPages?.pages]\n );\n const totalProjectMembers = projectMembersPages?.pages[0]?.total ?? projectMembers.length;\n const actors = useMemo<ActorInput[]>(\n () => projectMembers.map((member) => ({ actorId: member.userId, actorType: 'user' })),\n [projectMembers]\n );\n const profileMap: ActorProfileMap = useActorProfiles(actors, {\n apiGatewayUrl,\n authToken,\n orgId: organizationId,\n workspaceId,\n });\n\n const filteredMembers = useMemo(() => {\n const needle = searchValue.trim().toLowerCase();\n if (!needle) {\n return projectMembers;\n }\n\n return projectMembers.filter((member) => {\n const fallback = getMemberFallback(profileMap, member);\n return (\n fallback.name.toLowerCase().includes(needle) ||\n fallback.email?.toLowerCase().includes(needle) ||\n member.userId.toLowerCase().includes(needle)\n );\n });\n }, [profileMap, projectMembers, searchValue]);\n\n if (permissionsLoading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-10 w-40\" />\n <Skeleton className=\"h-10 w-64\" />\n <Skeleton className=\"h-40 w-full\" />\n </div>\n );\n }\n\n if (!canViewProject || !canListProjectMembers) {\n return (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <div className=\"space-y-2 text-center\">\n <h2 className=\"text-lg font-semibold\">\n {tWithFallback(t, 'projectMembers.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(\n t,\n 'projectMembers.noPermission',\n \"You don't have permission to manage project members.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const handleBack = () => {\n navigate(withCurrentSearch(`${basePath}/${workspaceId}/projects/${projectId}`));\n };\n\n const handleRemoveMember = async (member: ProjectMember) => {\n const fallback = getMemberFallback(profileMap, member);\n void nativeImpact('medium');\n setRemoveError(null);\n const message = `${tWithFallback(t, 'projectMembers.removeConfirmPrefix', 'Remove')} ${fallback.name} ${tWithFallback(t, 'projectMembers.removeConfirmSuffix', 'from this project?')}`;\n const native = await nativeConfirm({\n title: tWithFallback(t, 'projectMembers.removeConfirmTitle', 'Remove project member'),\n message,\n okButtonTitle: tWithFallback(t, 'projectMembers.removeAction', 'Remove'),\n cancelButtonTitle: tWithFallback(t, 'projectMembers.cancel', 'Cancel'),\n });\n const confirmed = native === null ? window.confirm(message) : native;\n if (!confirmed) {\n return;\n }\n\n setRemovingUserId(member.userId);\n removeProjectMember(\n { projectId: member.projectId, userId: member.userId },\n {\n onSuccess: () => {\n void nativeNotify('success');\n setRemovingUserId(null);\n },\n onError: () => {\n void nativeNotify('error');\n setRemoveError(\n tWithFallback(\n t,\n 'projectMembers.removeError',\n 'Failed to remove project member. Please try again.'\n )\n );\n setRemovingUserId(null);\n },\n }\n );\n };\n\n return (\n <PageContainer padded={false}>\n <div className=\"space-y-4 p-3 sm:space-y-6 sm:p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div>\n <Button variant=\"ghost\" onClick={handleBack} className=\"mb-2 -ml-2 gap-2\">\n <ArrowLeft size={20} />\n {tWithFallback(t, 'projectMembers.backToProject', 'Back to Project')}\n </Button>\n <h1 className=\"text-2xl font-bold sm:text-3xl\">\n {tWithFallback(t, 'projectMembers.pageTitle', 'Project Members')}\n </h1>\n <p className=\"text-sm text-muted-foreground sm:text-base\">\n {project?.name\n ? `${project.name} · ${tWithFallback(\n t,\n 'projectMembers.pageSubtitle',\n 'Only existing workspace members can be added to this project.'\n )}`\n : tWithFallback(\n t,\n 'projectMembers.pageSubtitle',\n 'Only existing workspace members can be added to this project.'\n )}\n </p>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\">{totalProjectMembers}</Badge>\n {canAddProjectMember ? (\n <Button\n onClick={() => setIsAddDialogOpen(true)}\n disabled={!workspaceId || !projectId}\n >\n <UserPlus className=\"mr-2 size-4\" />\n {tWithFallback(t, 'projectMembers.addMember', 'Add Member')}\n </Button>\n ) : null}\n </div>\n </div>\n\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={searchValue}\n onChange={(event) => setSearchValue(event.target.value)}\n placeholder={tWithFallback(\n t,\n 'projectMembers.searchPlaceholder',\n 'Search project members'\n )}\n className=\"pl-9\"\n />\n </div>\n\n {removeError ? (\n <div className=\"rounded-lg border border-destructive/20 bg-destructive/10 p-4 text-sm text-destructive\">\n {removeError}\n </div>\n ) : null}\n\n {isLoadingProject || isLoadingMembers ? (\n <div className=\"space-y-4\">\n {Array.from({ length: 4 }).map((_, index) => (\n <div key={index} className=\"rounded-lg border bg-card p-4\">\n <Skeleton className=\"h-5 w-40\" />\n <Skeleton className=\"mt-2 h-4 w-56\" />\n </div>\n ))}\n </div>\n ) : projectError || membersError ? (\n <div className=\"rounded-lg border border-destructive/20 bg-destructive/10 p-4 text-sm text-destructive\">\n {(membersError instanceof Error && membersError.message) ||\n (projectError instanceof Error && projectError.message) ||\n tWithFallback(t, 'projectMembers.loadError', 'Failed to load project members')}\n </div>\n ) : filteredMembers.length === 0 ? (\n <div className=\"rounded-lg border bg-card p-10 text-center\">\n <Users className=\"mx-auto mb-4 size-12 text-muted-foreground\" />\n <h2 className=\"text-xl font-semibold\">\n {searchValue\n ? tWithFallback(t, 'projectMembers.noResultsTitle', 'No matching members found')\n : tWithFallback(t, 'projectMembers.emptyTitle', 'No project members yet')}\n </h2>\n <p className=\"mt-2 text-muted-foreground\">\n {searchValue\n ? tWithFallback(\n t,\n 'projectMembers.noResultsSubtitle',\n 'Try a different search to find a project member.'\n )\n : tWithFallback(\n t,\n 'projectMembers.emptySubtitle',\n 'Add workspace members to this project so they can collaborate here.'\n )}\n </p>\n {hasMoreProjectMembers ? (\n <div className=\"mt-4\">\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextProjectMembersPage()}\n disabled={isFetchingNextProjectMembersPage}\n >\n {isFetchingNextProjectMembersPage ? 'Loading more…' : 'Load More Members'}\n </Button>\n </div>\n ) : null}\n {!searchValue && canAddProjectMember ? (\n <div className=\"mt-4\">\n <Button onClick={() => setIsAddDialogOpen(true)}>\n <UserPlus className=\"mr-2 size-4\" />\n {tWithFallback(t, 'projectMembers.addFirstMember', 'Add First Member')}\n </Button>\n </div>\n ) : null}\n </div>\n ) : (\n <div className=\"space-y-3\">\n {filteredMembers.map((member) => {\n const profile = profileMap[member.userId];\n const fallback = getMemberFallback(profileMap, member);\n const isRemoving = isRemovingProjectMember && removingUserId === member.userId;\n\n return (\n <div\n key={member.id}\n className=\"flex items-center justify-between gap-4 rounded-lg border bg-card p-4\"\n >\n <div className=\"min-w-0 flex-1\">\n {profile ? (\n <ActorIdentity profile={profile} variant=\"cell\" />\n ) : (\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium\">{fallback.name}</p>\n {fallback.email ? (\n <p className=\"truncate text-sm text-muted-foreground\">{fallback.email}</p>\n ) : (\n <p className=\"truncate text-xs text-muted-foreground\">{member.userId}</p>\n )}\n </div>\n )}\n\n <div className=\"mt-1 flex flex-wrap items-center gap-2\">\n <Badge variant=\"secondary\" className=\"text-xs\">\n {tWithFallback(t, 'projectMembers.memberBadge', 'Project member')}\n </Badge>\n <span className=\"text-xs text-muted-foreground\">\n {tWithFallback(t, 'projectMembers.addedLabel', 'Added')}{' '}\n {formatRelativeTime(member.createdAt)}\n </span>\n </div>\n </div>\n\n {canRemoveProjectMember ? (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => void handleRemoveMember(member)}\n disabled={isRemovingProjectMember}\n >\n <UserMinus className=\"mr-2 size-4\" />\n {isRemoving\n ? tWithFallback(t, 'projectMembers.removing', 'Removing...')\n : tWithFallback(t, 'projectMembers.removeAction', 'Remove')}\n </Button>\n ) : null}\n </div>\n );\n })}\n\n {hasMoreProjectMembers ? (\n <div className=\"flex flex-col items-center gap-2 rounded-lg border border-dashed bg-muted/20 p-4 text-center\">\n <p className=\"text-sm text-muted-foreground\">\n Showing {projectMembers.length} of {totalProjectMembers} project members.\n </p>\n <Button\n variant=\"outline\"\n onClick={() => void fetchNextProjectMembersPage()}\n disabled={isFetchingNextProjectMembersPage}\n >\n {isFetchingNextProjectMembersPage ? 'Loading more…' : 'Load More Members'}\n </Button>\n </div>\n ) : null}\n </div>\n )}\n\n {workspaceId && projectId && isAddDialogOpen ? (\n <ProjectMemberPickerDialog\n open={isAddDialogOpen}\n onOpenChange={setIsAddDialogOpen}\n workspaceId={workspaceId}\n projectId={projectId}\n projectName={project?.name}\n />\n ) : null}\n </div>\n </PageContainer>\n );\n}\n"],"mappings":"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"}
@@ -2,12 +2,12 @@ import { useWorkspacesStore as e } from "../store/workspacesStore.js";
2
2
  import { tWithFallback as t } from "../utils/i18n.js";
3
3
  import { useWorkspacesPageView as n } from "../hooks/useWorkspacesPageView.js";
4
4
  import { nativeConfirm as r, nativeImpact as i } from "../utils/nativeBridge.js";
5
- import { useMyProjects as a } from "../hooks/useProjects.js";
6
- import { useArchiveProject as o, useDeleteProject as s, useUnarchiveProject as c } from "../hooks/useProjectMutations.js";
5
+ import { useWorkspacePermissions as a } from "../hooks/useWorkspacePermissions.js";
6
+ import { useMyProjects as o } from "../hooks/useProjects.js";
7
+ import { useArchiveProject as s, useDeleteProject as c, useUnarchiveProject as l } from "../hooks/useProjectMutations.js";
7
8
  import { getProjectDisplayTags as ee } from "../utils/projectTags.js";
8
- import { ProjectCard as l } from "../components/ProjectCard.js";
9
- import { CreateProjectDialog as u } from "../components/CreateProjectDialog.js";
10
- import { useWorkspacePermissions as d } from "../hooks/useWorkspacePermissions.js";
9
+ import { ProjectCard as u } from "../components/ProjectCard.js";
10
+ import { CreateProjectDialog as d } from "../components/CreateProjectDialog.js";
11
11
  import { useEffect as f, useMemo as p, useState as m } from "react";
12
12
  import { FolderKanban as te, Grid3x3 as ne, List as re, Plus as h, Search as ie } from "lucide-react";
13
13
  import { Fragment as ae, jsx as g, jsxs as _ } from "react/jsx-runtime";
@@ -47,7 +47,7 @@ function D() {
47
47
  B,
48
48
  M
49
49
  ]), f(() => () => F(), [F]);
50
- let { data: U, isLoading: W, error: G } = a({
50
+ let { data: U, isLoading: W, error: G } = o({
51
51
  limit: 500,
52
52
  offset: 0
53
53
  }), K = p(() => {
@@ -67,7 +67,7 @@ function D() {
67
67
  U?.hasMore,
68
68
  j,
69
69
  O
70
- ]), { mutateAsync: he } = o(O), { mutateAsync: ge } = c(O), { mutateAsync: _e } = s(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)), ve = (K?.items ?? []).some((e) => N.has(e.id)) && !X, { canCreateProject: ye, canDeleteProject: be, canArchiveProject: Z, isLoading: xe } = d();
70
+ ]), { mutateAsync: he } = s(O), { mutateAsync: ge } = l(O), { mutateAsync: _e } = 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)), ve = (K?.items ?? []).some((e) => N.has(e.id)) && !X, { canCreateProject: ye, canDeleteProject: be, canArchiveProject: Z, isLoading: xe } = a();
71
71
  if (xe) 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", {
@@ -376,7 +376,7 @@ function D() {
376
376
  }),
377
377
  /* @__PURE__ */ g("div", {
378
378
  className: k === "list" ? "space-y-3" : "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4",
379
- children: K.items.map((e) => /* @__PURE__ */ g(l, {
379
+ children: K.items.map((e) => /* @__PURE__ */ g(u, {
380
380
  project: e,
381
381
  selected: N.has(e.id),
382
382
  canArchive: Z,
@@ -402,7 +402,7 @@ function D() {
402
402
  ]
403
403
  })
404
404
  ] }),
405
- me && /* @__PURE__ */ g(u, { workspaceId: O })
405
+ me && /* @__PURE__ */ g(d, { workspaceId: O })
406
406
  ]
407
407
  })
408
408
  });