@burdenoff/microfe-planmagnet 2026.924.2 → 2026.924.3
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/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoot.js +93 -142
- package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
- package/dist/planmagnet/admin/PlanMagnetAdminRoot.d.ts.map +1 -1
- package/dist/planmagnet/admin/PlanMagnetAdminRoot.js +150 -149
- package/dist/planmagnet/admin/PlanMagnetAdminRoot.js.map +1 -1
- package/dist/planmagnet/pages/DashboardDashboardPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js +113 -116
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/planmagnet/pages/GoalsGoalsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsGoalsPagePage.js +59 -58
- package/dist/planmagnet/pages/GoalsGoalsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +150 -149
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js +108 -107
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +93 -92
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.js +89 -87
- package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +94 -93
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsReleasesPagePage.js +57 -56
- package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +82 -81
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +72 -71
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js +38 -37
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +72 -71
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js +47 -46
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +120 -119
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +131 -130
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +77 -75
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ReportsReportsPagePage.js +71 -70
- package/dist/planmagnet/pages/ReportsReportsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TeamTeamPagePage.js +58 -57
- package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
- package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.js +81 -72
- package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.js.map +1 -1
- package/dist/planmagnet/people/People.d.ts +20 -0
- package/dist/planmagnet/people/People.d.ts.map +1 -0
- package/dist/planmagnet/people/People.js +42 -0
- package/dist/planmagnet/people/People.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlanMagnetAdminRoot.js","names":[],"sources":["../../../src/planmagnet/admin/PlanMagnetAdminRoot.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Checkbox,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { AlertCircle, Archive, FolderKanban, LayoutTemplate, Loader2, Zap } from 'lucide-react';\n\nimport { PlanMagnetProvider, usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { cn } from '../utils/cn';\nimport type { PlanMagnetRootProps } from '../types';\n\n// ------------------------------------------------------------------------------\n// Domain types (mirror the planmagnet subgraph until generated types are fixed)\n// ------------------------------------------------------------------------------\n\ntype ProjectStatus = 'ACTIVE' | 'ARCHIVED' | 'CANCELLED' | 'COMPLETED' | 'ON_HOLD' | 'PLANNING';\n\ninterface AdminProject {\n id: string;\n name: string;\n key: string;\n status: ProjectStatus;\n methodology: string;\n ownerId: string;\n createdAt: string;\n}\n\ninterface AdminBoard {\n id: string;\n name: string;\n type: string;\n projectId?: string | null;\n}\n\ninterface AdminAutomationRule {\n id: string;\n name: string;\n status: string;\n triggerType: string;\n executionCount: number;\n lastExecutedAt?: string | null;\n createdAt: string;\n}\n\n// ------------------------------------------------------------------------------\n// GraphQL documents (inline so the admin surface does not depend on generated\n// hooks while the rest of the MFE's operation files are being reconciled with\n// the schema).\n// ------------------------------------------------------------------------------\n\nconst ADMIN_PROJECTS_QUERY = gql`\n query AdminPlanmagnetProjects($workspaceId: ID!) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n key\n status\n methodology\n ownerId\n createdAt\n }\n totalCount\n }\n }\n`;\n\nconst ADMIN_BOARDS_QUERY = gql`\n query AdminPlanmagnetBoards($workspaceId: ID!) {\n planmagnetBoards(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n type\n projectId\n }\n totalCount\n }\n }\n`;\n\nconst ADMIN_AUTOMATION_RULES_QUERY = gql`\n query AdminPlanmagnetAutomationRules($workspaceId: ID!) {\n planmagnetAutomationRules(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n status\n triggerType\n executionCount\n lastExecutedAt\n createdAt\n }\n totalCount\n }\n }\n`;\n\nconst ARCHIVE_PROJECT_MUTATION = gql`\n mutation AdminArchiveProjectr($id: ID!) {\n planmagnetUpdateProjectr(id: $id, input: { status: ARCHIVED }) {\n id\n status\n }\n }\n`;\n\n// ------------------------------------------------------------------------------\n// Presentation helpers\n// ------------------------------------------------------------------------------\n\nfunction projectStatusVariantr(status: ProjectStatus) {\n switch (status) {\n case 'ACTIVE':\n return 'success';\n case 'ARCHIVED':\n return 'secondary';\n case 'CANCELLED':\n return 'destructive';\n case 'COMPLETED':\n return 'info';\n case 'ON_HOLD':\n return 'warning';\n case 'PLANNING':\n return 'outline';\n default:\n return 'default';\n }\n}\n\nfunction automationStatusVariantr(status: string) {\n switch (status) {\n case 'ACTIVE':\n return 'success';\n case 'PAUSED':\n return 'warning';\n case 'ERROR':\n return 'destructive';\n case 'DISABLED':\n return 'secondary';\n default:\n return 'default';\n }\n}\n\nfunction formatDate(value: string | null | undefined) {\n if (!value) return '—';\n try {\n return new Date(value).toLocaleDateString();\n } catch {\n return value;\n }\n}\n\nfunction localizeMethodology(value: string) {\n return value.toLowerCase().replace(/_/g, ' ');\n}\n\nfunction localizeTrigger(value: string) {\n return value.toLowerCase().replace(/_/g, ' ');\n}\n\n// ------------------------------------------------------------------------------\n// Sub-components\n// ------------------------------------------------------------------------------\n\nfunction QuotaCard({\n icon: Icon,\n label,\n used,\n limit,\n className,\n}: {\n icon: React.ComponentType<{ className?: string }>;\n label: string;\n used: number;\n limit: number;\n className?: string;\n}) {\n const percentage = Math.min(100, Math.round((used / Math.max(1, limit)) * 100));\n\n return (\n <Card className={className}>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Icon className=\"h-4 w-4\" />\n <span className=\"text-sm\">{label}</span>\n </div>\n <div className=\"mt-2 flex items-baseline gap-2\">\n <span className=\"text-2xl font-semibold\">{used}</span>\n <span className=\"text-sm text-muted-foreground\">/ {limit}</span>\n </div>\n <div className=\"mt-3 h-2 w-full overflow-hidden rounded-full bg-muted\">\n <div\n className={cn(\n 'h-full rounded-full transition-all',\n percentage >= 90 ? 'bg-destructive' : percentage >= 70 ? 'bg-warning' : 'bg-success'\n )}\n style={{ width: `${percentage}%` }}\n />\n </div>\n <p className=\"mt-2 text-xs text-muted-foreground\">{percentage}% used</p>\n </CardContent>\n </Card>\n );\n}\n\nfunction SectionLoader() {\n return (\n <div className=\"space-y-3\">\n <Skeleton className=\"h-8 w-1/3\" />\n <Skeleton className=\"h-32 w-full\" />\n </div>\n );\n}\n\ninterface SimpleTableColumn<T> {\n key: string;\n header: React.ReactNode;\n cell: (row: T) => React.ReactNode;\n className?: string;\n}\n\nfunction SimpleTable<T extends { id: string }>({\n columns,\n data,\n emptyState,\n}: {\n columns: SimpleTableColumn<T>[];\n data: T[];\n emptyState: React.ReactNode;\n}) {\n if (data.length === 0) {\n return <div className=\"py-8 text-center text-sm text-muted-foreground\">{emptyState}</div>;\n }\n\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50\">\n <tr>\n {columns.map((column) => (\n <th\n key={column.key}\n className={cn(\n 'px-4 py-3 text-left font-medium text-muted-foreground',\n column.className\n )}\n >\n {column.header}\n </th>\n ))}\n </tr>\n </thead>\n <tbody className=\"divide-y\">\n {data.map((row) => (\n <tr key={row.id} className=\"hover:bg-muted/30\">\n {columns.map((column) => (\n <td key={`${row.id}-${column.key}`} className={cn('px-4 py-3', column.className)}>\n {column.cell(row)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\n// ------------------------------------------------------------------------------\n// Main dashboard\n// ------------------------------------------------------------------------------\n\nfunction PlanMagnetAdminDashboard() {\n const { workspaceId } = usePlanMagnet();\n const tr = useTr();\n\n const [selectedProjectIds, setSelectedProjectIds] = useState<Set<string>>(new Set());\n const [archiving, setArchiving] = useState(false);\n const [archiveError, setArchiveError] = useState<string | null>(null);\n\n const projectsQuery = useQuery<{\n planmagnetProjects: { items: AdminProject[]; totalCount: number };\n }>(ADMIN_PROJECTS_QUERY, {\n variables: { workspaceId: workspaceId ?? '' },\n skip: !workspaceId,\n });\n\n const boardsQuery = useQuery<{\n planmagnetBoards: { items: AdminBoard[]; totalCount: number };\n }>(ADMIN_BOARDS_QUERY, {\n variables: { workspaceId: workspaceId ?? '' },\n skip: !workspaceId,\n });\n\n const rulesQuery = useQuery<{\n planmagnetAutomationRules: { items: AdminAutomationRule[]; totalCount: number };\n }>(ADMIN_AUTOMATION_RULES_QUERY, {\n variables: { workspaceId: workspaceId ?? '' },\n skip: !workspaceId,\n });\n\n const [archiveProject] = useMutation(ARCHIVE_PROJECT_MUTATION);\n\n const projects = projectsQuery.data?.planmagnetProjects.items ?? [];\n const boards = boardsQuery.data?.planmagnetBoards.items ?? [];\n const rules = rulesQuery.data?.planmagnetAutomationRules.items ?? [];\n\n const activeProjects = useMemo(\n () => projects.filter((project) => project.status === 'ACTIVE'),\n [projects]\n );\n\n const toggleProjectSelection = (id: string) => {\n setSelectedProjectIds((previous) => {\n const next = new Set(previous);\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n return next;\n });\n };\n\n const toggleAll = () => {\n const selectable = activeProjects.filter((project) => project.status !== 'ARCHIVED');\n if (selectedProjectIds.size === selectable.length) {\n setSelectedProjectIds(new Set());\n } else {\n setSelectedProjectIds(new Set(selectable.map((project) => project.id)));\n }\n };\n\n const handleBulkArchive = async () => {\n if (selectedProjectIds.size === 0) return;\n setArchiving(true);\n setArchiveError(null);\n try {\n await Promise.all(\n Array.from(selectedProjectIds).map((id) => archiveProject({ variables: { id } }))\n );\n await projectsQuery.refetch();\n setSelectedProjectIds(new Set());\n } catch (error) {\n setArchiveError(\n error instanceof Error\n ? error.message\n : tr('planmagnet.admin.archive.unknownError', 'Bulk archive failed')\n );\n } finally {\n setArchiving(false);\n }\n };\n\n if (!workspaceId) {\n return (\n <div className=\"min-h-screen bg-background\">\n <div className=\"flex min-h-[320px] flex-col items-center justify-center gap-3 text-muted-foreground\">\n <AlertCircle className=\"h-8 w-8\" />\n <p>\n {tr(\n 'planmagnet.admin.noWorkspace',\n 'Select a workspace to manage PlanMagnet admin settings.'\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const isLoading = projectsQuery.loading || boardsQuery.loading || rulesQuery.loading;\n\n return (\n <div className=\"min-h-screen bg-background\">\n <div className=\"mx-auto max-w-7xl space-y-6 p-6\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">\n {tr('planmagnet.admin.title', 'PlanMagnet Admin')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'planmagnet.admin.purpose',\n 'Workspace quota overview, bulk project archive, and automation rule audit.'\n )}\n </p>\n </div>\n </div>\n\n {/* Quota overview — counts are real; limits are placeholder until a\n dedicated admin quota query exists in the backend. */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <QuotaCard\n icon={FolderKanban}\n label={tr('planmagnet.admin.quota.projects', 'Projects')}\n used={projects.length}\n limit={50}\n />\n <QuotaCard\n icon={LayoutTemplate}\n label={tr('planmagnet.admin.quota.boards', 'Boards')}\n used={boards.length}\n limit={100}\n />\n <QuotaCard\n icon={Zap}\n label={tr('planmagnet.admin.quota.automationRules', 'Automation Rules')}\n used={rules.length}\n limit={25}\n />\n </div>\n\n {isLoading ? (\n <SectionLoader />\n ) : (\n <>\n {/* Bulk project archive */}\n <Card>\n <CardHeader className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <CardTitle>\n {tr('planmagnet.admin.archive.title', 'Bulk Project Archive')}\n </CardTitle>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'planmagnet.admin.archive.subtitle',\n 'Select active projects and archive them in one action.'\n )}\n </p>\n </div>\n <Button\n size=\"sm\"\n variant=\"destructive\"\n disabled={selectedProjectIds.size === 0 || archiving}\n onClick={handleBulkArchive}\n >\n {archiving ? (\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n ) : (\n <Archive className=\"mr-2 h-4 w-4\" />\n )}\n {tr('planmagnet.admin.archive.action', 'Archive')} ({selectedProjectIds.size})\n </Button>\n </CardHeader>\n <CardContent>\n {archiveError && (\n <div className=\"mb-4 flex items-center gap-2 rounded-md bg-destructive/10 p-3 text-sm text-destructive\">\n <AlertCircle className=\"h-4 w-4\" />\n {archiveError}\n </div>\n )}\n <div className=\"rounded-md border\">\n <SimpleTable<AdminProject>\n data={activeProjects}\n columns={[\n {\n key: 'select',\n header: (\n <Checkbox\n checked={\n activeProjects.length > 0 &&\n selectedProjectIds.size === activeProjects.length\n }\n onCheckedChange={toggleAll}\n aria-label={tr(\n 'planmagnet.admin.archive.selectAll',\n 'Select all active projects'\n )}\n />\n ),\n cell: (row) => (\n <Checkbox\n checked={selectedProjectIds.has(row.id)}\n onCheckedChange={() => toggleProjectSelection(row.id)}\n aria-label={tr(\n 'planmagnet.admin.archive.selectProject',\n 'Select project'\n )}\n />\n ),\n className: 'w-12',\n },\n {\n key: 'name',\n header: tr('planmagnet.admin.archive.column.name', 'Project'),\n cell: (row) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.name}</span>\n <span className=\"text-xs text-muted-foreground font-mono\">\n {row.key}\n </span>\n </div>\n ),\n },\n {\n key: 'status',\n header: tr('planmagnet.admin.archive.column.status', 'Status'),\n cell: (row) => (\n <Badge variant={projectStatusVariantr(row.status)}>\n {row.status.toLowerCase().replace('_', ' ')}\n </Badge>\n ),\n },\n {\n key: 'methodology',\n header: tr('planmagnet.admin.archive.column.methodology', 'Methodology'),\n cell: (row) => (\n <span className=\"text-sm capitalize\">\n {localizeMethodology(row.methodology)}\n </span>\n ),\n },\n {\n key: 'owner',\n header: tr('planmagnet.admin.archive.column.owner', 'Owner'),\n cell: (row) => (\n <span className=\"text-sm text-muted-foreground font-mono\">\n {row.ownerId}\n </span>\n ),\n },\n {\n key: 'created',\n header: tr('planmagnet.admin.archive.column.created', 'Created'),\n cell: (row) => <span className=\"text-sm\">{formatDate(row.createdAt)}</span>,\n },\n ]}\n emptyState={tr(\n 'planmagnet.admin.archive.empty',\n 'No active projects to archive.'\n )}\n />\n </div>\n </CardContent>\n </Card>\n\n {/* Automation rule audit */}\n <Card>\n <CardHeader>\n <CardTitle>\n {tr('planmagnet.admin.automation.title', 'Automation Rule Audit')}\n </CardTitle>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'planmagnet.admin.automation.subtitle',\n 'Review rule health, execution counts, and last run timestamps.'\n )}\n </p>\n </CardHeader>\n <CardContent>\n <div className=\"rounded-md border\">\n <SimpleTable<AdminAutomationRule>\n data={rules}\n columns={[\n {\n key: 'name',\n header: tr('planmagnet.admin.automation.column.name', 'Rule'),\n cell: (row) => <span className=\"font-medium\">{row.name}</span>,\n },\n {\n key: 'trigger',\n header: tr('planmagnet.admin.automation.column.trigger', 'Trigger'),\n cell: (row) => (\n <span className=\"text-sm\">{localizeTrigger(row.triggerType)}</span>\n ),\n },\n {\n key: 'status',\n header: tr('planmagnet.admin.automation.column.status', 'Status'),\n cell: (row) => (\n <Badge variant={automationStatusVariantr(row.status)}>\n {row.status.toLowerCase()}\n </Badge>\n ),\n },\n {\n key: 'executions',\n header: tr('planmagnet.admin.automation.column.executions', 'Executions'),\n cell: (row) => <span className=\"text-sm\">{row.executionCount}</span>,\n },\n {\n key: 'lastRun',\n header: tr('planmagnet.admin.automation.column.lastRun', 'Last Run'),\n cell: (row) => (\n <span className=\"text-sm\">{formatDate(row.lastExecutedAt)}</span>\n ),\n },\n {\n key: 'created',\n header: tr('planmagnet.admin.automation.column.created', 'Created'),\n cell: (row) => <span className=\"text-sm\">{formatDate(row.createdAt)}</span>,\n },\n ]}\n emptyState={tr(\n 'planmagnet.admin.automation.empty',\n 'No automation rules found.'\n )}\n />\n </div>\n </CardContent>\n </Card>\n </>\n )}\n </div>\n </div>\n );\n}\n\n// ------------------------------------------------------------------------------\n// Public root export consumed by the admin app shell\n// ------------------------------------------------------------------------------\n\nexport function PlanMagnetAdminRoot(props: PlanMagnetRootProps) {\n return (\n <PlanMagnetProvider {...props}>\n <PlanMagnetAdminDashboard />\n </PlanMagnetProvider>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PlanMagnetAdminRoot.js","names":[],"sources":["../../../src/planmagnet/admin/PlanMagnetAdminRoot.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Checkbox,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { AlertCircle, Archive, FolderKanban, LayoutTemplate, Loader2, Zap } from 'lucide-react';\n\nimport { PlanMagnetProvider, usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { cn } from '../utils/cn';\nimport type { PlanMagnetRootProps } from '../types';\nimport { PersonName } from '../people/People';\n\n// ------------------------------------------------------------------------------\n// Domain types (mirror the planmagnet subgraph until generated types are fixed)\n// ------------------------------------------------------------------------------\n\ntype ProjectStatus = 'ACTIVE' | 'ARCHIVED' | 'CANCELLED' | 'COMPLETED' | 'ON_HOLD' | 'PLANNING';\n\ninterface AdminProject {\n id: string;\n name: string;\n key: string;\n status: ProjectStatus;\n methodology: string;\n ownerId: string;\n createdAt: string;\n}\n\ninterface AdminBoard {\n id: string;\n name: string;\n type: string;\n projectId?: string | null;\n}\n\ninterface AdminAutomationRule {\n id: string;\n name: string;\n status: string;\n triggerType: string;\n executionCount: number;\n lastExecutedAt?: string | null;\n createdAt: string;\n}\n\n// ------------------------------------------------------------------------------\n// GraphQL documents (inline so the admin surface does not depend on generated\n// hooks while the rest of the MFE's operation files are being reconciled with\n// the schema).\n// ------------------------------------------------------------------------------\n\nconst ADMIN_PROJECTS_QUERY = gql`\n query AdminPlanmagnetProjects($workspaceId: ID!) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n key\n status\n methodology\n ownerId\n createdAt\n }\n totalCount\n }\n }\n`;\n\nconst ADMIN_BOARDS_QUERY = gql`\n query AdminPlanmagnetBoards($workspaceId: ID!) {\n planmagnetBoards(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n type\n projectId\n }\n totalCount\n }\n }\n`;\n\nconst ADMIN_AUTOMATION_RULES_QUERY = gql`\n query AdminPlanmagnetAutomationRules($workspaceId: ID!) {\n planmagnetAutomationRules(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n status\n triggerType\n executionCount\n lastExecutedAt\n createdAt\n }\n totalCount\n }\n }\n`;\n\nconst ARCHIVE_PROJECT_MUTATION = gql`\n mutation AdminArchiveProjectr($id: ID!) {\n planmagnetUpdateProjectr(id: $id, input: { status: ARCHIVED }) {\n id\n status\n }\n }\n`;\n\n// ------------------------------------------------------------------------------\n// Presentation helpers\n// ------------------------------------------------------------------------------\n\nfunction projectStatusVariantr(status: ProjectStatus) {\n switch (status) {\n case 'ACTIVE':\n return 'success';\n case 'ARCHIVED':\n return 'secondary';\n case 'CANCELLED':\n return 'destructive';\n case 'COMPLETED':\n return 'info';\n case 'ON_HOLD':\n return 'warning';\n case 'PLANNING':\n return 'outline';\n default:\n return 'default';\n }\n}\n\nfunction automationStatusVariantr(status: string) {\n switch (status) {\n case 'ACTIVE':\n return 'success';\n case 'PAUSED':\n return 'warning';\n case 'ERROR':\n return 'destructive';\n case 'DISABLED':\n return 'secondary';\n default:\n return 'default';\n }\n}\n\nfunction formatDate(value: string | null | undefined) {\n if (!value) return '—';\n try {\n return new Date(value).toLocaleDateString();\n } catch {\n return value;\n }\n}\n\nfunction localizeMethodology(value: string) {\n return value.toLowerCase().replace(/_/g, ' ');\n}\n\nfunction localizeTrigger(value: string) {\n return value.toLowerCase().replace(/_/g, ' ');\n}\n\n// ------------------------------------------------------------------------------\n// Sub-components\n// ------------------------------------------------------------------------------\n\nfunction QuotaCard({\n icon: Icon,\n label,\n used,\n limit,\n className,\n}: {\n icon: React.ComponentType<{ className?: string }>;\n label: string;\n used: number;\n limit: number;\n className?: string;\n}) {\n const percentage = Math.min(100, Math.round((used / Math.max(1, limit)) * 100));\n\n return (\n <Card className={className}>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Icon className=\"h-4 w-4\" />\n <span className=\"text-sm\">{label}</span>\n </div>\n <div className=\"mt-2 flex items-baseline gap-2\">\n <span className=\"text-2xl font-semibold\">{used}</span>\n <span className=\"text-sm text-muted-foreground\">/ {limit}</span>\n </div>\n <div className=\"mt-3 h-2 w-full overflow-hidden rounded-full bg-muted\">\n <div\n className={cn(\n 'h-full rounded-full transition-all',\n percentage >= 90 ? 'bg-destructive' : percentage >= 70 ? 'bg-warning' : 'bg-success'\n )}\n style={{ width: `${percentage}%` }}\n />\n </div>\n <p className=\"mt-2 text-xs text-muted-foreground\">{percentage}% used</p>\n </CardContent>\n </Card>\n );\n}\n\nfunction SectionLoader() {\n return (\n <div className=\"space-y-3\">\n <Skeleton className=\"h-8 w-1/3\" />\n <Skeleton className=\"h-32 w-full\" />\n </div>\n );\n}\n\ninterface SimpleTableColumn<T> {\n key: string;\n header: React.ReactNode;\n cell: (row: T) => React.ReactNode;\n className?: string;\n}\n\nfunction SimpleTable<T extends { id: string }>({\n columns,\n data,\n emptyState,\n}: {\n columns: SimpleTableColumn<T>[];\n data: T[];\n emptyState: React.ReactNode;\n}) {\n if (data.length === 0) {\n return <div className=\"py-8 text-center text-sm text-muted-foreground\">{emptyState}</div>;\n }\n\n return (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-muted/50\">\n <tr>\n {columns.map((column) => (\n <th\n key={column.key}\n className={cn(\n 'px-4 py-3 text-left font-medium text-muted-foreground',\n column.className\n )}\n >\n {column.header}\n </th>\n ))}\n </tr>\n </thead>\n <tbody className=\"divide-y\">\n {data.map((row) => (\n <tr key={row.id} className=\"hover:bg-muted/30\">\n {columns.map((column) => (\n <td key={`${row.id}-${column.key}`} className={cn('px-4 py-3', column.className)}>\n {column.cell(row)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\n// ------------------------------------------------------------------------------\n// Main dashboard\n// ------------------------------------------------------------------------------\n\nfunction PlanMagnetAdminDashboard() {\n const { workspaceId } = usePlanMagnet();\n const tr = useTr();\n\n const [selectedProjectIds, setSelectedProjectIds] = useState<Set<string>>(new Set());\n const [archiving, setArchiving] = useState(false);\n const [archiveError, setArchiveError] = useState<string | null>(null);\n\n const projectsQuery = useQuery<{\n planmagnetProjects: { items: AdminProject[]; totalCount: number };\n }>(ADMIN_PROJECTS_QUERY, {\n variables: { workspaceId: workspaceId ?? '' },\n skip: !workspaceId,\n });\n\n const boardsQuery = useQuery<{\n planmagnetBoards: { items: AdminBoard[]; totalCount: number };\n }>(ADMIN_BOARDS_QUERY, {\n variables: { workspaceId: workspaceId ?? '' },\n skip: !workspaceId,\n });\n\n const rulesQuery = useQuery<{\n planmagnetAutomationRules: { items: AdminAutomationRule[]; totalCount: number };\n }>(ADMIN_AUTOMATION_RULES_QUERY, {\n variables: { workspaceId: workspaceId ?? '' },\n skip: !workspaceId,\n });\n\n const [archiveProject] = useMutation(ARCHIVE_PROJECT_MUTATION);\n\n const projects = projectsQuery.data?.planmagnetProjects.items ?? [];\n const boards = boardsQuery.data?.planmagnetBoards.items ?? [];\n const rules = rulesQuery.data?.planmagnetAutomationRules.items ?? [];\n\n const activeProjects = useMemo(\n () => projects.filter((project) => project.status === 'ACTIVE'),\n [projects]\n );\n\n const toggleProjectSelection = (id: string) => {\n setSelectedProjectIds((previous) => {\n const next = new Set(previous);\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n return next;\n });\n };\n\n const toggleAll = () => {\n const selectable = activeProjects.filter((project) => project.status !== 'ARCHIVED');\n if (selectedProjectIds.size === selectable.length) {\n setSelectedProjectIds(new Set());\n } else {\n setSelectedProjectIds(new Set(selectable.map((project) => project.id)));\n }\n };\n\n const handleBulkArchive = async () => {\n if (selectedProjectIds.size === 0) return;\n setArchiving(true);\n setArchiveError(null);\n try {\n await Promise.all(\n Array.from(selectedProjectIds).map((id) => archiveProject({ variables: { id } }))\n );\n await projectsQuery.refetch();\n setSelectedProjectIds(new Set());\n } catch (error) {\n setArchiveError(\n error instanceof Error\n ? error.message\n : tr('planmagnet.admin.archive.unknownError', 'Bulk archive failed')\n );\n } finally {\n setArchiving(false);\n }\n };\n\n if (!workspaceId) {\n return (\n <div className=\"min-h-screen bg-background\">\n <div className=\"flex min-h-[320px] flex-col items-center justify-center gap-3 text-muted-foreground\">\n <AlertCircle className=\"h-8 w-8\" />\n <p>\n {tr(\n 'planmagnet.admin.noWorkspace',\n 'Select a workspace to manage PlanMagnet admin settings.'\n )}\n </p>\n </div>\n </div>\n );\n }\n\n const isLoading = projectsQuery.loading || boardsQuery.loading || rulesQuery.loading;\n\n return (\n <div className=\"min-h-screen bg-background\">\n <div className=\"mx-auto max-w-7xl space-y-6 p-6\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">\n {tr('planmagnet.admin.title', 'PlanMagnet Admin')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'planmagnet.admin.purpose',\n 'Workspace quota overview, bulk project archive, and automation rule audit.'\n )}\n </p>\n </div>\n </div>\n\n {/* Quota overview — counts are real; limits are placeholder until a\n dedicated admin quota query exists in the backend. */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n <QuotaCard\n icon={FolderKanban}\n label={tr('planmagnet.admin.quota.projects', 'Projects')}\n used={projects.length}\n limit={50}\n />\n <QuotaCard\n icon={LayoutTemplate}\n label={tr('planmagnet.admin.quota.boards', 'Boards')}\n used={boards.length}\n limit={100}\n />\n <QuotaCard\n icon={Zap}\n label={tr('planmagnet.admin.quota.automationRules', 'Automation Rules')}\n used={rules.length}\n limit={25}\n />\n </div>\n\n {isLoading ? (\n <SectionLoader />\n ) : (\n <>\n {/* Bulk project archive */}\n <Card>\n <CardHeader className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <CardTitle>\n {tr('planmagnet.admin.archive.title', 'Bulk Project Archive')}\n </CardTitle>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'planmagnet.admin.archive.subtitle',\n 'Select active projects and archive them in one action.'\n )}\n </p>\n </div>\n <Button\n size=\"sm\"\n variant=\"destructive\"\n disabled={selectedProjectIds.size === 0 || archiving}\n onClick={handleBulkArchive}\n >\n {archiving ? (\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n ) : (\n <Archive className=\"mr-2 h-4 w-4\" />\n )}\n {tr('planmagnet.admin.archive.action', 'Archive')} ({selectedProjectIds.size})\n </Button>\n </CardHeader>\n <CardContent>\n {archiveError && (\n <div className=\"mb-4 flex items-center gap-2 rounded-md bg-destructive/10 p-3 text-sm text-destructive\">\n <AlertCircle className=\"h-4 w-4\" />\n {archiveError}\n </div>\n )}\n <div className=\"rounded-md border\">\n <SimpleTable<AdminProject>\n data={activeProjects}\n columns={[\n {\n key: 'select',\n header: (\n <Checkbox\n checked={\n activeProjects.length > 0 &&\n selectedProjectIds.size === activeProjects.length\n }\n onCheckedChange={toggleAll}\n aria-label={tr(\n 'planmagnet.admin.archive.selectAll',\n 'Select all active projects'\n )}\n />\n ),\n cell: (row) => (\n <Checkbox\n checked={selectedProjectIds.has(row.id)}\n onCheckedChange={() => toggleProjectSelection(row.id)}\n aria-label={tr(\n 'planmagnet.admin.archive.selectProject',\n 'Select project'\n )}\n />\n ),\n className: 'w-12',\n },\n {\n key: 'name',\n header: tr('planmagnet.admin.archive.column.name', 'Project'),\n cell: (row) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium\">{row.name}</span>\n <span className=\"text-xs text-muted-foreground font-mono\">\n {row.key}\n </span>\n </div>\n ),\n },\n {\n key: 'status',\n header: tr('planmagnet.admin.archive.column.status', 'Status'),\n cell: (row) => (\n <Badge variant={projectStatusVariantr(row.status)}>\n {row.status.toLowerCase().replace('_', ' ')}\n </Badge>\n ),\n },\n {\n key: 'methodology',\n header: tr('planmagnet.admin.archive.column.methodology', 'Methodology'),\n cell: (row) => (\n <span className=\"text-sm capitalize\">\n {localizeMethodology(row.methodology)}\n </span>\n ),\n },\n {\n key: 'owner',\n header: tr('planmagnet.admin.archive.column.owner', 'Owner'),\n cell: (row) => (\n <span className=\"text-sm text-muted-foreground font-mono\">\n <PersonName id={row.ownerId} />\n </span>\n ),\n },\n {\n key: 'created',\n header: tr('planmagnet.admin.archive.column.created', 'Created'),\n cell: (row) => <span className=\"text-sm\">{formatDate(row.createdAt)}</span>,\n },\n ]}\n emptyState={tr(\n 'planmagnet.admin.archive.empty',\n 'No active projects to archive.'\n )}\n />\n </div>\n </CardContent>\n </Card>\n\n {/* Automation rule audit */}\n <Card>\n <CardHeader>\n <CardTitle>\n {tr('planmagnet.admin.automation.title', 'Automation Rule Audit')}\n </CardTitle>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'planmagnet.admin.automation.subtitle',\n 'Review rule health, execution counts, and last run timestamps.'\n )}\n </p>\n </CardHeader>\n <CardContent>\n <div className=\"rounded-md border\">\n <SimpleTable<AdminAutomationRule>\n data={rules}\n columns={[\n {\n key: 'name',\n header: tr('planmagnet.admin.automation.column.name', 'Rule'),\n cell: (row) => <span className=\"font-medium\">{row.name}</span>,\n },\n {\n key: 'trigger',\n header: tr('planmagnet.admin.automation.column.trigger', 'Trigger'),\n cell: (row) => (\n <span className=\"text-sm\">{localizeTrigger(row.triggerType)}</span>\n ),\n },\n {\n key: 'status',\n header: tr('planmagnet.admin.automation.column.status', 'Status'),\n cell: (row) => (\n <Badge variant={automationStatusVariantr(row.status)}>\n {row.status.toLowerCase()}\n </Badge>\n ),\n },\n {\n key: 'executions',\n header: tr('planmagnet.admin.automation.column.executions', 'Executions'),\n cell: (row) => <span className=\"text-sm\">{row.executionCount}</span>,\n },\n {\n key: 'lastRun',\n header: tr('planmagnet.admin.automation.column.lastRun', 'Last Run'),\n cell: (row) => (\n <span className=\"text-sm\">{formatDate(row.lastExecutedAt)}</span>\n ),\n },\n {\n key: 'created',\n header: tr('planmagnet.admin.automation.column.created', 'Created'),\n cell: (row) => <span className=\"text-sm\">{formatDate(row.createdAt)}</span>,\n },\n ]}\n emptyState={tr(\n 'planmagnet.admin.automation.empty',\n 'No automation rules found.'\n )}\n />\n </div>\n </CardContent>\n </Card>\n </>\n )}\n </div>\n </div>\n );\n}\n\n// ------------------------------------------------------------------------------\n// Public root export consumed by the admin app shell\n// ------------------------------------------------------------------------------\n\nexport function PlanMagnetAdminRoot(props: PlanMagnetRootProps) {\n return (\n <PlanMagnetProvider {...props}>\n <PlanMagnetAdminDashboard />\n </PlanMagnetProvider>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardDashboardPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/DashboardDashboardPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DashboardDashboardPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/DashboardDashboardPagePage.tsx"],"names":[],"mappings":"AAwFA,wBAAgB,0BAA0B,4CAyVzC"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
2
|
+
import { PersonName as t } from "../people/People.js";
|
|
3
|
+
import { AlertTriangle as n, ArrowUpRight as r, Calendar as i, CheckSquare as a, FolderKanban as o, LayoutDashboard as s, TrendingUp as c, Users as l, Zap as u } from "lucide-react";
|
|
4
|
+
import { gql as d } from "@apollo/client";
|
|
5
|
+
import { useQuery as f } from "@apollo/client/react";
|
|
6
|
+
import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
7
|
+
import { useI18n as g } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
8
|
+
import { Badge as _, Card as v, CardContent as y, CardHeader as b, CardTitle as x } from "@burdenoff/fe-libs/ui";
|
|
8
9
|
//#region src/planmagnet/pages/DashboardDashboardPagePage.tsx
|
|
9
|
-
var
|
|
10
|
+
var S = d`
|
|
10
11
|
query GetDashboardData($workspaceId: ID!) {
|
|
11
12
|
planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 50 }) {
|
|
12
13
|
items {
|
|
@@ -73,126 +74,126 @@ var b = u`
|
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
`;
|
|
76
|
-
function
|
|
77
|
-
let { workspaceId:
|
|
78
|
-
if (
|
|
77
|
+
function C() {
|
|
78
|
+
let { workspaceId: d } = e(), { t: C } = g(), { data: w, loading: T, error: E } = f(S, { variables: { workspaceId: d ?? "default" } });
|
|
79
|
+
if (T) return /* @__PURE__ */ m("div", {
|
|
79
80
|
className: "flex items-center justify-center p-12",
|
|
80
|
-
children: /* @__PURE__ */
|
|
81
|
+
children: /* @__PURE__ */ m("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
|
|
81
82
|
});
|
|
82
|
-
if (
|
|
83
|
+
if (E) return /* @__PURE__ */ h("div", {
|
|
83
84
|
className: "p-6 text-red-500",
|
|
84
85
|
children: [
|
|
85
|
-
|
|
86
|
+
C("planmagnet.dashboard.errorLoading", "Error loading dashboard"),
|
|
86
87
|
": ",
|
|
87
|
-
|
|
88
|
+
E.message
|
|
88
89
|
]
|
|
89
90
|
});
|
|
90
|
-
let
|
|
91
|
-
|
|
92
|
-
let
|
|
91
|
+
let D = w?.planmagnetProjects?.items ?? [], O = w?.planmagnetSprints?.items ?? [], k = w?.planmagnetMilestones?.items ?? [], A = w?.planmagnetWorkItems?.items ?? [], j = D.filter((e) => e.status === "ACTIVE");
|
|
92
|
+
D.filter((e) => e.status === "COMPLETED");
|
|
93
|
+
let M = O.filter((e) => e.status === "ACTIVE"), N = k.filter((e) => e.status === "PENDING" || e.status === "IN_PROGRESS").slice(0, 4), P = A.slice(0, 5), F = new Map(D.map((e) => [e.id, e.name])), I = [
|
|
93
94
|
{
|
|
94
|
-
label:
|
|
95
|
-
value: String(
|
|
96
|
-
trend: `${
|
|
95
|
+
label: C("planmagnet.dashboard.metrics.totalProjects.label", "Total Projects"),
|
|
96
|
+
value: String(D.length),
|
|
97
|
+
trend: `${j.length} ${C("planmagnet.dashboard.metrics.totalProjects.trend", "active")}`
|
|
97
98
|
},
|
|
98
99
|
{
|
|
99
|
-
label:
|
|
100
|
-
value: String(
|
|
101
|
-
trend: `${
|
|
100
|
+
label: C("planmagnet.dashboard.metrics.activeSprints.label", "Active Sprints"),
|
|
101
|
+
value: String(M.length),
|
|
102
|
+
trend: `${O.length} ${C("planmagnet.dashboard.metrics.activeSprints.trend", "total")}`
|
|
102
103
|
},
|
|
103
104
|
{
|
|
104
|
-
label:
|
|
105
|
-
value: String(
|
|
106
|
-
trend:
|
|
105
|
+
label: C("planmagnet.dashboard.metrics.workItems.label", "Work Items"),
|
|
106
|
+
value: String(w?.planmagnetWorkItems?.totalCount ?? 0),
|
|
107
|
+
trend: C("planmagnet.dashboard.metrics.workItems.trend", "across all projects")
|
|
107
108
|
},
|
|
108
109
|
{
|
|
109
|
-
label:
|
|
110
|
-
value: String(
|
|
111
|
-
trend: `${
|
|
110
|
+
label: C("planmagnet.dashboard.metrics.milestones.label", "Milestones"),
|
|
111
|
+
value: String(k.length),
|
|
112
|
+
trend: `${N.length} ${C("planmagnet.dashboard.metrics.milestones.trend", "upcoming")}`
|
|
112
113
|
}
|
|
113
114
|
];
|
|
114
|
-
return /* @__PURE__ */
|
|
115
|
+
return /* @__PURE__ */ h("div", {
|
|
115
116
|
className: "space-y-6 p-6",
|
|
116
117
|
children: [
|
|
117
|
-
/* @__PURE__ */
|
|
118
|
+
/* @__PURE__ */ m("div", {
|
|
118
119
|
className: "flex items-center justify-between",
|
|
119
|
-
children: /* @__PURE__ */
|
|
120
|
+
children: /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ h("h1", {
|
|
120
121
|
className: "text-2xl font-semibold flex items-center gap-2",
|
|
121
|
-
children: [/* @__PURE__ */
|
|
122
|
-
}), /* @__PURE__ */
|
|
122
|
+
children: [/* @__PURE__ */ m(s, { className: "h-6 w-6" }), C("planmagnet.dashboard.title", "Dashboard")]
|
|
123
|
+
}), /* @__PURE__ */ m("p", {
|
|
123
124
|
className: "text-sm text-muted-foreground mt-1",
|
|
124
|
-
children:
|
|
125
|
+
children: C("planmagnet.dashboard.subtitle", "Welcome back! Here is your project overview.")
|
|
125
126
|
})] })
|
|
126
127
|
}),
|
|
127
|
-
/* @__PURE__ */
|
|
128
|
+
/* @__PURE__ */ m("div", {
|
|
128
129
|
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-4",
|
|
129
|
-
children:
|
|
130
|
+
children: I.map((e) => /* @__PURE__ */ h(v, { children: [/* @__PURE__ */ h(b, {
|
|
130
131
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
131
|
-
children: [/* @__PURE__ */
|
|
132
|
+
children: [/* @__PURE__ */ m(x, {
|
|
132
133
|
className: "text-sm font-medium",
|
|
133
134
|
children: e.label
|
|
134
|
-
}), /* @__PURE__ */
|
|
135
|
-
}), /* @__PURE__ */
|
|
135
|
+
}), /* @__PURE__ */ m(c, { className: "h-4 w-4 text-muted-foreground" })]
|
|
136
|
+
}), /* @__PURE__ */ h(y, { children: [/* @__PURE__ */ m("div", {
|
|
136
137
|
className: "text-2xl font-bold",
|
|
137
138
|
children: e.value
|
|
138
|
-
}), /* @__PURE__ */
|
|
139
|
+
}), /* @__PURE__ */ m("p", {
|
|
139
140
|
className: "text-xs text-muted-foreground",
|
|
140
141
|
children: e.trend
|
|
141
142
|
})] })] }, e.label))
|
|
142
143
|
}),
|
|
143
|
-
/* @__PURE__ */
|
|
144
|
+
/* @__PURE__ */ h("div", {
|
|
144
145
|
className: "grid gap-6 lg:grid-cols-12",
|
|
145
|
-
children: [/* @__PURE__ */
|
|
146
|
+
children: [/* @__PURE__ */ h(v, {
|
|
146
147
|
className: "lg:col-span-8",
|
|
147
|
-
children: [/* @__PURE__ */
|
|
148
|
+
children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ h(x, {
|
|
148
149
|
className: "flex items-center gap-2 text-base",
|
|
149
|
-
children: [/* @__PURE__ */
|
|
150
|
-
}) }), /* @__PURE__ */
|
|
150
|
+
children: [/* @__PURE__ */ m(u, { className: "h-5 w-5 text-blue-500" }), C("planmagnet.dashboard.activeSprints.title", "Active Sprints")]
|
|
151
|
+
}) }), /* @__PURE__ */ h(y, {
|
|
151
152
|
className: "space-y-4",
|
|
152
|
-
children: [
|
|
153
|
+
children: [M.map((e) => {
|
|
153
154
|
let t = e.workItems.reduce((e, t) => e + (t.storyPoints ?? 0), 0), n = e.workItems.filter((e) => e.status === "DONE").reduce((e, t) => e + (t.storyPoints ?? 0), 0), r = e.workItems.length, i = e.workItems.filter((e) => e.status === "DONE").length, a = t > 0 ? Math.round(n / t * 100) : 0;
|
|
154
|
-
return /* @__PURE__ */
|
|
155
|
+
return /* @__PURE__ */ h("div", {
|
|
155
156
|
className: "rounded-lg border p-4",
|
|
156
157
|
children: [
|
|
157
|
-
/* @__PURE__ */
|
|
158
|
+
/* @__PURE__ */ h("div", {
|
|
158
159
|
className: "flex items-center justify-between mb-2",
|
|
159
|
-
children: [/* @__PURE__ */
|
|
160
|
+
children: [/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
|
|
160
161
|
className: "font-medium",
|
|
161
162
|
children: e.name
|
|
162
|
-
}), /* @__PURE__ */
|
|
163
|
+
}), /* @__PURE__ */ h("p", {
|
|
163
164
|
className: "text-xs text-muted-foreground",
|
|
164
165
|
children: [
|
|
165
|
-
|
|
166
|
+
C("planmagnet.dashboard.shared.project", "Project"),
|
|
166
167
|
": ",
|
|
167
168
|
e.projectId
|
|
168
169
|
]
|
|
169
|
-
})] }), /* @__PURE__ */
|
|
170
|
+
})] }), /* @__PURE__ */ h(_, {
|
|
170
171
|
variant: "secondary",
|
|
171
172
|
children: [
|
|
172
173
|
n,
|
|
173
174
|
"/",
|
|
174
175
|
t,
|
|
175
176
|
" ",
|
|
176
|
-
|
|
177
|
+
C("planmagnet.dashboard.shared.pointsShort", "pts")
|
|
177
178
|
]
|
|
178
179
|
})]
|
|
179
180
|
}),
|
|
180
|
-
/* @__PURE__ */
|
|
181
|
+
/* @__PURE__ */ m("div", {
|
|
181
182
|
className: "w-full h-2 bg-muted rounded-full overflow-hidden",
|
|
182
|
-
children: /* @__PURE__ */
|
|
183
|
+
children: /* @__PURE__ */ m("div", {
|
|
183
184
|
className: "h-full bg-primary rounded-full transition-all",
|
|
184
185
|
style: { width: `${a}%` }
|
|
185
186
|
})
|
|
186
187
|
}),
|
|
187
|
-
/* @__PURE__ */
|
|
188
|
+
/* @__PURE__ */ h("div", {
|
|
188
189
|
className: "flex items-center justify-between mt-2 text-xs text-muted-foreground",
|
|
189
|
-
children: [/* @__PURE__ */
|
|
190
|
+
children: [/* @__PURE__ */ h("span", { children: [
|
|
190
191
|
i,
|
|
191
192
|
"/",
|
|
192
193
|
r,
|
|
193
194
|
" ",
|
|
194
|
-
|
|
195
|
-
] }), /* @__PURE__ */
|
|
195
|
+
C("planmagnet.dashboard.shared.tasks", "tasks")
|
|
196
|
+
] }), /* @__PURE__ */ h("span", { children: [
|
|
196
197
|
e.startDate ? new Date(e.startDate).toLocaleDateString() : "—",
|
|
197
198
|
" -",
|
|
198
199
|
" ",
|
|
@@ -201,143 +202,139 @@ function x() {
|
|
|
201
202
|
})
|
|
202
203
|
]
|
|
203
204
|
}, e.id);
|
|
204
|
-
}),
|
|
205
|
+
}), M.length === 0 && /* @__PURE__ */ m("p", {
|
|
205
206
|
className: "text-sm text-muted-foreground text-center py-4",
|
|
206
|
-
children:
|
|
207
|
+
children: C("planmagnet.dashboard.activeSprints.empty", "No active sprints.")
|
|
207
208
|
})]
|
|
208
209
|
})]
|
|
209
|
-
}), /* @__PURE__ */
|
|
210
|
+
}), /* @__PURE__ */ h("div", {
|
|
210
211
|
className: "lg:col-span-4 space-y-6",
|
|
211
|
-
children: [
|
|
212
|
+
children: [D.filter((e) => e.status === "ON_HOLD").length > 0 && /* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ h(x, {
|
|
212
213
|
className: "flex items-center gap-2 text-base",
|
|
213
|
-
children: [/* @__PURE__ */
|
|
214
|
-
}) }), /* @__PURE__ */
|
|
214
|
+
children: [/* @__PURE__ */ m(n, { className: "h-5 w-5 text-orange-500" }), C("planmagnet.dashboard.onHoldProjects.title", "On-Hold Projects")]
|
|
215
|
+
}) }), /* @__PURE__ */ m(y, {
|
|
215
216
|
className: "space-y-3",
|
|
216
|
-
children:
|
|
217
|
+
children: D.filter((e) => e.status === "ON_HOLD").map((e) => /* @__PURE__ */ h("div", {
|
|
217
218
|
className: "flex items-center justify-between rounded border p-2",
|
|
218
|
-
children: [/* @__PURE__ */
|
|
219
|
+
children: [/* @__PURE__ */ m("div", { children: /* @__PURE__ */ m("p", {
|
|
219
220
|
className: "text-sm font-medium",
|
|
220
221
|
children: e.name
|
|
221
|
-
}) }), /* @__PURE__ */
|
|
222
|
+
}) }), /* @__PURE__ */ m(_, {
|
|
222
223
|
variant: "destructive",
|
|
223
224
|
children: e.status.toLowerCase().replace("_", " ")
|
|
224
225
|
})]
|
|
225
226
|
}, e.id))
|
|
226
|
-
})] }), /* @__PURE__ */
|
|
227
|
+
})] }), /* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ h(x, {
|
|
227
228
|
className: "flex items-center gap-2 text-base",
|
|
228
|
-
children: [/* @__PURE__ */
|
|
229
|
-
}) }), /* @__PURE__ */
|
|
229
|
+
children: [/* @__PURE__ */ m(i, { className: "h-5 w-5 text-green-500" }), C("planmagnet.dashboard.upcomingMilestones.title", "Upcoming Milestones")]
|
|
230
|
+
}) }), /* @__PURE__ */ h(y, {
|
|
230
231
|
className: "space-y-3",
|
|
231
|
-
children: [
|
|
232
|
+
children: [N.map((e) => /* @__PURE__ */ h("div", {
|
|
232
233
|
className: "flex items-start gap-3 rounded border p-2",
|
|
233
|
-
children: [/* @__PURE__ */
|
|
234
|
+
children: [/* @__PURE__ */ h("div", {
|
|
234
235
|
className: "flex-1 min-w-0",
|
|
235
236
|
children: [
|
|
236
|
-
/* @__PURE__ */
|
|
237
|
+
/* @__PURE__ */ m("p", {
|
|
237
238
|
className: "text-sm font-medium truncate",
|
|
238
239
|
children: e.name
|
|
239
240
|
}),
|
|
240
|
-
/* @__PURE__ */
|
|
241
|
+
/* @__PURE__ */ h("p", {
|
|
241
242
|
className: "text-xs text-muted-foreground",
|
|
242
243
|
children: [
|
|
243
|
-
|
|
244
|
+
C("planmagnet.dashboard.shared.project", "Project"),
|
|
244
245
|
": ",
|
|
245
246
|
e.projectId
|
|
246
247
|
]
|
|
247
248
|
}),
|
|
248
|
-
/* @__PURE__ */
|
|
249
|
+
/* @__PURE__ */ h("p", {
|
|
249
250
|
className: "text-xs text-muted-foreground",
|
|
250
251
|
children: [
|
|
251
|
-
|
|
252
|
+
C("planmagnet.dashboard.shared.due", "Due"),
|
|
252
253
|
" ",
|
|
253
254
|
e.dueDate ? new Date(e.dueDate).toLocaleDateString() : "—"
|
|
254
255
|
]
|
|
255
256
|
})
|
|
256
257
|
]
|
|
257
|
-
}), /* @__PURE__ */
|
|
258
|
+
}), /* @__PURE__ */ m(_, {
|
|
258
259
|
variant: "secondary",
|
|
259
260
|
className: "text-xs flex-shrink-0 capitalize",
|
|
260
261
|
children: e.status.toLowerCase().replace("_", " ")
|
|
261
262
|
})]
|
|
262
|
-
}, e.id)),
|
|
263
|
+
}, e.id)), N.length === 0 && /* @__PURE__ */ m("p", {
|
|
263
264
|
className: "text-sm text-muted-foreground text-center py-2",
|
|
264
|
-
children:
|
|
265
|
+
children: C("planmagnet.dashboard.upcomingMilestones.empty", "No upcoming milestones.")
|
|
265
266
|
})]
|
|
266
267
|
})] })]
|
|
267
268
|
})]
|
|
268
269
|
}),
|
|
269
|
-
/* @__PURE__ */
|
|
270
|
+
/* @__PURE__ */ h("div", {
|
|
270
271
|
className: "grid gap-6 lg:grid-cols-2",
|
|
271
|
-
children: [/* @__PURE__ */
|
|
272
|
+
children: [/* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ h(x, {
|
|
272
273
|
className: "flex items-center gap-2 text-base",
|
|
273
|
-
children: [/* @__PURE__ */
|
|
274
|
-
}) }), /* @__PURE__ */
|
|
274
|
+
children: [/* @__PURE__ */ m(a, { className: "h-5 w-5 text-purple-500" }), C("planmagnet.dashboard.recentWorkItems.title", "Recent Work Items")]
|
|
275
|
+
}) }), /* @__PURE__ */ h(y, {
|
|
275
276
|
className: "space-y-3",
|
|
276
|
-
children: [
|
|
277
|
+
children: [P.map((e) => /* @__PURE__ */ h("div", {
|
|
277
278
|
className: "flex items-center justify-between rounded border p-2",
|
|
278
|
-
children: [/* @__PURE__ */
|
|
279
|
+
children: [/* @__PURE__ */ h("div", {
|
|
279
280
|
className: "min-w-0 flex-1",
|
|
280
|
-
children: [/* @__PURE__ */
|
|
281
|
+
children: [/* @__PURE__ */ m("p", {
|
|
281
282
|
className: "text-sm font-medium truncate",
|
|
282
283
|
children: e.key
|
|
283
|
-
}), /* @__PURE__ */
|
|
284
|
+
}), /* @__PURE__ */ h("p", {
|
|
284
285
|
className: "text-xs text-muted-foreground",
|
|
285
|
-
children: [
|
|
286
|
-
e.projectId,
|
|
287
|
-
" ",
|
|
288
|
-
e.assigneeId ? `\u00b7 ${e.assigneeId}` : ""
|
|
289
|
-
]
|
|
286
|
+
children: [F.get(e.projectId) ?? `Project ${e.projectId.slice(0, 6)}`, e.assigneeId ? /* @__PURE__ */ h(p, { children: [" · ", /* @__PURE__ */ m(t, { id: e.assigneeId })] }) : null]
|
|
290
287
|
})]
|
|
291
|
-
}), /* @__PURE__ */
|
|
288
|
+
}), /* @__PURE__ */ h("div", {
|
|
292
289
|
className: "flex items-center gap-2 flex-shrink-0",
|
|
293
|
-
children: [/* @__PURE__ */
|
|
290
|
+
children: [/* @__PURE__ */ h(_, {
|
|
294
291
|
variant: "outline",
|
|
295
292
|
className: "text-xs",
|
|
296
293
|
children: [
|
|
297
294
|
e.storyPoints ?? 0,
|
|
298
295
|
" ",
|
|
299
|
-
|
|
296
|
+
C("planmagnet.dashboard.shared.pointsShort", "pts")
|
|
300
297
|
]
|
|
301
|
-
}), /* @__PURE__ */
|
|
298
|
+
}), /* @__PURE__ */ m(_, {
|
|
302
299
|
variant: "secondary",
|
|
303
300
|
className: "text-xs capitalize",
|
|
304
301
|
children: e.status.toLowerCase().replace("_", " ")
|
|
305
302
|
})]
|
|
306
303
|
})]
|
|
307
|
-
}, e.id)),
|
|
304
|
+
}, e.id)), P.length === 0 && /* @__PURE__ */ m("p", {
|
|
308
305
|
className: "text-sm text-muted-foreground text-center py-4",
|
|
309
|
-
children:
|
|
306
|
+
children: C("planmagnet.dashboard.recentWorkItems.empty", "No work items yet.")
|
|
310
307
|
})]
|
|
311
|
-
})] }), /* @__PURE__ */
|
|
308
|
+
})] }), /* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ h(x, {
|
|
312
309
|
className: "flex items-center gap-2 text-base",
|
|
313
|
-
children: [/* @__PURE__ */
|
|
314
|
-
}) }), /* @__PURE__ */
|
|
310
|
+
children: [/* @__PURE__ */ m(o, { className: "h-5 w-5 text-cyan-500" }), C("planmagnet.dashboard.projectStatus.title", "Project Status")]
|
|
311
|
+
}) }), /* @__PURE__ */ h(y, {
|
|
315
312
|
className: "space-y-3",
|
|
316
|
-
children: [
|
|
313
|
+
children: [j.map((e) => /* @__PURE__ */ h("div", {
|
|
317
314
|
className: "flex items-center justify-between rounded border p-2",
|
|
318
|
-
children: [/* @__PURE__ */
|
|
315
|
+
children: [/* @__PURE__ */ h("div", {
|
|
319
316
|
className: "min-w-0 flex-1",
|
|
320
|
-
children: [/* @__PURE__ */
|
|
317
|
+
children: [/* @__PURE__ */ m("p", {
|
|
321
318
|
className: "text-sm font-medium truncate",
|
|
322
319
|
children: e.name
|
|
323
|
-
}), /* @__PURE__ */
|
|
320
|
+
}), /* @__PURE__ */ m("p", {
|
|
324
321
|
className: "text-xs text-muted-foreground capitalize",
|
|
325
322
|
children: e.status.toLowerCase().replace("_", " ")
|
|
326
323
|
})]
|
|
327
|
-
}), /* @__PURE__ */
|
|
324
|
+
}), /* @__PURE__ */ h("div", {
|
|
328
325
|
className: "flex items-center gap-2 ml-3 flex-shrink-0",
|
|
329
326
|
children: [
|
|
330
|
-
/* @__PURE__ */
|
|
331
|
-
/* @__PURE__ */
|
|
327
|
+
/* @__PURE__ */ m(l, { className: "h-3 w-3 text-muted-foreground" }),
|
|
328
|
+
/* @__PURE__ */ m("span", {
|
|
332
329
|
className: "text-xs text-muted-foreground",
|
|
333
|
-
children: e.ownerId
|
|
330
|
+
children: /* @__PURE__ */ m(t, { id: e.ownerId })
|
|
334
331
|
}),
|
|
335
|
-
/* @__PURE__ */
|
|
332
|
+
/* @__PURE__ */ m(r, { className: "h-3 w-3 text-muted-foreground" })
|
|
336
333
|
]
|
|
337
334
|
})]
|
|
338
|
-
}, e.id)),
|
|
335
|
+
}, e.id)), j.length === 0 && /* @__PURE__ */ m("p", {
|
|
339
336
|
className: "text-sm text-muted-foreground text-center py-4",
|
|
340
|
-
children:
|
|
337
|
+
children: C("planmagnet.dashboard.projectStatus.empty", "No active projects.")
|
|
341
338
|
})]
|
|
342
339
|
})] })]
|
|
343
340
|
})
|
|
@@ -345,6 +342,6 @@ function x() {
|
|
|
345
342
|
});
|
|
346
343
|
}
|
|
347
344
|
//#endregion
|
|
348
|
-
export {
|
|
345
|
+
export { C as DashboardDashboardPagePage };
|
|
349
346
|
|
|
350
347
|
//# sourceMappingURL=DashboardDashboardPagePage.js.map
|