@burdenoff/microfe-workspaces 2026.616.1 → 2026.624.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.
- package/dist/components/CreateWorkspaceDialog.js +5 -5
- package/dist/components/CreateWorkspaceDialog.js.map +1 -1
- package/dist/components/InviteMemberDialog.js +11 -11
- package/dist/components/InviteMemberDialog.js.map +1 -1
- package/dist/components/ProjectCard.js +99 -76
- package/dist/components/ProjectCard.js.map +1 -1
- package/dist/components/ProjectMemberPickerDialog.js +14 -14
- package/dist/components/ProjectMemberPickerDialog.js.map +1 -1
- package/dist/components/WorkspaceCard.js +3 -3
- package/dist/components/WorkspaceCard.js.map +1 -1
- package/dist/components/WorkspacesErrorBoundary.js +9 -9
- package/dist/components/WorkspacesErrorBoundary.js.map +1 -1
- package/dist/components/tags/ProjectTagsWidget.js +8 -8
- package/dist/components/tags/ProjectTagsWidget.js.map +1 -1
- package/dist/pages/ActivityListPage.js +12 -12
- package/dist/pages/ActivityListPage.js.map +1 -1
- package/dist/pages/CreateWorkspacePage.js +24 -24
- package/dist/pages/CreateWorkspacePage.js.map +1 -1
- package/dist/pages/InviteAcceptancePage.js +46 -46
- package/dist/pages/InviteAcceptancePage.js.map +1 -1
- package/dist/pages/MembersListPage.js +27 -27
- package/dist/pages/MembersListPage.js.map +1 -1
- package/dist/pages/PendingInvitesPage.js +23 -23
- package/dist/pages/PendingInvitesPage.js.map +1 -1
- package/dist/pages/ProjectDetailPage.js +192 -181
- package/dist/pages/ProjectDetailPage.js.map +1 -1
- package/dist/pages/ProjectMembersPage.js +15 -15
- package/dist/pages/ProjectMembersPage.js.map +1 -1
- package/dist/pages/ProjectsListPage.js +16 -16
- package/dist/pages/ProjectsListPage.js.map +1 -1
- package/dist/pages/WorkspaceDetailPage.js +16 -16
- package/dist/pages/WorkspaceDetailPage.js.map +1 -1
- package/dist/pages/WorkspaceInvitePage.js +51 -51
- package/dist/pages/WorkspaceInvitePage.js.map +1 -1
- package/dist/pages/WorkspaceSettingsPage.js +2 -2
- package/dist/pages/WorkspaceSettingsPage.js.map +1 -1
- package/dist/pages/WorkspacesListPage.js +8 -8
- package/dist/pages/WorkspacesListPage.js.map +1 -1
- package/dist/pages/dashboard/DashboardCustomizer.js +3 -3
- package/dist/pages/dashboard/DashboardCustomizer.js.map +1 -1
- package/dist/pages/dashboard/DashboardGrid.js +2 -2
- package/dist/pages/dashboard/DashboardGrid.js.map +1 -1
- package/dist/pages/dashboard/DashboardPage.js +5 -5
- package/dist/pages/dashboard/DashboardPage.js.map +1 -1
- package/dist/pages/dashboard/WidgetWrapper.js +6 -6
- package/dist/pages/dashboard/WidgetWrapper.js.map +1 -1
- package/dist/pages/dashboard/widgets/AgentsWidget.js +6 -6
- package/dist/pages/dashboard/widgets/AgentsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/GroupsWidget.js +3 -3
- package/dist/pages/dashboard/widgets/GroupsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/MembersWidget.js +3 -3
- package/dist/pages/dashboard/widgets/MembersWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/ProjectsWidget.js +2 -2
- package/dist/pages/dashboard/widgets/ProjectsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/RecentActivityWidget.js +3 -3
- package/dist/pages/dashboard/widgets/RecentActivityWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/SessionsWidget.js +4 -4
- package/dist/pages/dashboard/widgets/SessionsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/TagCloudWidget.js +2 -2
- package/dist/pages/dashboard/widgets/TagCloudWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/TipsWidget.js +3 -3
- package/dist/pages/dashboard/widgets/TipsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js +2 -2
- package/dist/pages/dashboard/widgets/UpcomingEventsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/VibesWidget.js +2 -2
- package/dist/pages/dashboard/widgets/VibesWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/WelcomeWidget.js +3 -3
- package/dist/pages/dashboard/widgets/WelcomeWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js +4 -4
- package/dist/pages/dashboard/widgets/WorkflowStatsWidget.js.map +1 -1
- package/dist/pages/dashboard/widgets/WorkspacesWidget.js +2 -2
- package/dist/pages/dashboard/widgets/WorkspacesWidget.js.map +1 -1
- package/package.json +2 -2
|
@@ -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 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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"}
|
|
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 EntityIdModal,\n useEntityIdModal,\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 <div className=\"text-center py-12\">\n <div className=\"rounded-full bg-bg-sunken p-6 mb-4 inline-block\">\n <FolderKanban size={48} className=\"text-text-secondary\" />\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-text-secondary 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-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 {/* 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-text-secondary\" />\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-text-secondary\">\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-text-secondary\" />\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-text-secondary\">\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-text-secondary\">\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-text-secondary 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\">\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 </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-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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"}
|
|
@@ -70,7 +70,7 @@ function T() {
|
|
|
70
70
|
className: "text-lg font-semibold",
|
|
71
71
|
children: n(T, "projectMembers.accessDenied", "Access Denied")
|
|
72
72
|
}), /* @__PURE__ */ f("p", {
|
|
73
|
-
className: "text-sm text-
|
|
73
|
+
className: "text-sm text-text-secondary",
|
|
74
74
|
children: n(T, "projectMembers.noPermission", "You don't have permission to manage project members.")
|
|
75
75
|
})]
|
|
76
76
|
})
|
|
@@ -117,7 +117,7 @@ function T() {
|
|
|
117
117
|
children: n(T, "projectMembers.pageTitle", "Project Members")
|
|
118
118
|
}),
|
|
119
119
|
/* @__PURE__ */ f("p", {
|
|
120
|
-
className: "text-sm text-
|
|
120
|
+
className: "text-sm text-text-secondary sm:text-base",
|
|
121
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
122
|
})
|
|
123
123
|
] }), /* @__PURE__ */ p("div", {
|
|
@@ -134,7 +134,7 @@ function T() {
|
|
|
134
134
|
}),
|
|
135
135
|
/* @__PURE__ */ p("div", {
|
|
136
136
|
className: "relative",
|
|
137
|
-
children: [/* @__PURE__ */ f(ae, { className: "absolute left-3 top-1/2 size-4 -translate-y-1/2 text-
|
|
137
|
+
children: [/* @__PURE__ */ f(ae, { className: "absolute left-3 top-1/2 size-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ f(g, {
|
|
138
138
|
value: j,
|
|
139
139
|
onChange: (e) => M(e.target.value),
|
|
140
140
|
placeholder: n(T, "projectMembers.searchPlaceholder", "Search project members"),
|
|
@@ -142,28 +142,28 @@ function T() {
|
|
|
142
142
|
})]
|
|
143
143
|
}),
|
|
144
144
|
L ? /* @__PURE__ */ f("div", {
|
|
145
|
-
className: "rounded-lg border border-
|
|
145
|
+
className: "rounded-lg border border-status-error-border/20 bg-status-error-bg/10 p-4 text-sm text-status-error-text",
|
|
146
146
|
children: L
|
|
147
147
|
}) : null,
|
|
148
148
|
B || fe ? /* @__PURE__ */ f("div", {
|
|
149
149
|
className: "space-y-4",
|
|
150
150
|
children: Array.from({ length: 4 }).map((e, t) => /* @__PURE__ */ p("div", {
|
|
151
|
-
className: "rounded-lg border bg-
|
|
151
|
+
className: "rounded-lg border bg-bg-surface p-4",
|
|
152
152
|
children: [/* @__PURE__ */ f(v, { className: "h-5 w-40" }), /* @__PURE__ */ f(v, { className: "mt-2 h-4 w-56" })]
|
|
153
153
|
}, t))
|
|
154
154
|
}) : V || U ? /* @__PURE__ */ f("div", {
|
|
155
|
-
className: "rounded-lg border border-
|
|
155
|
+
className: "rounded-lg border border-status-error-border/20 bg-status-error-bg/10 p-4 text-sm text-status-error-text",
|
|
156
156
|
children: U instanceof Error && U.message || V instanceof Error && V.message || n(T, "projectMembers.loadError", "Failed to load project members")
|
|
157
157
|
}) : $.length === 0 ? /* @__PURE__ */ p("div", {
|
|
158
|
-
className: "rounded-lg border bg-
|
|
158
|
+
className: "rounded-lg border bg-bg-surface p-10 text-center",
|
|
159
159
|
children: [
|
|
160
|
-
/* @__PURE__ */ f(se, { className: "mx-auto mb-4 size-12 text-
|
|
160
|
+
/* @__PURE__ */ f(se, { className: "mx-auto mb-4 size-12 text-text-secondary" }),
|
|
161
161
|
/* @__PURE__ */ f("h2", {
|
|
162
162
|
className: "text-xl font-semibold",
|
|
163
163
|
children: j ? n(T, "projectMembers.noResultsTitle", "No matching members found") : n(T, "projectMembers.emptyTitle", "No project members yet")
|
|
164
164
|
}),
|
|
165
165
|
/* @__PURE__ */ f("p", {
|
|
166
|
-
className: "mt-2 text-
|
|
166
|
+
className: "mt-2 text-text-secondary",
|
|
167
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
168
|
}),
|
|
169
169
|
W ? /* @__PURE__ */ f("div", {
|
|
@@ -188,7 +188,7 @@ function T() {
|
|
|
188
188
|
children: [$.map((e) => {
|
|
189
189
|
let t = Q[e.userId], r = w(Q, e), i = q && F === e.userId;
|
|
190
190
|
return /* @__PURE__ */ p("div", {
|
|
191
|
-
className: "flex items-center justify-between gap-4 rounded-lg border bg-
|
|
191
|
+
className: "flex items-center justify-between gap-4 rounded-lg border bg-bg-surface p-4",
|
|
192
192
|
children: [/* @__PURE__ */ p("div", {
|
|
193
193
|
className: "min-w-0 flex-1",
|
|
194
194
|
children: [t ? /* @__PURE__ */ f(S, {
|
|
@@ -200,10 +200,10 @@ function T() {
|
|
|
200
200
|
className: "truncate font-medium",
|
|
201
201
|
children: r.name
|
|
202
202
|
}), r.email ? /* @__PURE__ */ f("p", {
|
|
203
|
-
className: "truncate text-sm text-
|
|
203
|
+
className: "truncate text-sm text-text-secondary",
|
|
204
204
|
children: r.email
|
|
205
205
|
}) : /* @__PURE__ */ f("p", {
|
|
206
|
-
className: "truncate text-xs text-
|
|
206
|
+
className: "truncate text-xs text-text-secondary",
|
|
207
207
|
children: e.userId
|
|
208
208
|
})]
|
|
209
209
|
}), /* @__PURE__ */ p("div", {
|
|
@@ -213,7 +213,7 @@ function T() {
|
|
|
213
213
|
className: "text-xs",
|
|
214
214
|
children: n(T, "projectMembers.memberBadge", "Project member")
|
|
215
215
|
}), /* @__PURE__ */ p("span", {
|
|
216
|
-
className: "text-xs text-
|
|
216
|
+
className: "text-xs text-text-secondary",
|
|
217
217
|
children: [
|
|
218
218
|
n(T, "projectMembers.addedLabel", "Added"),
|
|
219
219
|
" ",
|
|
@@ -230,9 +230,9 @@ function T() {
|
|
|
230
230
|
}) : null]
|
|
231
231
|
}, e.id);
|
|
232
232
|
}), W ? /* @__PURE__ */ p("div", {
|
|
233
|
-
className: "flex flex-col items-center gap-2 rounded-lg border border-dashed bg-
|
|
233
|
+
className: "flex flex-col items-center gap-2 rounded-lg border border-dashed bg-bg-sunken/20 p-4 text-center",
|
|
234
234
|
children: [/* @__PURE__ */ p("p", {
|
|
235
|
-
className: "text-sm text-
|
|
235
|
+
className: "text-sm text-text-secondary",
|
|
236
236
|
children: [
|
|
237
237
|
"Showing ",
|
|
238
238
|
X.length,
|
|
@@ -1 +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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"}
|
|
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-text-secondary\">\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-text-secondary 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-text-secondary\" />\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-status-error-border/20 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\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-bg-surface 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-status-error-border/20 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\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-bg-surface p-10 text-center\">\n <Users className=\"mx-auto mb-4 size-12 text-text-secondary\" />\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-text-secondary\">\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-bg-surface 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-text-secondary\">{fallback.email}</p>\n ) : (\n <p className=\"truncate text-xs text-text-secondary\">{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-text-secondary\">\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-bg-sunken/20 p-4 text-center\">\n <p className=\"text-sm text-text-secondary\">\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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"}
|
|
@@ -111,7 +111,7 @@ function D() {
|
|
|
111
111
|
className: "text-2xl sm:text-3xl font-bold",
|
|
112
112
|
children: t(D, "projectsList.pageTitle", "Projects")
|
|
113
113
|
}), /* @__PURE__ */ g("p", {
|
|
114
|
-
className: "text-
|
|
114
|
+
className: "text-text-secondary",
|
|
115
115
|
children: t(D, "projectsList.pageSubtitle", "Manage and organize your projects")
|
|
116
116
|
})] }), /* @__PURE__ */ _("div", {
|
|
117
117
|
className: "flex items-center gap-2",
|
|
@@ -147,7 +147,7 @@ function D() {
|
|
|
147
147
|
/* @__PURE__ */ _("div", {
|
|
148
148
|
className: "relative flex-1",
|
|
149
149
|
children: [/* @__PURE__ */ g(ie, {
|
|
150
|
-
className: "absolute left-3 top-1/2 transform -translate-y-1/2 text-
|
|
150
|
+
className: "absolute left-3 top-1/2 transform -translate-y-1/2 text-text-secondary",
|
|
151
151
|
size: 18
|
|
152
152
|
}), /* @__PURE__ */ g(oe, {
|
|
153
153
|
placeholder: t(D, "projectsList.searchPlaceholder", "Search projects..."),
|
|
@@ -206,14 +206,14 @@ function D() {
|
|
|
206
206
|
]
|
|
207
207
|
}),
|
|
208
208
|
R.length > 0 && /* @__PURE__ */ _("div", {
|
|
209
|
-
className: "flex flex-col gap-3 rounded-lg border border-border bg-
|
|
209
|
+
className: "flex flex-col gap-3 rounded-lg border border-border-subtle bg-bg-surface px-4 py-3",
|
|
210
210
|
children: [/* @__PURE__ */ _("div", {
|
|
211
211
|
className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
|
|
212
212
|
children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
|
|
213
213
|
className: "text-sm font-medium",
|
|
214
214
|
children: t(D, "projectsList.activeTagFilters", "Active tag filters")
|
|
215
215
|
}), /* @__PURE__ */ g("p", {
|
|
216
|
-
className: "text-xs text-
|
|
216
|
+
className: "text-xs text-text-secondary",
|
|
217
217
|
children: t(D, "projectsList.activeTagFiltersDescription", "Click a project tag to add or remove it from the current filter set.")
|
|
218
218
|
})] }), /* @__PURE__ */ _(x, {
|
|
219
219
|
value: B ? "all" : "any",
|
|
@@ -244,16 +244,16 @@ function D() {
|
|
|
244
244
|
})]
|
|
245
245
|
}),
|
|
246
246
|
G && /* @__PURE__ */ g(y, {
|
|
247
|
-
className: "border-
|
|
247
|
+
className: "border-status-error-border",
|
|
248
248
|
children: /* @__PURE__ */ g(b, {
|
|
249
249
|
className: "pt-6",
|
|
250
250
|
children: /* @__PURE__ */ _("div", {
|
|
251
251
|
className: "flex flex-col items-center justify-center py-8 text-center",
|
|
252
252
|
children: [/* @__PURE__ */ g("p", {
|
|
253
|
-
className: "text-
|
|
253
|
+
className: "text-status-error-text font-semibold mb-2",
|
|
254
254
|
children: t(D, "projectsList.errorLoading", "Error loading projects")
|
|
255
255
|
}), /* @__PURE__ */ g("p", {
|
|
256
|
-
className: "text-sm text-
|
|
256
|
+
className: "text-sm text-text-secondary",
|
|
257
257
|
children: G instanceof Error ? G.message : t(D, "projectsList.unknownError", "An unknown error occurred")
|
|
258
258
|
})]
|
|
259
259
|
})
|
|
@@ -279,10 +279,10 @@ function D() {
|
|
|
279
279
|
className: "flex flex-col items-center justify-center py-12 text-center",
|
|
280
280
|
children: [
|
|
281
281
|
/* @__PURE__ */ g("div", {
|
|
282
|
-
className: "rounded-full bg-
|
|
282
|
+
className: "rounded-full bg-bg-sunken p-6 mb-4",
|
|
283
283
|
children: /* @__PURE__ */ g(te, {
|
|
284
284
|
size: 48,
|
|
285
|
-
className: "text-
|
|
285
|
+
className: "text-text-secondary"
|
|
286
286
|
})
|
|
287
287
|
}),
|
|
288
288
|
/* @__PURE__ */ g("h3", {
|
|
@@ -290,7 +290,7 @@ function D() {
|
|
|
290
290
|
children: t(D, "projectsList.noProjectsFound", "No projects found")
|
|
291
291
|
}),
|
|
292
292
|
/* @__PURE__ */ g("p", {
|
|
293
|
-
className: "text-
|
|
293
|
+
className: "text-text-secondary max-w-sm mb-4",
|
|
294
294
|
children: 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")
|
|
295
295
|
}),
|
|
296
296
|
!j.search && ye && /* @__PURE__ */ _(v, {
|
|
@@ -305,9 +305,9 @@ function D() {
|
|
|
305
305
|
}) }),
|
|
306
306
|
!W && !G && K && K.items.length > 0 && /* @__PURE__ */ _(ae, { children: [
|
|
307
307
|
/* @__PURE__ */ _("div", {
|
|
308
|
-
className: "flex flex-col gap-3 rounded-lg border bg-
|
|
308
|
+
className: "flex flex-col gap-3 rounded-lg border bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between",
|
|
309
309
|
children: [/* @__PURE__ */ _("label", {
|
|
310
|
-
className: "flex items-center gap-2 text-sm text-
|
|
310
|
+
className: "flex items-center gap-2 text-sm text-text-primary",
|
|
311
311
|
children: [/* @__PURE__ */ g("input", {
|
|
312
312
|
type: "checkbox",
|
|
313
313
|
checked: X,
|
|
@@ -316,13 +316,13 @@ function D() {
|
|
|
316
316
|
},
|
|
317
317
|
onChange: (e) => Te(e.target.checked),
|
|
318
318
|
"aria-label": t(D, "projectsList.selectAllAriaLabel", "Select all projects"),
|
|
319
|
-
className: "size-4 rounded border-border text-primary"
|
|
319
|
+
className: "size-4 rounded border-border-subtle text-primary"
|
|
320
320
|
}), /* @__PURE__ */ g("span", { children: t(D, "projectsList.selectAllVisible", "Select all visible") })]
|
|
321
321
|
}), /* @__PURE__ */ _("div", {
|
|
322
322
|
className: "flex flex-wrap items-center gap-2",
|
|
323
323
|
children: [
|
|
324
324
|
/* @__PURE__ */ _("span", {
|
|
325
|
-
className: "text-sm text-
|
|
325
|
+
className: "text-sm text-text-secondary",
|
|
326
326
|
children: [
|
|
327
327
|
N.size,
|
|
328
328
|
" ",
|
|
@@ -368,7 +368,7 @@ function D() {
|
|
|
368
368
|
(n === null ? window.confirm(e) : n) && (await Promise.allSettled(q.map((e) => _e(e.id))), F());
|
|
369
369
|
},
|
|
370
370
|
disabled: q.length === 0,
|
|
371
|
-
className: "text-
|
|
371
|
+
className: "text-status-error-text",
|
|
372
372
|
children: [t(D, "projectsList.deleteSelected", "Delete Selected"), q.length > 0 ? ` (${q.length})` : ""]
|
|
373
373
|
})
|
|
374
374
|
]
|
|
@@ -388,7 +388,7 @@ function D() {
|
|
|
388
388
|
}, e.id))
|
|
389
389
|
}),
|
|
390
390
|
K.total > K.items.length && /* @__PURE__ */ _("div", {
|
|
391
|
-
className: "text-center text-sm text-
|
|
391
|
+
className: "text-center text-sm text-text-secondary",
|
|
392
392
|
children: [
|
|
393
393
|
t(D, "projectsList.showingPrefix", "Showing"),
|
|
394
394
|
" ",
|