@burdenoff/microfe-planmagnet 2026.927.4 → 2026.927.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/generated/global-types.d.ts +9 -1
  2. package/dist/generated/global-types.d.ts.map +1 -1
  3. package/dist/generated/wspace-operations.d.ts +0 -12
  4. package/dist/generated/wspace-operations.d.ts.map +1 -1
  5. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  6. package/dist/planmagnet/PlanMagnetRoutes.js +14 -10
  7. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  8. package/dist/planmagnet/admin/PlanMagnetAdminRoot.d.ts.map +1 -1
  9. package/dist/planmagnet/admin/PlanMagnetAdminRoot.js +149 -127
  10. package/dist/planmagnet/admin/PlanMagnetAdminRoot.js.map +1 -1
  11. package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
  12. package/dist/planmagnet/controls/FeatureControls.js +142 -139
  13. package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
  14. package/dist/planmagnet/controls/FeatureTriage.d.ts +21 -0
  15. package/dist/planmagnet/controls/FeatureTriage.d.ts.map +1 -0
  16. package/dist/planmagnet/controls/FeatureTriage.js +398 -0
  17. package/dist/planmagnet/controls/FeatureTriage.js.map +1 -0
  18. package/dist/planmagnet/controls/MilestoneControls.d.ts.map +1 -1
  19. package/dist/planmagnet/controls/MilestoneControls.js +76 -73
  20. package/dist/planmagnet/controls/MilestoneControls.js.map +1 -1
  21. package/dist/planmagnet/controls/ProjectControls.d.ts.map +1 -1
  22. package/dist/planmagnet/controls/ProjectControls.js +47 -44
  23. package/dist/planmagnet/controls/ProjectControls.js.map +1 -1
  24. package/dist/planmagnet/controls/ReleaseControls.d.ts.map +1 -1
  25. package/dist/planmagnet/controls/ReleaseControls.js +47 -44
  26. package/dist/planmagnet/controls/ReleaseControls.js.map +1 -1
  27. package/dist/planmagnet/controls/WorkItemControls.d.ts.map +1 -1
  28. package/dist/planmagnet/controls/WorkItemControls.js +68 -65
  29. package/dist/planmagnet/controls/WorkItemControls.js.map +1 -1
  30. package/dist/planmagnet/hooks/useCounts.d.ts +39 -0
  31. package/dist/planmagnet/hooks/useCounts.d.ts.map +1 -0
  32. package/dist/planmagnet/hooks/useCounts.js +209 -0
  33. package/dist/planmagnet/hooks/useCounts.js.map +1 -0
  34. package/dist/planmagnet/import/exportColumns.d.ts +52 -0
  35. package/dist/planmagnet/import/exportColumns.d.ts.map +1 -0
  36. package/dist/planmagnet/import/exportColumns.js +81 -0
  37. package/dist/planmagnet/import/exportColumns.js.map +1 -0
  38. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
  39. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +255 -242
  40. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
  41. package/dist/planmagnet/pages/DashboardDashboardPagePage.d.ts.map +1 -1
  42. package/dist/planmagnet/pages/DashboardDashboardPagePage.js +199 -160
  43. package/dist/planmagnet/pages/DashboardDashboardPagePage.js.map +1 -1
  44. package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
  45. package/dist/planmagnet/pages/DocsDocDetailPagePage.js +147 -144
  46. package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
  47. package/dist/planmagnet/pages/GoalsGoalsPagePage.d.ts.map +1 -1
  48. package/dist/planmagnet/pages/GoalsGoalsPagePage.js +2 -7
  49. package/dist/planmagnet/pages/GoalsGoalsPagePage.js.map +1 -1
  50. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
  51. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +173 -173
  52. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
  53. package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
  54. package/dist/planmagnet/pages/ProductsEditProductPagePage.js +120 -117
  55. package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
  56. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.d.ts.map +1 -1
  57. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js +97 -117
  58. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js.map +1 -1
  59. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
  60. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +168 -103
  61. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
  62. package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
  63. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +117 -103
  64. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
  65. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  66. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +4 -8
  67. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  68. package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
  69. package/dist/planmagnet/pages/ProductsProductsPagePage.js +85 -90
  70. package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
  71. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
  72. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +12 -2
  73. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
  74. package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -1
  75. package/dist/planmagnet/pages/ProductsReleasesPagePage.js +2 -2
  76. package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
  77. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
  78. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +48 -71
  79. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
  80. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
  81. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +17 -13
  82. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
  83. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.d.ts.map +1 -1
  84. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js +1 -1
  85. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js.map +1 -1
  86. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +6 -6
  87. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  88. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +11 -8
  89. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  90. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +4 -4
  91. package/dist/planmagnet/pages/ProjectsPlanningPagePage.d.ts.map +1 -1
  92. package/dist/planmagnet/pages/ProjectsPlanningPagePage.js +1 -1
  93. package/dist/planmagnet/pages/ProjectsPlanningPagePage.js.map +1 -1
  94. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
  95. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +152 -139
  96. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
  97. package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
  98. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +75 -74
  99. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
  100. package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts +7 -0
  101. package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts.map +1 -1
  102. package/dist/planmagnet/pages/ReportsReportsPagePage.js +39 -201
  103. package/dist/planmagnet/pages/ReportsReportsPagePage.js.map +1 -1
  104. package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
  105. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +118 -99
  106. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
  107. package/dist/planmagnet/pages/TasksImportPagePage.d.ts +2 -0
  108. package/dist/planmagnet/pages/TasksImportPagePage.d.ts.map +1 -0
  109. package/dist/planmagnet/pages/TasksImportPagePage.js +309 -0
  110. package/dist/planmagnet/pages/TasksImportPagePage.js.map +1 -0
  111. package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
  112. package/dist/planmagnet/pages/TasksTasksPagePage.js +110 -104
  113. package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
  114. package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
  115. package/dist/planmagnet/pages/TeamTeamPagePage.js +99 -177
  116. package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
  117. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.d.ts.map +1 -1
  118. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +8 -4
  119. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js.map +1 -1
  120. package/dist/planmagnet/releases/ReleaseWorkItemsCard.d.ts.map +1 -1
  121. package/dist/planmagnet/releases/ReleaseWorkItemsCard.js +10 -4
  122. package/dist/planmagnet/releases/ReleaseWorkItemsCard.js.map +1 -1
  123. package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -1
  124. package/dist/planmagnet/roadmaps/RoadmapControls.js +151 -138
  125. package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -1
  126. package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
  127. package/dist/planmagnet/sprints/SprintControls.js +32 -30
  128. package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
  129. package/dist/planmagnet/utils/mutationResult.d.ts +10 -0
  130. package/dist/planmagnet/utils/mutationResult.d.ts.map +1 -1
  131. package/dist/planmagnet/utils/mutationResult.js +2 -1
  132. package/dist/planmagnet/utils/mutationResult.js.map +1 -1
  133. package/dist/planmagnet/utils/rowNavigation.d.ts +2 -0
  134. package/dist/planmagnet/utils/rowNavigation.d.ts.map +1 -1
  135. package/dist/planmagnet/utils/rowNavigation.js +9 -8
  136. package/dist/planmagnet/utils/rowNavigation.js.map +1 -1
  137. 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 { throwOnError } from '../utils/mutationResult';\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 AdminArchiveProject($id: ID!) {\n planmagnetUpdateProject(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 // Every project that is not archived yet (BOFF-7940). Only ACTIVE ones were offered, so completed projects — the\n // natural candidates — and anything left in PLANNING or ON_HOLD could never be archived here.\n const archivableProjects = useMemo(\n () => projects.filter((project) => project.status !== 'ARCHIVED'),\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 if (selectedProjectIds.size === archivableProjects.length) {\n setSelectedProjectIds(new Set());\n } else {\n setSelectedProjectIds(new Set(archivableProjects.map((project) => project.id)));\n }\n };\n\n const handleBulkArchive = async () => {\n if (selectedProjectIds.size === 0) return;\n setArchiving(true);\n setArchiveError(null);\n // Each archive is checked on its own (BOFF-7958): a refused mutation RESOLVES with { error } (errorPolicy 'all'),\n // so Promise.all over the raw calls reported success for archives the service refused. Failures stay selected\n // and are named, with the reason.\n const ids = Array.from(selectedProjectIds);\n try {\n const outcomes = await Promise.allSettled(\n ids.map((id) => throwOnError(archiveProject({ variables: { id } })))\n );\n const failed = ids.filter((_, index) => outcomes[index]!.status === 'rejected');\n await projectsQuery.refetch();\n setSelectedProjectIds(new Set(failed));\n if (failed.length > 0) {\n const first = outcomes.find((o) => o.status === 'rejected') as PromiseRejectedResult;\n const reason = first.reason instanceof Error ? first.reason.message : String(first.reason);\n const names = failed.map((id) => projects.find((p) => p.id === id)?.name ?? id).join(', ');\n setArchiveError(\n `${failed.length} of ${ids.length} could not be archived (${names}): ${reason}`\n );\n }\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 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={archivableProjects}\n columns={[\n {\n key: 'select',\n header: (\n <Checkbox\n checked={\n archivableProjects.length > 0 &&\n selectedProjectIds.size === archivableProjects.length\n }\n onCheckedChange={toggleAll}\n aria-label={tr(\n 'planmagnet.admin.archive.selectAll',\n 'Select all 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
+ {"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 {\n AlertCircle,\n Archive,\n FolderKanban,\n LayoutTemplate,\n Loader2,\n Users,\n Zap,\n} from 'lucide-react';\n\nimport { PlanMagnetProvider, usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { cn } from '../utils/cn';\nimport { throwOnError } from '../utils/mutationResult';\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\n// The real quota: active counts and plan limits from the service (BOFF-7968 — the cards showed the length of one\n// page against hardcoded limits of 50/100/25, counting archived projects and paused rules).\nconst ADMIN_QUOTA_QUERY = gql`\n query AdminPlanmagnetQuota($workspaceId: ID!) {\n planmagnetWorkspaceQuotaOverview(workspaceId: $workspaceId) {\n projectCount\n projectLimit\n boardCount\n boardLimit\n automationRuleCount\n editorCount\n editorLimit\n }\n }\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 AdminArchiveProject($id: ID!) {\n planmagnetUpdateProject(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 /** undefined while loading or when the quota could not be read. */\n used: number | undefined;\n /** null = no limit on this plan. */\n limit: number | null | undefined;\n className?: string;\n}) {\n const percentage =\n used !== undefined && limit != null\n ? Math.min(100, Math.round((used / Math.max(1, limit)) * 100))\n : null;\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\">\n {limit === null ? 'No limit' : limit === undefined ? '' : `/ ${limit}`}\n </span>\n </div>\n {percentage !== null && (\n <>\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\n ? 'bg-destructive'\n : percentage >= 70\n ? 'bg-warning'\n : 'bg-success'\n )}\n style={{ width: `${percentage}%` }}\n />\n </div>\n <p className=\"mt-2 text-xs text-muted-foreground\">{percentage}% used</p>\n </>\n )}\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 quotaQuery = useQuery<{\n planmagnetWorkspaceQuotaOverview: {\n projectCount: number;\n projectLimit: number | null;\n boardCount: number;\n boardLimit: number | null;\n automationRuleCount: number;\n editorCount: number;\n editorLimit: number | null;\n };\n }>(ADMIN_QUOTA_QUERY, { variables: { workspaceId: workspaceId ?? '' }, skip: !workspaceId });\n const quota = quotaQuery.data?.planmagnetWorkspaceQuotaOverview;\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 // Every project that is not archived yet (BOFF-7940). Only ACTIVE ones were offered, so completed projects — the\n // natural candidates — and anything left in PLANNING or ON_HOLD could never be archived here.\n const archivableProjects = useMemo(\n () => projects.filter((project) => project.status !== 'ARCHIVED'),\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 if (selectedProjectIds.size === archivableProjects.length) {\n setSelectedProjectIds(new Set());\n } else {\n setSelectedProjectIds(new Set(archivableProjects.map((project) => project.id)));\n }\n };\n\n const handleBulkArchive = async () => {\n if (selectedProjectIds.size === 0) return;\n setArchiving(true);\n setArchiveError(null);\n // Each archive is checked on its own (BOFF-7958): a refused mutation RESOLVES with { error } (errorPolicy 'all'),\n // so Promise.all over the raw calls reported success for archives the service refused. Failures stay selected\n // and are named, with the reason.\n const ids = Array.from(selectedProjectIds);\n try {\n const outcomes = await Promise.allSettled(\n ids.map((id) => throwOnError(archiveProject({ variables: { id } })))\n );\n const failed = ids.filter((_, index) => outcomes[index]!.status === 'rejected');\n await projectsQuery.refetch();\n setSelectedProjectIds(new Set(failed));\n if (failed.length > 0) {\n const first = outcomes.find((o) => o.status === 'rejected') as PromiseRejectedResult;\n const reason = first.reason instanceof Error ? first.reason.message : String(first.reason);\n const names = failed.map((id) => projects.find((p) => p.id === id)?.name ?? id).join(', ');\n setArchiveError(\n `${failed.length} of ${ids.length} could not be archived (${names}): ${reason}`\n );\n }\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 from planmagnetWorkspaceQuotaOverview: active counts against the plan's limits. */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <QuotaCard\n icon={FolderKanban}\n label={tr('planmagnet.admin.quota.projects', 'Projects')}\n used={quota?.projectCount}\n limit={quota?.projectLimit}\n />\n <QuotaCard\n icon={LayoutTemplate}\n label={tr('planmagnet.admin.quota.boards', 'Boards')}\n used={quota?.boardCount}\n limit={quota?.boardLimit}\n />\n <QuotaCard\n icon={Zap}\n label={tr('planmagnet.admin.quota.automationRules', 'Automation Rules')}\n used={quota?.automationRuleCount}\n limit={null}\n />\n <QuotaCard\n icon={Users}\n label={tr('planmagnet.admin.quota.editors', 'Editors')}\n used={quota?.editorCount}\n limit={quota?.editorLimit}\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 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={archivableProjects}\n columns={[\n {\n key: 'select',\n header: (\n <Checkbox\n checked={\n archivableProjects.length > 0 &&\n selectedProjectIds.size === archivableProjects.length\n }\n onCheckedChange={toggleAll}\n aria-label={tr(\n 'planmagnet.admin.archive.selectAll',\n 'Select all 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":"FeatureControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/controls/FeatureControls.tsx"],"names":[],"mappings":"AAuBA;;;;GAIG;AAEH,eAAO,MAAM,gBAAgB,wGAQnB,CAAC;AA+CX,kDAAkD;AAClD,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,2CA6B5F;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,oEAAoE;AACpE,wBAAgB,oBAAoB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,aAAa,CAAA;CAAE,2CA2E3E"}
1
+ {"version":3,"file":"FeatureControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/controls/FeatureControls.tsx"],"names":[],"mappings":"AAuBA;;;;GAIG;AAEH,eAAO,MAAM,gBAAgB,wGAQnB,CAAC;AA+CX,kDAAkD;AAClD,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,2CA6B5F;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,oEAAoE;AACpE,wBAAgB,oBAAoB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,aAAa,CAAA;CAAE,2CA4E3E"}
@@ -1,18 +1,18 @@
1
1
  import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
- import { throwOnError as t } from "../utils/mutationResult.js";
3
- import { ProjectSelect as n, WorkItemSearchSelect as r } from "./Pickers.js";
4
- import { ConfirmButton as i, ErrorLine as a } from "./ConfirmButton.js";
5
- import { parseIntField as o } from "../utils/formValidation.js";
6
- import { enumLabel as s } from "./WorkItemControls.js";
7
- import { detailPath as c } from "../utils/rowNavigation.js";
8
- import { useState as l } from "react";
9
- import { BarChart3 as u, Pencil as d, ThumbsUp as f, Trash2 as p } from "lucide-react";
10
- import { gql as m } from "@apollo/client";
11
- import { useMutation as h } from "@apollo/client/react";
12
- import { jsx as g, jsxs as _ } from "react/jsx-runtime";
13
- import { Button as v, Input as y, ResponsiveDialog as b, ResponsiveDialogContent as x, ResponsiveDialogDescription as S, ResponsiveDialogFooter as C, ResponsiveDialogHeader as w, ResponsiveDialogTitle as T, ResponsiveDialogTrigger as E } from "@burdenoff/fe-libs/ui";
2
+ import { LEAVES_PAGE_CONTEXT as t, throwOnError as n } from "../utils/mutationResult.js";
3
+ import { detailPath as r } from "../utils/rowNavigation.js";
4
+ import { ProjectSelect as i, WorkItemSearchSelect as a } from "./Pickers.js";
5
+ import { ConfirmButton as o, ErrorLine as s } from "./ConfirmButton.js";
6
+ import { parseIntField as c } from "../utils/formValidation.js";
7
+ import { enumLabel as l } from "./WorkItemControls.js";
8
+ import { useState as u } from "react";
9
+ import { BarChart3 as d, Pencil as f, ThumbsUp as p, Trash2 as m } from "lucide-react";
10
+ import { gql as h } from "@apollo/client";
11
+ import { useMutation as g } from "@apollo/client/react";
12
+ import { jsx as _, jsxs as v } from "react/jsx-runtime";
13
+ import { Button as y, Input as b, ResponsiveDialog as x, ResponsiveDialogContent as S, ResponsiveDialogDescription as C, ResponsiveDialogFooter as w, ResponsiveDialogHeader as T, ResponsiveDialogTitle as E, ResponsiveDialogTrigger as D } from "@burdenoff/fe-libs/ui";
14
14
  //#region src/planmagnet/controls/FeatureControls.tsx
15
- var D = [
15
+ var O = [
16
16
  "SUBMITTED",
17
17
  "UNDER_REVIEW",
18
18
  "PLANNED",
@@ -20,7 +20,7 @@ var D = [
20
20
  "COMPLETED",
21
21
  "DECLINED",
22
22
  "DUPLICATE"
23
- ], O = [
23
+ ], k = [
24
24
  "GetFeatureRequest",
25
25
  "ListFeatureRequests",
26
26
  "ListFeatureRequestsForProduct",
@@ -28,17 +28,17 @@ var D = [
28
28
  "ListAllFeaturesAnalytics",
29
29
  "GetFeatureRequestAnalytics",
30
30
  "ReleaseCustomerRequests"
31
- ], k = m`
31
+ ], A = h`
32
32
  mutation FeatureControlsVote($id: ID!) {
33
33
  planmagnetVoteOnFeature(featureRequestId: $id) {
34
34
  id
35
35
  }
36
36
  }
37
- `, A = m`
37
+ `, j = h`
38
38
  mutation FeatureControlsUnvote($id: ID!) {
39
39
  planmagnetRemoveFeatureVote(featureRequestId: $id)
40
40
  }
41
- `, j = m`
41
+ `, M = h`
42
42
  mutation FeatureControlsUpdate($id: ID!, $input: PlanmagnetUpdateFeatureRequestInput!) {
43
43
  planmagnetUpdateFeatureRequest(id: $id, input: $input) {
44
44
  id
@@ -51,260 +51,263 @@ var D = [
51
51
  workItemId
52
52
  }
53
53
  }
54
- `, M = m`
54
+ `, N = h`
55
55
  mutation FeatureControlsDelete($id: ID!) {
56
56
  planmagnetDeleteFeatureRequest(id: $id)
57
57
  }
58
- `, N = {
59
- refetchQueries: O,
58
+ `, P = {
59
+ refetchQueries: k,
60
60
  awaitRefetchQueries: !0
61
61
  };
62
- function P({ featureId: e, voted: n }) {
63
- let [r, i] = h(k, N), [o, s] = h(A, N), [c, u] = l(null), d = i.loading || s.loading, p = async () => {
64
- u(null);
62
+ function F({ featureId: e, voted: t }) {
63
+ let [r, i] = g(A, P), [a, o] = g(j, P), [c, l] = u(null), d = i.loading || o.loading, f = async () => {
64
+ l(null);
65
65
  try {
66
- await t((n ? o : r)({ variables: { id: e } }));
66
+ await n((t ? a : r)({ variables: { id: e } }));
67
67
  } catch (e) {
68
- u(e instanceof Error ? e.message : "Your vote could not be saved.");
68
+ l(e instanceof Error ? e.message : "Your vote could not be saved.");
69
69
  }
70
70
  };
71
- return /* @__PURE__ */ _("div", {
71
+ return /* @__PURE__ */ v("div", {
72
72
  className: "flex flex-col items-end gap-1",
73
- children: [/* @__PURE__ */ _(v, {
73
+ children: [/* @__PURE__ */ v(y, {
74
74
  size: "sm",
75
- variant: n ? "default" : "outline",
75
+ variant: t ? "default" : "outline",
76
76
  "data-testid": "feature-vote",
77
- "aria-pressed": n,
77
+ "aria-pressed": t,
78
78
  disabled: d,
79
- onClick: () => void p(),
80
- children: [/* @__PURE__ */ g(f, { className: "mr-1 h-4 w-4" }), n ? "Voted" : "Vote"]
81
- }), c && /* @__PURE__ */ g(a, { message: c })]
79
+ onClick: () => void f(),
80
+ children: [/* @__PURE__ */ _(p, { className: "mr-1 h-4 w-4" }), t ? "Voted" : "Vote"]
81
+ }), c && /* @__PURE__ */ _(s, { message: c })]
82
82
  });
83
83
  }
84
- function F({ feature: n }) {
85
- let r = e(), [o, d] = h(j, N), [f, m] = h(M, { refetchQueries: O.filter((e) => e !== "GetFeatureRequest") }), [y, b] = l(null), x = d.loading || m.loading, S = D.includes(n.status) ? D : [n.status, ...D], C = async (e) => {
86
- b(null);
84
+ function I({ feature: i }) {
85
+ let a = e(), [c, f] = g(M, P), [p, h] = g(N, {
86
+ refetchQueries: k.filter((e) => e !== "GetFeatureRequest"),
87
+ context: t
88
+ }), [b, x] = u(null), S = f.loading || h.loading, C = O.includes(i.status) ? O : [i.status, ...O], w = async (e) => {
89
+ x(null);
87
90
  try {
88
- await t(o({ variables: {
89
- id: n.id,
91
+ await n(c({ variables: {
92
+ id: i.id,
90
93
  input: { status: e }
91
94
  } }));
92
95
  } catch (e) {
93
- b(e instanceof Error ? e.message : "The status could not be changed.");
96
+ x(e instanceof Error ? e.message : "The status could not be changed.");
94
97
  }
95
- }, w = async () => {
96
- b(null);
98
+ }, T = async () => {
99
+ x(null);
97
100
  try {
98
- await t(f({ variables: { id: n.id } })), r("/products/features-page");
101
+ await n(p({ variables: { id: i.id } })), a("/products/features-page");
99
102
  } catch (e) {
100
- b(e instanceof Error ? e.message : "The request could not be deleted.");
103
+ x(e instanceof Error ? e.message : "The request could not be deleted.");
101
104
  }
102
105
  };
103
- return /* @__PURE__ */ _("div", {
106
+ return /* @__PURE__ */ v("div", {
104
107
  className: "flex flex-col items-end gap-2",
105
- children: [/* @__PURE__ */ _("div", {
108
+ children: [/* @__PURE__ */ v("div", {
106
109
  className: "flex flex-wrap items-center justify-end gap-2",
107
110
  children: [
108
- /* @__PURE__ */ g("select", {
111
+ /* @__PURE__ */ _("select", {
109
112
  "aria-label": "Status",
110
113
  "data-testid": "feature-status",
111
114
  className: "rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60",
112
- value: n.status,
113
- disabled: x,
115
+ value: i.status,
116
+ disabled: S,
114
117
  onChange: (e) => {
115
- e.target.value !== n.status && C(e.target.value);
118
+ e.target.value !== i.status && w(e.target.value);
116
119
  },
117
- children: S.map((e) => /* @__PURE__ */ g("option", {
120
+ children: C.map((e) => /* @__PURE__ */ _("option", {
118
121
  value: e,
119
- children: s(e)
122
+ children: l(e)
120
123
  }, e))
121
124
  }),
122
- /* @__PURE__ */ g(I, { feature: n }),
123
- /* @__PURE__ */ _(v, {
125
+ /* @__PURE__ */ _(L, { feature: i }),
126
+ /* @__PURE__ */ v(y, {
124
127
  size: "sm",
125
128
  variant: "outline",
126
129
  "data-testid": "feature-analytics",
127
- onClick: () => r(c("/products/feature-analytics-page", n.id)),
128
- children: [/* @__PURE__ */ g(u, { className: "mr-1 h-4 w-4" }), "Analytics"]
130
+ onClick: () => a(r("/products/feature-analytics-page", i.id)),
131
+ children: [/* @__PURE__ */ _(d, { className: "mr-1 h-4 w-4" }), "Analytics"]
129
132
  }),
130
- /* @__PURE__ */ g(i, {
133
+ /* @__PURE__ */ _(o, {
131
134
  label: "Delete",
132
135
  variant: "outline",
133
- icon: /* @__PURE__ */ g(p, { className: "mr-1 h-4 w-4" }),
134
- title: `Delete "${n.title}"?`,
136
+ icon: /* @__PURE__ */ _(m, { className: "mr-1 h-4 w-4" }),
137
+ title: `Delete "${i.title}"?`,
135
138
  description: "The request and its votes are deleted. This cannot be undone here.",
136
139
  confirmLabel: "Delete request",
137
140
  destructive: !0,
138
- disabled: x,
139
- onConfirm: () => void w()
141
+ disabled: S,
142
+ onConfirm: () => void T()
140
143
  })
141
144
  ]
142
- }), y && /* @__PURE__ */ g(a, { message: y })]
145
+ }), b && /* @__PURE__ */ _(s, { message: b })]
143
146
  });
144
147
  }
145
- function I({ feature: e }) {
146
- let i = {
148
+ function L({ feature: e }) {
149
+ let t = {
147
150
  title: e.title,
148
151
  description: e.description ?? "",
149
152
  businessValue: e.businessValue == null ? "" : String(e.businessValue),
150
153
  effortEstimate: e.effortEstimate ?? "",
151
154
  projectId: e.projectId ?? "",
152
155
  workItemId: e.workItemId ?? ""
153
- }, [s, c] = l(!1), [u, f] = l(i), [p, m] = l(null), [D, O] = h(j, N), k = o(u.businessValue, {
156
+ }, [r, o] = u(!1), [l, d] = u(t), [p, m] = u(null), [h, O] = g(M, P), k = c(l.businessValue, {
154
157
  min: 0,
155
158
  max: 100
156
- }), A = u.title.trim() !== "" && !k.invalid && !O.loading, M = async () => {
159
+ }), A = l.title.trim() !== "" && !k.invalid && !O.loading, j = async () => {
157
160
  m(null);
158
- let n = (e) => e.trim() ? e.trim() : null, r = {
159
- title: u.title.trim(),
160
- description: n(u.description),
161
+ let r = (e) => e.trim() ? e.trim() : null, i = {
162
+ title: l.title.trim(),
163
+ description: r(l.description),
161
164
  businessValue: k.value ?? null,
162
- effortEstimate: n(u.effortEstimate),
163
- projectId: u.projectId || null,
164
- workItemId: u.workItemId || null
165
+ effortEstimate: r(l.effortEstimate),
166
+ projectId: l.projectId || null,
167
+ workItemId: l.workItemId || null
165
168
  }, a = {
166
- title: i.title,
167
- description: n(i.description),
168
- businessValue: i.businessValue === "" ? null : Number(i.businessValue),
169
- effortEstimate: n(i.effortEstimate),
170
- projectId: i.projectId || null,
171
- workItemId: i.workItemId || null
172
- }, o = Object.fromEntries(Object.entries(r).filter(([e, t]) => t !== a[e]));
173
- if (Object.keys(o).length === 0) {
174
- c(!1);
169
+ title: t.title,
170
+ description: r(t.description),
171
+ businessValue: t.businessValue === "" ? null : Number(t.businessValue),
172
+ effortEstimate: r(t.effortEstimate),
173
+ projectId: t.projectId || null,
174
+ workItemId: t.workItemId || null
175
+ }, s = Object.fromEntries(Object.entries(i).filter(([e, t]) => t !== a[e]));
176
+ if (Object.keys(s).length === 0) {
177
+ o(!1);
175
178
  return;
176
179
  }
177
180
  try {
178
- await t(D({ variables: {
181
+ await n(h({ variables: {
179
182
  id: e.id,
180
- input: o
181
- } })), c(!1);
183
+ input: s
184
+ } })), o(!1);
182
185
  } catch (e) {
183
186
  m(e instanceof Error ? e.message : "The request could not be saved.");
184
187
  }
185
- }, P = "text-sm font-medium";
186
- return /* @__PURE__ */ _(b, {
187
- open: s,
188
+ }, N = "text-sm font-medium";
189
+ return /* @__PURE__ */ v(x, {
190
+ open: r,
188
191
  onOpenChange: (e) => {
189
- c(e), e ? f(i) : m(null);
192
+ o(e), e ? d(t) : m(null);
190
193
  },
191
- children: [/* @__PURE__ */ g(E, {
194
+ children: [/* @__PURE__ */ _(D, {
192
195
  asChild: !0,
193
- children: /* @__PURE__ */ _(v, {
196
+ children: /* @__PURE__ */ v(y, {
194
197
  size: "sm",
195
198
  variant: "outline",
196
199
  "data-testid": "feature-edit",
197
- children: [/* @__PURE__ */ g(d, { className: "mr-1 h-4 w-4" }), "Edit"]
200
+ children: [/* @__PURE__ */ _(f, { className: "mr-1 h-4 w-4" }), "Edit"]
198
201
  })
199
- }), /* @__PURE__ */ _(x, {
202
+ }), /* @__PURE__ */ v(S, {
200
203
  className: "max-w-lg",
201
204
  children: [
202
- /* @__PURE__ */ _(w, { children: [/* @__PURE__ */ g(T, { children: "Edit feature request" }), /* @__PURE__ */ g(S, { children: "Link the request to the work item that delivers it so releases can show who is waiting on it." })] }),
203
- /* @__PURE__ */ _("div", {
205
+ /* @__PURE__ */ v(T, { children: [/* @__PURE__ */ _(E, { children: "Edit feature request" }), /* @__PURE__ */ _(C, { children: "Link the request to the work item that delivers it so releases can show who is waiting on it." })] }),
206
+ /* @__PURE__ */ v("div", {
204
207
  className: "space-y-3",
205
208
  children: [
206
- /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
209
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
207
210
  htmlFor: "feature-edit-title",
208
- className: P,
211
+ className: N,
209
212
  children: "Title"
210
- }), /* @__PURE__ */ g(y, {
213
+ }), /* @__PURE__ */ _(b, {
211
214
  id: "feature-edit-title",
212
215
  className: "mt-1",
213
- value: u.title,
214
- onChange: (e) => f({
215
- ...u,
216
+ value: l.title,
217
+ onChange: (e) => d({
218
+ ...l,
216
219
  title: e.target.value
217
220
  })
218
221
  })] }),
219
- /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
222
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
220
223
  htmlFor: "feature-edit-description",
221
- className: P,
224
+ className: N,
222
225
  children: "Description"
223
- }), /* @__PURE__ */ g("textarea", {
226
+ }), /* @__PURE__ */ _("textarea", {
224
227
  id: "feature-edit-description",
225
228
  className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[72px]",
226
- value: u.description,
227
- onChange: (e) => f({
228
- ...u,
229
+ value: l.description,
230
+ onChange: (e) => d({
231
+ ...l,
229
232
  description: e.target.value
230
233
  })
231
234
  })] }),
232
- /* @__PURE__ */ _("div", {
235
+ /* @__PURE__ */ v("div", {
233
236
  className: "grid grid-cols-2 gap-3",
234
- children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
237
+ children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
235
238
  htmlFor: "feature-edit-value",
236
- className: P,
239
+ className: N,
237
240
  children: "Business value (0–100)"
238
- }), /* @__PURE__ */ g(y, {
241
+ }), /* @__PURE__ */ _(b, {
239
242
  id: "feature-edit-value",
240
243
  type: "number",
241
244
  min: 0,
242
245
  max: 100,
243
246
  className: "mt-1",
244
- value: u.businessValue,
247
+ value: l.businessValue,
245
248
  "aria-invalid": k.invalid,
246
- onChange: (e) => f({
247
- ...u,
249
+ onChange: (e) => d({
250
+ ...l,
248
251
  businessValue: e.target.value
249
252
  })
250
- })] }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
253
+ })] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
251
254
  htmlFor: "feature-edit-effort",
252
- className: P,
255
+ className: N,
253
256
  children: "Effort"
254
- }), /* @__PURE__ */ g(y, {
257
+ }), /* @__PURE__ */ _(b, {
255
258
  id: "feature-edit-effort",
256
259
  className: "mt-1",
257
260
  placeholder: "e.g. M",
258
- value: u.effortEstimate,
259
- onChange: (e) => f({
260
- ...u,
261
+ value: l.effortEstimate,
262
+ onChange: (e) => d({
263
+ ...l,
261
264
  effortEstimate: e.target.value
262
265
  })
263
266
  })] })]
264
267
  }),
265
- k.invalid && /* @__PURE__ */ g(a, { message: "Business value is a whole number from 0 to 100." }),
266
- /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
268
+ k.invalid && /* @__PURE__ */ _(s, { message: "Business value is a whole number from 0 to 100." }),
269
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
267
270
  htmlFor: "feature-edit-project",
268
- className: P,
271
+ className: N,
269
272
  children: "Project"
270
- }), /* @__PURE__ */ g("div", {
273
+ }), /* @__PURE__ */ _("div", {
271
274
  className: "mt-1",
272
- children: /* @__PURE__ */ g(n, {
275
+ children: /* @__PURE__ */ _(i, {
273
276
  id: "feature-edit-project",
274
- value: u.projectId,
277
+ value: l.projectId,
275
278
  noneLabel: "No project",
276
- onChange: (e) => f({
277
- ...u,
279
+ onChange: (e) => d({
280
+ ...l,
278
281
  projectId: e,
279
282
  workItemId: ""
280
283
  })
281
284
  })
282
285
  })] }),
283
- /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
286
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
284
287
  htmlFor: "feature-edit-workitem",
285
- className: P,
288
+ className: N,
286
289
  children: "Delivered by work item"
287
- }), /* @__PURE__ */ g("div", {
290
+ }), /* @__PURE__ */ _("div", {
288
291
  className: "mt-1",
289
- children: /* @__PURE__ */ g(r, {
292
+ children: /* @__PURE__ */ _(a, {
290
293
  id: "feature-edit-workitem",
291
- projectId: u.projectId || void 0,
292
- value: u.workItemId,
293
- onChange: (e) => f({
294
- ...u,
294
+ projectId: l.projectId || void 0,
295
+ value: l.workItemId,
296
+ onChange: (e) => d({
297
+ ...l,
295
298
  workItemId: e
296
299
  })
297
300
  })
298
301
  })] }),
299
- p && /* @__PURE__ */ g(a, { message: p })
302
+ p && /* @__PURE__ */ _(s, { message: p })
300
303
  ]
301
304
  }),
302
- /* @__PURE__ */ _(C, { children: [/* @__PURE__ */ g(v, {
305
+ /* @__PURE__ */ v(w, { children: [/* @__PURE__ */ _(y, {
303
306
  variant: "outline",
304
- onClick: () => c(!1),
307
+ onClick: () => o(!1),
305
308
  children: "Cancel"
306
- }), /* @__PURE__ */ g(v, {
307
- onClick: () => void M(),
309
+ }), /* @__PURE__ */ _(y, {
310
+ onClick: () => void j(),
308
311
  disabled: !A,
309
312
  children: O.loading ? "Saving…" : "Save"
310
313
  })] })
@@ -313,6 +316,6 @@ function I({ feature: e }) {
313
316
  });
314
317
  }
315
318
  //#endregion
316
- export { F as FeatureHeaderActions, P as FeatureVoteButton };
319
+ export { I as FeatureHeaderActions, F as FeatureVoteButton };
317
320
 
318
321
  //# sourceMappingURL=FeatureControls.js.map