@burdenoff/microfe-planmagnet 2026.716.1 → 2026.719.2

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 (77) hide show
  1. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  2. package/dist/planmagnet/PlanMagnetRoot.js +98 -173
  3. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  4. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  5. package/dist/planmagnet/PlanMagnetRoutes.js +43 -67
  6. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  7. package/dist/planmagnet/pages/ProductsCreateProductPagePage.js +2 -2
  8. package/dist/planmagnet/pages/ProductsCreateProductPagePage.js.map +1 -1
  9. package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.js +2 -2
  10. package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.js.map +1 -1
  11. package/dist/planmagnet/pages/ProductsEditProductPagePage.js +2 -2
  12. package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
  13. package/dist/planmagnet/pages/ProductsEditReleasePagePage.js +2 -2
  14. package/dist/planmagnet/pages/ProductsEditReleasePagePage.js.map +1 -1
  15. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js +1 -1
  16. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js.map +1 -1
  17. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +1 -1
  18. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
  19. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +2 -2
  20. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
  21. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +1 -1
  22. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  23. package/dist/planmagnet/pages/ProductsProductsPagePage.js +1 -1
  24. package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
  25. package/dist/planmagnet/pages/ProductsReleasesPagePage.js +1 -1
  26. package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
  27. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +1 -1
  28. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
  29. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +1 -1
  30. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
  31. package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.js +2 -2
  32. package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.js.map +1 -1
  33. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +2 -2
  34. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js.map +1 -1
  35. package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js +1 -1
  36. package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js.map +1 -1
  37. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +2 -2
  38. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js.map +1 -1
  39. package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
  40. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +75 -86
  41. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
  42. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +1 -1
  43. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
  44. package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
  45. package/dist/planmagnet/pages/TasksEditTaskPagePage.js +6 -10
  46. package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
  47. package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
  48. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +4 -8
  49. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
  50. package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
  51. package/dist/planmagnet/pages/TasksTasksPagePage.js +8 -9
  52. package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
  53. package/package.json +1 -1
  54. package/dist/planmagnet/pages/CollaborationCollaborationPagePage.d.ts +0 -2
  55. package/dist/planmagnet/pages/CollaborationCollaborationPagePage.d.ts.map +0 -1
  56. package/dist/planmagnet/pages/CollaborationCollaborationPagePage.js +0 -110
  57. package/dist/planmagnet/pages/CollaborationCollaborationPagePage.js.map +0 -1
  58. package/dist/planmagnet/pages/CollaborationCreateChannelPagePage.d.ts +0 -2
  59. package/dist/planmagnet/pages/CollaborationCreateChannelPagePage.d.ts.map +0 -1
  60. package/dist/planmagnet/pages/CollaborationCreateChannelPagePage.js +0 -96
  61. package/dist/planmagnet/pages/CollaborationCreateChannelPagePage.js.map +0 -1
  62. package/dist/planmagnet/pages/CollaborationFilesPagePage.d.ts +0 -2
  63. package/dist/planmagnet/pages/CollaborationFilesPagePage.d.ts.map +0 -1
  64. package/dist/planmagnet/pages/CollaborationFilesPagePage.js +0 -112
  65. package/dist/planmagnet/pages/CollaborationFilesPagePage.js.map +0 -1
  66. package/dist/planmagnet/pages/CollaborationMessagesPagePage.d.ts +0 -2
  67. package/dist/planmagnet/pages/CollaborationMessagesPagePage.d.ts.map +0 -1
  68. package/dist/planmagnet/pages/CollaborationMessagesPagePage.js +0 -141
  69. package/dist/planmagnet/pages/CollaborationMessagesPagePage.js.map +0 -1
  70. package/dist/planmagnet/pages/CollaborationNewDiscussionPagePage.d.ts +0 -2
  71. package/dist/planmagnet/pages/CollaborationNewDiscussionPagePage.d.ts.map +0 -1
  72. package/dist/planmagnet/pages/CollaborationNewDiscussionPagePage.js +0 -124
  73. package/dist/planmagnet/pages/CollaborationNewDiscussionPagePage.js.map +0 -1
  74. package/dist/planmagnet/pages/MeetingsScheduleMeetingPagePage.d.ts +0 -2
  75. package/dist/planmagnet/pages/MeetingsScheduleMeetingPagePage.d.ts.map +0 -1
  76. package/dist/planmagnet/pages/MeetingsScheduleMeetingPagePage.js +0 -290
  77. package/dist/planmagnet/pages/MeetingsScheduleMeetingPagePage.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsEditReleasePagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsEditReleasePagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState, useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, ArrowLeft, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router-dom';\n\nconst GET_RELEASE = gql`\n query GetReleaseForEdit($id: ID!) {\n planmagnetRelease(id: $id) {\n id\n name\n description\n version\n status\n plannedDate\n releasedAt\n releaseNotes\n ownerId\n tags\n }\n }\n`;\n\nconst UPDATE_RELEASE = gql`\n mutation UpdateRelease($id: ID!, $input: UpdateReleaseInput!) {\n planmagnetUpdateRelease(id: $id, input: $input) {\n id\n name\n description\n version\n status\n plannedDate\n releaseNotes\n tags\n updatedAt\n }\n }\n`;\n\nexport function ProductsEditReleasePagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const params = useParams<{ id: string }>();\n const releaseId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_RELEASE, {\n variables: { id: releaseId },\n skip: !releaseId,\n });\n\n const [updateRelease, { loading: saving, error: saveError }] = useMutation(UPDATE_RELEASE, {\n refetchQueries: ['ListReleases', 'GetReleaseForEdit'],\n });\n\n const release = data?.planmagnetRelease;\n\n const defaultForm = useMemo(\n () => ({\n version: release?.version ?? '',\n name: release?.name ?? '',\n description: release?.description ?? '',\n status: release?.status ?? 'DRAFT',\n plannedDate: release?.plannedDate\n ? new Date(release.plannedDate).toISOString().split('T')[0]\n : '',\n releaseNotes: release?.releaseNotes ?? '',\n tags: release?.tags?.join(', ') ?? '',\n }),\n [release]\n );\n\n const [formOverrides, setFormOverrides] = useState<Record<string, string>>({});\n const form = { ...defaultForm, ...formOverrides };\n const setForm = (val: Record<string, string>) => setFormOverrides(val);\n\n const statuses = [\n 'DRAFT',\n 'PLANNING',\n 'IN_PROGRESS',\n 'READY',\n 'RELEASED',\n 'CANCELLED',\n 'ROLLED_BACK',\n ] as const;\n\n const handleSave = () => {\n planmagnetUpdateRelease({\n variables: {\n id: releaseId,\n input: {\n version: form.version || undefined,\n name: form.name || undefined,\n description: form.description || undefined,\n status: form.status || undefined,\n plannedDate: form.plannedDate || undefined,\n releaseNotes: form.releaseNotes || undefined,\n tags: form.tags\n ? form.tags\n .split(',')\n .map((t: string) => t.trim())\n .filter(Boolean)\n : undefined,\n },\n },\n });\n };\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load release: {error.message}</div>;\n }\n\n if (!release) return <div className=\"p-6 text-muted-foreground\">Release not found.</div>;\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigate('/products/releases')}>\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <h1 className=\"text-2xl font-semibold\">Edit Release</h1>\n <p className=\"text-sm text-muted-foreground\">\n {release.version} - {release.name}\n </p>\n </div>\n </div>\n <Button size=\"sm\" onClick={handleSave} disabled={saving}>\n {saving ? (\n <Loader2 className=\"mr-1 h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"mr-1 h-4 w-4\" />\n )}\n Save Changes\n </Button>\n </div>\n\n {saveError && (\n <div className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n Failed to save: {saveError.message}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>Release Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Version</label>\n <Input\n value={form.version}\n onChange={(e) => setForm({ ...form, version: e.target.value })}\n className=\"mt-1\"\n placeholder=\"v1.0.0\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Name</label>\n <Input\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Status</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n >\n {statuses.map((s) => (\n <option key={s} value={s} className=\"capitalize\">\n {s.toLowerCase().replace('_', ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Planned Date</label>\n <Input\n type=\"date\"\n value={form.plannedDate}\n onChange={(e) => setForm({ ...form, plannedDate: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Tags (comma-separated)</label>\n <Input\n value={form.tags}\n onChange={(e) => setForm({ ...form, tags: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. critical, security, performance\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Release Notes</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[120px] font-mono\"\n value={form.releaseNotes}\n onChange={(e) => setForm({ ...form, releaseNotes: e.target.value })}\n placeholder=\"Describe what changed in this release...\"\n />\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsEditReleasePagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsEditReleasePagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState, useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, ArrowLeft, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router-dom';\n\nconst GET_RELEASE = gql`\n query GetReleaseForEdit($id: ID!) {\n planmagnetRelease(id: $id) {\n id\n name\n description\n version\n status\n plannedDate\n releasedAt\n releaseNotes\n ownerId\n tags\n }\n }\n`;\n\nconst UPDATE_RELEASE = gql`\n mutation UpdateRelease($id: ID!, $input: PlanmagnetUpdateReleaseInput!) {\n planmagnetUpdateRelease(id: $id, input: $input) {\n id\n name\n description\n version\n status\n plannedDate\n releaseNotes\n tags\n updatedAt\n }\n }\n`;\n\nexport function ProductsEditReleasePagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const params = useParams<{ id: string }>();\n const releaseId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_RELEASE, {\n variables: { id: releaseId },\n skip: !releaseId,\n });\n\n const [updateRelease, { loading: saving, error: saveError }] = useMutation(UPDATE_RELEASE, {\n refetchQueries: ['ListReleases', 'GetReleaseForEdit'],\n });\n\n const release = data?.planmagnetRelease;\n\n const defaultForm = useMemo(\n () => ({\n version: release?.version ?? '',\n name: release?.name ?? '',\n description: release?.description ?? '',\n status: release?.status ?? 'DRAFT',\n plannedDate: release?.plannedDate\n ? new Date(release.plannedDate).toISOString().split('T')[0]\n : '',\n releaseNotes: release?.releaseNotes ?? '',\n tags: release?.tags?.join(', ') ?? '',\n }),\n [release]\n );\n\n const [formOverrides, setFormOverrides] = useState<Record<string, string>>({});\n const form = { ...defaultForm, ...formOverrides };\n const setForm = (val: Record<string, string>) => setFormOverrides(val);\n\n const statuses = [\n 'DRAFT',\n 'PLANNING',\n 'IN_PROGRESS',\n 'READY',\n 'RELEASED',\n 'CANCELLED',\n 'ROLLED_BACK',\n ] as const;\n\n const handleSave = () => {\n updateRelease({\n variables: {\n id: releaseId,\n input: {\n version: form.version || undefined,\n name: form.name || undefined,\n description: form.description || undefined,\n status: form.status || undefined,\n plannedDate: form.plannedDate || undefined,\n releaseNotes: form.releaseNotes || undefined,\n tags: form.tags\n ? form.tags\n .split(',')\n .map((t: string) => t.trim())\n .filter(Boolean)\n : undefined,\n },\n },\n });\n };\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load release: {error.message}</div>;\n }\n\n if (!release) return <div className=\"p-6 text-muted-foreground\">Release not found.</div>;\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigate('/products/releases')}>\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <h1 className=\"text-2xl font-semibold\">Edit Release</h1>\n <p className=\"text-sm text-muted-foreground\">\n {release.version} - {release.name}\n </p>\n </div>\n </div>\n <Button size=\"sm\" onClick={handleSave} disabled={saving}>\n {saving ? (\n <Loader2 className=\"mr-1 h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"mr-1 h-4 w-4\" />\n )}\n Save Changes\n </Button>\n </div>\n\n {saveError && (\n <div className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n Failed to save: {saveError.message}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>Release Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Version</label>\n <Input\n value={form.version}\n onChange={(e) => setForm({ ...form, version: e.target.value })}\n className=\"mt-1\"\n placeholder=\"v1.0.0\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Name</label>\n <Input\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[80px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Status</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm\"\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n >\n {statuses.map((s) => (\n <option key={s} value={s} className=\"capitalize\">\n {s.toLowerCase().replace('_', ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Planned Date</label>\n <Input\n type=\"date\"\n value={form.plannedDate}\n onChange={(e) => setForm({ ...form, plannedDate: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Tags (comma-separated)</label>\n <Input\n value={form.tags}\n onChange={(e) => setForm({ ...form, tags: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. critical, security, performance\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Release Notes</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[120px] font-mono\"\n value={form.releaseNotes}\n onChange={(e) => setForm({ ...form, releaseNotes: e.target.value })}\n placeholder=\"Describe what changed in this release...\"\n />\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -72,7 +72,7 @@ function v() {
72
72
  className: "p-6 text-muted-foreground",
73
73
  children: "Feature not found."
74
74
  });
75
- let w = C.votes ?? [], T = C.competitors?.length ?? 0, E = S?.featureRequests?.items ?? [], D = {};
75
+ let w = C.votes ?? [], T = C.competitors?.length ?? 0, E = S?.planmagnetFeatureRequests?.items ?? [], D = {};
76
76
  for (let e of w) D[e.weight] = (D[e.weight] || 0) + 1;
77
77
  let O = Math.max(...Object.values(D), 1), k = [], A = {};
78
78
  for (let e of w) {
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsFeatureAnalyticsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeatureAnalyticsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { TrendingUp, ThumbsUp, Users, BarChart3, Target, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router-dom';\n\nconst GET_FEATURE_REQUEST_ANALYTICS = gql`\n query GetFeatureRequestAnalytics($id: ID!) {\n planmagnetFeatureRequest(id: $id) {\n id\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n tags\n createdAt\n updatedAt\n votes {\n id\n voterId\n weight\n comment\n createdAt\n }\n competitors {\n id\n name\n }\n }\n }\n`;\n\nconst LIST_ALL_FEATURES_ANALYTICS = gql`\n query ListAllFeaturesAnalytics($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n status\n voteCount\n businessValue\n priorityScore\n }\n totalCount\n }\n }\n`;\n\ninterface VoteNode {\n id: string;\n voterId: string;\n weight: number;\n comment: string | null;\n createdAt: string;\n}\n\nexport function ProductsFeatureAnalyticsPagePage() {\n const { workspaceId } = usePlanMagnet();\n const params = useParams<{ id: string }>();\n const featureId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_FEATURE_REQUEST_ANALYTICS, {\n variables: { id: featureId },\n skip: !featureId,\n });\n\n const {\n data: allFeaturesData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_ALL_FEATURES_ANALYTICS, {\n variables: { workspaceId, pagination: { first: 100 } },\n skip: !workspaceId,\n });\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load analytics: {error.message}</div>;\n }\n\n const feature = data?.planmagnetFeatureRequest;\n if (!feature) return <div className=\"p-6 text-muted-foreground\">Feature not found.</div>;\n\n const votes: VoteNode[] = feature.votes ?? [];\n const competitorCount = feature.competitors?.length ?? 0;\n const allFeatures = allFeaturesData?.featureRequests?.items ?? [];\n\n // Vote distribution by weight\n const votesByWeight: Record<number, number> = {};\n for (const v of votes) {\n votesByWeight[v.weight] = (votesByWeight[v.weight] || 0) + 1;\n }\n const maxVoteWeight = Math.max(...Object.values(votesByWeight), 1);\n\n // Vote timeline - group by creation date\n const voteTimeline: { date: string; count: number }[] = [];\n const voteDateMap: Record<string, number> = {};\n for (const v of votes) {\n const date = new Date(v.createdAt).toLocaleDateString();\n voteDateMap[date] = (voteDateMap[date] || 0) + 1;\n }\n for (const [date, count] of Object.entries(voteDateMap)) {\n voteTimeline.push({ date, count });\n }\n voteTimeline.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());\n const maxTimelineCount = Math.max(...voteTimeline.map((d) => d.count), 1);\n\n // Status distribution across all features\n const statusDistribution: Record<string, number> = {};\n for (const f of allFeatures) {\n statusDistribution[f.status] = (statusDistribution[f.status] || 0) + 1;\n }\n\n // Rank this feature among all features by vote count\n const rankByVotes =\n allFeatures\n .slice()\n .sort((a: { voteCount: number }, b: { voteCount: number }) => b.voteCount - a.voteCount)\n .findIndex((f: { id: string }) => f.id === feature.id) + 1;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Feature Analytics</h1>\n <p className=\"text-sm text-muted-foreground\">\n {feature.title} - {feature.status.toLowerCase().replace('_', ' ')}\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Target className=\"h-4 w-4\" />\n Priority Score\n </div>\n <p className=\"text-2xl font-bold mt-1\">{feature.priorityScore ?? 0}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <ThumbsUp className=\"h-4 w-4\" />\n Total Votes\n </div>\n <p className=\"text-2xl font-bold mt-1\">{feature.voteCount}</p>\n <p className=\"text-xs text-green-600 mt-1\">\n Rank #{rankByVotes || 'N/A'} of {allFeatures.length}\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Users className=\"h-4 w-4\" />\n Competitors\n </div>\n <p className=\"text-2xl font-bold mt-1\">{competitorCount}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <TrendingUp className=\"h-4 w-4\" />\n Business Value\n </div>\n <p className=\"text-2xl font-bold mt-1\">{feature.businessValue ?? 0}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" />\n Vote Timeline\n </CardTitle>\n </CardHeader>\n <CardContent>\n {voteTimeline.length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-8\">\n No vote data available\n </p>\n ) : (\n <div className=\"flex items-end gap-1 h-48\">\n {voteTimeline.map((d) => (\n <div key={d.date} className=\"flex-1 flex flex-col items-center gap-1\">\n <div className=\"w-full flex justify-center\">\n <div\n className=\"w-5 bg-primary rounded-t transition-all\"\n style={{ height: `${(d.count / maxTimelineCount) * 160}px` }}\n />\n </div>\n <span className=\"text-[10px] text-muted-foreground truncate max-w-[40px]\">\n {d.date}\n </span>\n </div>\n ))}\n </div>\n )}\n <p className=\"text-xs text-muted-foreground text-center mt-2\">Votes over time</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Vote Weight Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {Object.entries(votesByWeight).length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-8\">No votes recorded</p>\n ) : (\n Object.entries(votesByWeight)\n .sort(([a], [b]) => Number(a) - Number(b))\n .map(([weight, count]) => (\n <div key={weight}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span>Weight {weight}</span>\n <span className=\"font-semibold\">{count} votes</span>\n </div>\n <div className=\"w-full h-3 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-primary\"\n style={{ width: `${(count / maxVoteWeight) * 100}%` }}\n />\n </div>\n </div>\n ))\n )}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle>All Features - Status Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(statusDistribution).map(([status, count]) => {\n const total = allFeatures.length;\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\n const colors: Record<string, string> = {\n SUBMITTED: 'bg-gray-400',\n UNDER_REVIEW: 'bg-yellow-400',\n PLANNED: 'bg-blue-400',\n IN_PROGRESS: 'bg-purple-400',\n COMPLETED: 'bg-green-400',\n DECLINED: 'bg-red-400',\n DUPLICATE: 'bg-orange-400',\n };\n return (\n <div key={status}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span className=\"capitalize\">{status.toLowerCase().replace('_', ' ')}</span>\n <span className=\"font-medium\">\n {count} ({pct}%)\n </span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${colors[status] ?? 'bg-gray-300'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Feature Impact</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"text-center p-4 bg-muted rounded-md\">\n <p className=\"text-3xl font-bold text-primary\">{feature.voteCount}</p>\n <p className=\"text-sm text-muted-foreground\">total votes received</p>\n </div>\n <div className=\"grid grid-cols-2 gap-2 text-center\">\n <div className=\"p-3 border rounded-md\">\n <p className=\"text-lg font-semibold\">{feature.businessValue ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">business value</p>\n </div>\n <div className=\"p-3 border rounded-md\">\n <p className=\"text-lg font-semibold\">{feature.priorityScore ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">priority score</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsFeatureAnalyticsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeatureAnalyticsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { TrendingUp, ThumbsUp, Users, BarChart3, Target, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router-dom';\n\nconst GET_FEATURE_REQUEST_ANALYTICS = gql`\n query GetFeatureRequestAnalytics($id: ID!) {\n planmagnetFeatureRequest(id: $id) {\n id\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n tags\n createdAt\n updatedAt\n votes {\n id\n voterId\n weight\n comment\n createdAt\n }\n competitors {\n id\n name\n }\n }\n }\n`;\n\nconst LIST_ALL_FEATURES_ANALYTICS = gql`\n query ListAllFeaturesAnalytics($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n status\n voteCount\n businessValue\n priorityScore\n }\n totalCount\n }\n }\n`;\n\ninterface VoteNode {\n id: string;\n voterId: string;\n weight: number;\n comment: string | null;\n createdAt: string;\n}\n\nexport function ProductsFeatureAnalyticsPagePage() {\n const { workspaceId } = usePlanMagnet();\n const params = useParams<{ id: string }>();\n const featureId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_FEATURE_REQUEST_ANALYTICS, {\n variables: { id: featureId },\n skip: !featureId,\n });\n\n const {\n data: allFeaturesData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_ALL_FEATURES_ANALYTICS, {\n variables: { workspaceId, pagination: { first: 100 } },\n skip: !workspaceId,\n });\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load analytics: {error.message}</div>;\n }\n\n const feature = data?.planmagnetFeatureRequest;\n if (!feature) return <div className=\"p-6 text-muted-foreground\">Feature not found.</div>;\n\n const votes: VoteNode[] = feature.votes ?? [];\n const competitorCount = feature.competitors?.length ?? 0;\n const allFeatures = allFeaturesData?.planmagnetFeatureRequests?.items ?? [];\n\n // Vote distribution by weight\n const votesByWeight: Record<number, number> = {};\n for (const v of votes) {\n votesByWeight[v.weight] = (votesByWeight[v.weight] || 0) + 1;\n }\n const maxVoteWeight = Math.max(...Object.values(votesByWeight), 1);\n\n // Vote timeline - group by creation date\n const voteTimeline: { date: string; count: number }[] = [];\n const voteDateMap: Record<string, number> = {};\n for (const v of votes) {\n const date = new Date(v.createdAt).toLocaleDateString();\n voteDateMap[date] = (voteDateMap[date] || 0) + 1;\n }\n for (const [date, count] of Object.entries(voteDateMap)) {\n voteTimeline.push({ date, count });\n }\n voteTimeline.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());\n const maxTimelineCount = Math.max(...voteTimeline.map((d) => d.count), 1);\n\n // Status distribution across all features\n const statusDistribution: Record<string, number> = {};\n for (const f of allFeatures) {\n statusDistribution[f.status] = (statusDistribution[f.status] || 0) + 1;\n }\n\n // Rank this feature among all features by vote count\n const rankByVotes =\n allFeatures\n .slice()\n .sort((a: { voteCount: number }, b: { voteCount: number }) => b.voteCount - a.voteCount)\n .findIndex((f: { id: string }) => f.id === feature.id) + 1;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Feature Analytics</h1>\n <p className=\"text-sm text-muted-foreground\">\n {feature.title} - {feature.status.toLowerCase().replace('_', ' ')}\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Target className=\"h-4 w-4\" />\n Priority Score\n </div>\n <p className=\"text-2xl font-bold mt-1\">{feature.priorityScore ?? 0}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <ThumbsUp className=\"h-4 w-4\" />\n Total Votes\n </div>\n <p className=\"text-2xl font-bold mt-1\">{feature.voteCount}</p>\n <p className=\"text-xs text-green-600 mt-1\">\n Rank #{rankByVotes || 'N/A'} of {allFeatures.length}\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Users className=\"h-4 w-4\" />\n Competitors\n </div>\n <p className=\"text-2xl font-bold mt-1\">{competitorCount}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <TrendingUp className=\"h-4 w-4\" />\n Business Value\n </div>\n <p className=\"text-2xl font-bold mt-1\">{feature.businessValue ?? 0}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" />\n Vote Timeline\n </CardTitle>\n </CardHeader>\n <CardContent>\n {voteTimeline.length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-8\">\n No vote data available\n </p>\n ) : (\n <div className=\"flex items-end gap-1 h-48\">\n {voteTimeline.map((d) => (\n <div key={d.date} className=\"flex-1 flex flex-col items-center gap-1\">\n <div className=\"w-full flex justify-center\">\n <div\n className=\"w-5 bg-primary rounded-t transition-all\"\n style={{ height: `${(d.count / maxTimelineCount) * 160}px` }}\n />\n </div>\n <span className=\"text-[10px] text-muted-foreground truncate max-w-[40px]\">\n {d.date}\n </span>\n </div>\n ))}\n </div>\n )}\n <p className=\"text-xs text-muted-foreground text-center mt-2\">Votes over time</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Vote Weight Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {Object.entries(votesByWeight).length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-8\">No votes recorded</p>\n ) : (\n Object.entries(votesByWeight)\n .sort(([a], [b]) => Number(a) - Number(b))\n .map(([weight, count]) => (\n <div key={weight}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span>Weight {weight}</span>\n <span className=\"font-semibold\">{count} votes</span>\n </div>\n <div className=\"w-full h-3 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-primary\"\n style={{ width: `${(count / maxVoteWeight) * 100}%` }}\n />\n </div>\n </div>\n ))\n )}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle>All Features - Status Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(statusDistribution).map(([status, count]) => {\n const total = allFeatures.length;\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\n const colors: Record<string, string> = {\n SUBMITTED: 'bg-gray-400',\n UNDER_REVIEW: 'bg-yellow-400',\n PLANNED: 'bg-blue-400',\n IN_PROGRESS: 'bg-purple-400',\n COMPLETED: 'bg-green-400',\n DECLINED: 'bg-red-400',\n DUPLICATE: 'bg-orange-400',\n };\n return (\n <div key={status}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span className=\"capitalize\">{status.toLowerCase().replace('_', ' ')}</span>\n <span className=\"font-medium\">\n {count} ({pct}%)\n </span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${colors[status] ?? 'bg-gray-300'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Feature Impact</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"text-center p-4 bg-muted rounded-md\">\n <p className=\"text-3xl font-bold text-primary\">{feature.voteCount}</p>\n <p className=\"text-sm text-muted-foreground\">total votes received</p>\n </div>\n <div className=\"grid grid-cols-2 gap-2 text-center\">\n <div className=\"p-3 border rounded-md\">\n <p className=\"text-lg font-semibold\">{feature.businessValue ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">business value</p>\n </div>\n <div className=\"p-3 border rounded-md\">\n <p className=\"text-lg font-semibold\">{feature.priorityScore ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">priority score</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -9,7 +9,7 @@ import { Badge as p, Button as m, Card as h, CardContent as g } from "@burdenoff
9
9
  var _ = l`
10
10
  query ListFeatureRequests(
11
11
  $workspaceId: ID!
12
- $filter: FeatureRequestFilter
12
+ $filter: PlanmagnetFeatureRequestFilter
13
13
  $pagination: PlanmagnetPaginationInput
14
14
  ) {
15
15
  planmagnetFeatureRequests(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsFeaturesPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport {\n Star,\n Plus,\n ThumbsUp,\n MessageSquare,\n Target,\n Filter,\n ArrowUpDown,\n Loader2,\n} from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst LIST_FEATURE_REQUESTS = gql`\n query ListFeatureRequests(\n $workspaceId: ID!\n $filter: FeatureRequestFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n tags\n createdAt\n updatedAt\n }\n totalCount\n }\n }\n`;\n\ninterface FeatureRequestNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n businessValue: number | null;\n effortEstimate: string | null;\n priorityScore: number | null;\n voteCount: number;\n submittedBy: string;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nconst statusColor: Record<string, string> = {\n SUBMITTED: 'bg-gray-100 text-gray-800',\n UNDER_REVIEW: 'bg-yellow-100 text-yellow-800',\n PLANNED: 'bg-blue-100 text-blue-800',\n IN_PROGRESS: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-green-100 text-green-800',\n DECLINED: 'bg-red-100 text-red-800',\n DUPLICATE: 'bg-orange-100 text-orange-800',\n};\n\nexport function ProductsFeaturesPagePage() {\n const { workspaceId } = usePlanMagnet();\n const [statusFilter, setStatusFilter] = useState<string>('all');\n const [sortBy, setSortBy] = useState<'priorityScore' | 'voteCount' | 'businessValue'>(\n 'priorityScore'\n );\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_FEATURE_REQUESTS, {\n variables: {\n workspaceId,\n filter: statusFilter !== 'all' ? { status: statusFilter } : undefined,\n pagination: { first: 50 },\n },\n skip: !workspaceId,\n });\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load features: {error.message}</div>;\n }\n\n const features: FeatureRequestNode[] = data?.planmagnetFeatureRequests?.items ?? [];\n const totalCount = data?.planmagnetFeatureRequests?.totalCount ?? 0;\n\n const sorted = [...features].sort((a, b) => {\n const aVal = a[sortBy] ?? 0;\n const bVal = b[sortBy] ?? 0;\n return (bVal as number) - (aVal as number);\n });\n\n const statuses = [\n 'all',\n 'SUBMITTED',\n 'UNDER_REVIEW',\n 'PLANNED',\n 'IN_PROGRESS',\n 'COMPLETED',\n 'DECLINED',\n 'DUPLICATE',\n ];\n\n const stats = {\n total: totalCount,\n inProgress: features.filter((f) => f.status === 'IN_PROGRESS').length,\n completed: features.filter((f) => f.status === 'COMPLETED').length,\n avgPriority:\n features.length > 0\n ? Math.round(features.reduce((s, f) => s + (f.priorityScore ?? 0), 0) / features.length)\n : 0,\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Features</h1>\n <p className=\"text-sm text-muted-foreground\">\n {totalCount} feature requests across all products\n </p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"mr-1 h-4 w-4\" />\n New Feature\n </Button>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Total Features</p>\n <p className=\"text-xl font-bold\">{stats.total}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">In Progress</p>\n <p className=\"text-xl font-bold\">{stats.inProgress}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Completed</p>\n <p className=\"text-xl font-bold\">{stats.completed}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Avg Priority Score</p>\n <p className=\"text-xl font-bold\">{stats.avgPriority}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n {statuses.map((s) => (\n <button\n key={s}\n onClick={() => setStatusFilter(s)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${statusFilter === s ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {s === 'all' ? 'All' : s.toLowerCase().replace('_', ' ')}\n </button>\n ))}\n </div>\n <div className=\"flex items-center gap-2\">\n <ArrowUpDown className=\"h-4 w-4 text-muted-foreground\" />\n {(['priorityScore', 'voteCount', 'businessValue'] as const).map((s) => (\n <button\n key={s}\n onClick={() => setSortBy(s)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${sortBy === s ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {s === 'priorityScore' ? 'Priority' : s === 'voteCount' ? 'Votes' : 'Value'}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"space-y-3\">\n {sorted.map((feature) => (\n <Card key={feature.id}>\n <CardContent className=\"py-4\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium\">{feature.title}</span>\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${statusColor[feature.status] ?? 'bg-gray-100'}`}\n >\n {feature.status.toLowerCase().replace('_', ' ')}\n </span>\n </div>\n <p className=\"text-xs text-muted-foreground\">{feature.description}</p>\n <div className=\"flex items-center gap-4 text-xs text-muted-foreground\">\n <span>Effort: {feature.effortEstimate ?? 'N/A'}</span>\n <span>Submitted by: {feature.submittedBy}</span>\n <span>{new Date(feature.updatedAt).toLocaleDateString()}</span>\n </div>\n {feature.tags.length > 0 && (\n <div className=\"flex gap-1\">\n {feature.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-[10px]\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-4 shrink-0\">\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <Target className=\"h-3 w-3 text-primary\" />\n <span className=\"text-sm font-bold\">{feature.priorityScore ?? 0}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Priority</p>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <ThumbsUp className=\"h-3 w-3 text-green-500\" />\n <span className=\"text-sm font-bold\">{feature.voteCount}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Votes</p>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-yellow-500\" />\n <span className=\"text-sm font-bold\">{feature.businessValue ?? 0}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Value</p>\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n {sorted.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">\n No features match the selected filters.\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsFeaturesPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport {\n Star,\n Plus,\n ThumbsUp,\n MessageSquare,\n Target,\n Filter,\n ArrowUpDown,\n Loader2,\n} from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst LIST_FEATURE_REQUESTS = gql`\n query ListFeatureRequests(\n $workspaceId: ID!\n $filter: PlanmagnetFeatureRequestFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n tags\n createdAt\n updatedAt\n }\n totalCount\n }\n }\n`;\n\ninterface FeatureRequestNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n businessValue: number | null;\n effortEstimate: string | null;\n priorityScore: number | null;\n voteCount: number;\n submittedBy: string;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nconst statusColor: Record<string, string> = {\n SUBMITTED: 'bg-gray-100 text-gray-800',\n UNDER_REVIEW: 'bg-yellow-100 text-yellow-800',\n PLANNED: 'bg-blue-100 text-blue-800',\n IN_PROGRESS: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-green-100 text-green-800',\n DECLINED: 'bg-red-100 text-red-800',\n DUPLICATE: 'bg-orange-100 text-orange-800',\n};\n\nexport function ProductsFeaturesPagePage() {\n const { workspaceId } = usePlanMagnet();\n const [statusFilter, setStatusFilter] = useState<string>('all');\n const [sortBy, setSortBy] = useState<'priorityScore' | 'voteCount' | 'businessValue'>(\n 'priorityScore'\n );\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_FEATURE_REQUESTS, {\n variables: {\n workspaceId,\n filter: statusFilter !== 'all' ? { status: statusFilter } : undefined,\n pagination: { first: 50 },\n },\n skip: !workspaceId,\n });\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load features: {error.message}</div>;\n }\n\n const features: FeatureRequestNode[] = data?.planmagnetFeatureRequests?.items ?? [];\n const totalCount = data?.planmagnetFeatureRequests?.totalCount ?? 0;\n\n const sorted = [...features].sort((a, b) => {\n const aVal = a[sortBy] ?? 0;\n const bVal = b[sortBy] ?? 0;\n return (bVal as number) - (aVal as number);\n });\n\n const statuses = [\n 'all',\n 'SUBMITTED',\n 'UNDER_REVIEW',\n 'PLANNED',\n 'IN_PROGRESS',\n 'COMPLETED',\n 'DECLINED',\n 'DUPLICATE',\n ];\n\n const stats = {\n total: totalCount,\n inProgress: features.filter((f) => f.status === 'IN_PROGRESS').length,\n completed: features.filter((f) => f.status === 'COMPLETED').length,\n avgPriority:\n features.length > 0\n ? Math.round(features.reduce((s, f) => s + (f.priorityScore ?? 0), 0) / features.length)\n : 0,\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Features</h1>\n <p className=\"text-sm text-muted-foreground\">\n {totalCount} feature requests across all products\n </p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"mr-1 h-4 w-4\" />\n New Feature\n </Button>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Total Features</p>\n <p className=\"text-xl font-bold\">{stats.total}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">In Progress</p>\n <p className=\"text-xl font-bold\">{stats.inProgress}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Completed</p>\n <p className=\"text-xl font-bold\">{stats.completed}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Avg Priority Score</p>\n <p className=\"text-xl font-bold\">{stats.avgPriority}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n {statuses.map((s) => (\n <button\n key={s}\n onClick={() => setStatusFilter(s)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${statusFilter === s ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {s === 'all' ? 'All' : s.toLowerCase().replace('_', ' ')}\n </button>\n ))}\n </div>\n <div className=\"flex items-center gap-2\">\n <ArrowUpDown className=\"h-4 w-4 text-muted-foreground\" />\n {(['priorityScore', 'voteCount', 'businessValue'] as const).map((s) => (\n <button\n key={s}\n onClick={() => setSortBy(s)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${sortBy === s ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {s === 'priorityScore' ? 'Priority' : s === 'voteCount' ? 'Votes' : 'Value'}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"space-y-3\">\n {sorted.map((feature) => (\n <Card key={feature.id}>\n <CardContent className=\"py-4\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium\">{feature.title}</span>\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${statusColor[feature.status] ?? 'bg-gray-100'}`}\n >\n {feature.status.toLowerCase().replace('_', ' ')}\n </span>\n </div>\n <p className=\"text-xs text-muted-foreground\">{feature.description}</p>\n <div className=\"flex items-center gap-4 text-xs text-muted-foreground\">\n <span>Effort: {feature.effortEstimate ?? 'N/A'}</span>\n <span>Submitted by: {feature.submittedBy}</span>\n <span>{new Date(feature.updatedAt).toLocaleDateString()}</span>\n </div>\n {feature.tags.length > 0 && (\n <div className=\"flex gap-1\">\n {feature.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-[10px]\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-4 shrink-0\">\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <Target className=\"h-3 w-3 text-primary\" />\n <span className=\"text-sm font-bold\">{feature.priorityScore ?? 0}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Priority</p>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <ThumbsUp className=\"h-3 w-3 text-green-500\" />\n <span className=\"text-sm font-bold\">{feature.voteCount}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Votes</p>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-yellow-500\" />\n <span className=\"text-sm font-bold\">{feature.businessValue ?? 0}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Value</p>\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n {sorted.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">\n No features match the selected filters.\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -16,7 +16,7 @@ var v = s`
16
16
  }
17
17
  }
18
18
  `, y = s`
19
- mutation CreateFeatureRequest($input: CreateFeatureRequestInput!) {
19
+ mutation CreateFeatureRequest($input: PlanmagnetCreateFeatureRequestInput!) {
20
20
  planmagnetCreateFeatureRequest(input: $input) {
21
21
  id
22
22
  title
@@ -47,7 +47,7 @@ function b() {
47
47
  b("/products/features");
48
48
  }
49
49
  });
50
- return x?.roadmaps?.items, /* @__PURE__ */ d("div", {
50
+ return x?.planmagnetRoadmaps?.items, /* @__PURE__ */ d("div", {
51
51
  className: "space-y-6 p-6 max-w-4xl",
52
52
  children: [
53
53
  /* @__PURE__ */ d("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsNewFeaturePagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsNewFeaturePagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, X, Star, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\n\nconst LIST_ROADMAPS_FOR_SELECT = gql`\n query ListRoadmapsForFeatureSelect($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetRoadmaps(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n name\n }\n }\n }\n`;\n\nconst CREATE_FEATURE_REQUEST = gql`\n mutation CreateFeatureRequest($input: CreateFeatureRequestInput!) {\n planmagnetCreateFeatureRequest(input: $input) {\n id\n title\n status\n businessValue\n effortEstimate\n priorityScore\n }\n }\n`;\n\nexport function ProductsNewFeaturePagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n\n const {\n data: roadmapsData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_ROADMAPS_FOR_SELECT, {\n variables: { workspaceId, pagination: { first: 100 } },\n skip: !workspaceId,\n });\n\n const [form, setForm] = useState({\n title: '',\n description: '',\n status: 'SUBMITTED' as string,\n businessValue: 50,\n effortEstimate: 'medium',\n tags: '',\n });\n\n const [createFeature, { loading: saving, error: saveError }] = useMutation(\n CREATE_FEATURE_REQUEST,\n {\n refetchQueries: ['ListFeatureRequests'],\n onCompleted: () => {\n navigate('/products/features');\n },\n }\n );\n\n const statusOptions = [\n 'SUBMITTED',\n 'UNDER_REVIEW',\n 'PLANNED',\n 'IN_PROGRESS',\n 'COMPLETED',\n ] as const;\n const efforts = ['small', 'medium', 'large', 'epic'] as const;\n\n const handleCreate = () => {\n if (!form.title.trim()) return;\n createFeature({\n variables: {\n input: {\n title: form.title,\n description: form.description || undefined,\n status: form.status,\n businessValue: form.businessValue,\n effortEstimate: form.effortEstimate,\n tags: form.tags\n ? form.tags\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean)\n : undefined,\n },\n },\n });\n };\n\n const roadmaps = roadmapsData?.roadmaps?.items ?? [];\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">New Feature</h1>\n <p className=\"text-sm text-muted-foreground\">\n Define a new feature request with business value metrics.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => navigate('/products/features')}>\n <X className=\"mr-1 h-4 w-4\" />\n Cancel\n </Button>\n <Button size=\"sm\" onClick={handleCreate} disabled={saving || !form.title.trim()}>\n {saving ? (\n <Loader2 className=\"mr-1 h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"mr-1 h-4 w-4\" />\n )}\n Create Feature\n </Button>\n </div>\n </div>\n\n {saveError && (\n <div className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n Failed to create feature: {saveError.message}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>Feature Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Title</label>\n <Input\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. Advanced Search Filters\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[100px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"Describe the feature and its user value...\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Status</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm capitalize\"\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n >\n {statusOptions.map((s) => (\n <option key={s} value={s}>\n {s.toLowerCase().replace('_', ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Effort Estimate</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm capitalize\"\n value={form.effortEstimate}\n onChange={(e) => setForm({ ...form, effortEstimate: e.target.value })}\n >\n {efforts.map((e) => (\n <option key={e} value={e}>\n {e}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Tags (comma-separated)</label>\n <Input\n value={form.tags}\n onChange={(e) => setForm({ ...form, tags: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. search, ux, performance\"\n />\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Star className=\"h-4 w-4\" />\n Business Value\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Business Value (0-100)</label>\n <Input\n type=\"number\"\n min={0}\n max={100}\n value={form.businessValue}\n onChange={(e) => setForm({ ...form, businessValue: Number(e.target.value) })}\n className=\"mt-1\"\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n How much business value does this feature deliver?\n </p>\n </div>\n\n <div className=\"p-4 bg-muted rounded-md text-center\">\n <p className=\"text-xs text-muted-foreground\">Business Value Score</p>\n <p className=\"text-3xl font-bold text-primary mt-2\">{form.businessValue}</p>\n <p className=\"text-xs text-muted-foreground\">out of 100</p>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsNewFeaturePagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsNewFeaturePagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, X, Star, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\n\nconst LIST_ROADMAPS_FOR_SELECT = gql`\n query ListRoadmapsForFeatureSelect($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetRoadmaps(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n name\n }\n }\n }\n`;\n\nconst CREATE_FEATURE_REQUEST = gql`\n mutation CreateFeatureRequest($input: PlanmagnetCreateFeatureRequestInput!) {\n planmagnetCreateFeatureRequest(input: $input) {\n id\n title\n status\n businessValue\n effortEstimate\n priorityScore\n }\n }\n`;\n\nexport function ProductsNewFeaturePagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n\n const {\n data: roadmapsData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_ROADMAPS_FOR_SELECT, {\n variables: { workspaceId, pagination: { first: 100 } },\n skip: !workspaceId,\n });\n\n const [form, setForm] = useState({\n title: '',\n description: '',\n status: 'SUBMITTED' as string,\n businessValue: 50,\n effortEstimate: 'medium',\n tags: '',\n });\n\n const [createFeature, { loading: saving, error: saveError }] = useMutation(\n CREATE_FEATURE_REQUEST,\n {\n refetchQueries: ['ListFeatureRequests'],\n onCompleted: () => {\n navigate('/products/features');\n },\n }\n );\n\n const statusOptions = [\n 'SUBMITTED',\n 'UNDER_REVIEW',\n 'PLANNED',\n 'IN_PROGRESS',\n 'COMPLETED',\n ] as const;\n const efforts = ['small', 'medium', 'large', 'epic'] as const;\n\n const handleCreate = () => {\n if (!form.title.trim()) return;\n createFeature({\n variables: {\n input: {\n title: form.title,\n description: form.description || undefined,\n status: form.status,\n businessValue: form.businessValue,\n effortEstimate: form.effortEstimate,\n tags: form.tags\n ? form.tags\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean)\n : undefined,\n },\n },\n });\n };\n\n const roadmaps = roadmapsData?.planmagnetRoadmaps?.items ?? [];\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">New Feature</h1>\n <p className=\"text-sm text-muted-foreground\">\n Define a new feature request with business value metrics.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => navigate('/products/features')}>\n <X className=\"mr-1 h-4 w-4\" />\n Cancel\n </Button>\n <Button size=\"sm\" onClick={handleCreate} disabled={saving || !form.title.trim()}>\n {saving ? (\n <Loader2 className=\"mr-1 h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"mr-1 h-4 w-4\" />\n )}\n Create Feature\n </Button>\n </div>\n </div>\n\n {saveError && (\n <div className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n Failed to create feature: {saveError.message}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>Feature Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Title</label>\n <Input\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. Advanced Search Filters\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[100px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"Describe the feature and its user value...\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Status</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm capitalize\"\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n >\n {statusOptions.map((s) => (\n <option key={s} value={s}>\n {s.toLowerCase().replace('_', ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Effort Estimate</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm capitalize\"\n value={form.effortEstimate}\n onChange={(e) => setForm({ ...form, effortEstimate: e.target.value })}\n >\n {efforts.map((e) => (\n <option key={e} value={e}>\n {e}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Tags (comma-separated)</label>\n <Input\n value={form.tags}\n onChange={(e) => setForm({ ...form, tags: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. search, ux, performance\"\n />\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Star className=\"h-4 w-4\" />\n Business Value\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Business Value (0-100)</label>\n <Input\n type=\"number\"\n min={0}\n max={100}\n value={form.businessValue}\n onChange={(e) => setForm({ ...form, businessValue: Number(e.target.value) })}\n className=\"mt-1\"\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n How much business value does this feature deliver?\n </p>\n </div>\n\n <div className=\"p-4 bg-muted rounded-md text-center\">\n <p className=\"text-xs text-muted-foreground\">Business Value Score</p>\n <p className=\"text-3xl font-bold text-primary mt-2\">{form.businessValue}</p>\n <p className=\"text-xs text-muted-foreground\">out of 100</p>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -91,7 +91,7 @@ function b() {
91
91
  className: "p-6 text-muted-foreground",
92
92
  children: "Product not found."
93
93
  });
94
- let D = E.items ?? [], O = w?.featureRequests?.items ?? [], k = T?.releases?.items ?? [], A = w?.featureRequests?.totalCount ?? 0, j = T?.releases?.totalCount ?? 0;
94
+ let D = E.items ?? [], O = w?.planmagnetFeatureRequests?.items ?? [], k = T?.planmagnetReleases?.items ?? [], A = w?.planmagnetFeatureRequests?.totalCount ?? 0, j = T?.planmagnetReleases?.totalCount ?? 0;
95
95
  return /* @__PURE__ */ d("div", {
96
96
  className: "space-y-6 p-6",
97
97
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsProductDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Package, GitBranch, Map, Star, Users, Calendar, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router-dom';\n\nconst GET_ROADMAP = gql`\n query GetRoadmapDetail($id: ID!) {\n planmagnetRoadmap(id: $id) {\n id\n workspaceId\n name\n description\n type\n isPublic\n ownerId\n createdAt\n updatedAt\n items {\n id\n title\n description\n status\n startDate\n endDate\n color\n swimlane\n sortOrder\n }\n }\n }\n`;\n\nconst LIST_FEATURE_REQUESTS_FOR_PRODUCT = gql`\n query ListFeatureRequestsForProduct($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n title\n description\n status\n priorityScore\n voteCount\n businessValue\n createdAt\n }\n totalCount\n }\n }\n`;\n\nconst LIST_RELEASES_FOR_PRODUCT = gql`\n query ListReleasesForProduct($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetReleases(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n name\n version\n status\n plannedDate\n }\n totalCount\n }\n }\n`;\n\ninterface RoadmapItemNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n startDate: string | null;\n endDate: string | null;\n}\n\ninterface FeatureRequestNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n priorityScore: number | null;\n voteCount: number;\n businessValue: number | null;\n}\n\ninterface ReleaseNode {\n id: string;\n name: string;\n version: string;\n status: string;\n plannedDate: string | null;\n}\n\nexport function ProductsProductDetailPagePage() {\n const { workspaceId } = usePlanMagnet();\n const params = useParams<{ id: string }>();\n const roadmapId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_ROADMAP, {\n variables: { id: roadmapId },\n skip: !roadmapId,\n });\n\n const {\n data: featuresData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_FEATURE_REQUESTS_FOR_PRODUCT, {\n variables: { workspaceId, pagination: { first: 20 } },\n skip: !workspaceId,\n });\n\n const {\n data: releasesData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_RELEASES_FOR_PRODUCT, {\n variables: { workspaceId, pagination: { first: 20 } },\n skip: !workspaceId,\n });\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load product: {error.message}</div>;\n }\n\n const roadmap = data?.planmagnetRoadmap;\n if (!roadmap) return <div className=\"p-6 text-muted-foreground\">Product not found.</div>;\n\n const roadmapItems: RoadmapItemNode[] = roadmap.items ?? [];\n const features: FeatureRequestNode[] = featuresData?.featureRequests?.items ?? [];\n const releases: ReleaseNode[] = releasesData?.releases?.items ?? [];\n const totalFeatures = featuresData?.featureRequests?.totalCount ?? 0;\n const totalReleases = releasesData?.releases?.totalCount ?? 0;\n\n const typeColor: Record<string, string> = {\n TIMELINE: 'bg-green-500',\n GANTT: 'bg-blue-500',\n SWIMLANE: 'bg-yellow-500',\n CUSTOM: 'bg-purple-500',\n };\n\n const statusColor: Record<string, string> = {\n SUBMITTED: 'bg-gray-100 text-gray-800',\n UNDER_REVIEW: 'bg-yellow-100 text-yellow-800',\n PLANNED: 'bg-blue-100 text-blue-800',\n IN_PROGRESS: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-green-100 text-green-800',\n DECLINED: 'bg-red-100 text-red-800',\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className={`h-3 w-3 rounded-full ${typeColor[roadmap.type] ?? 'bg-gray-400'}`} />\n <div>\n <h1 className=\"text-2xl font-semibold\">{roadmap.name}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {roadmap.description ?? 'No description'}\n </p>\n </div>\n </div>\n <Badge className=\"capitalize\">{roadmap.type.toLowerCase()}</Badge>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {[\n { label: 'Roadmap Items', value: roadmapItems.length, icon: Star },\n { label: 'Releases', value: totalReleases, icon: GitBranch },\n { label: 'Features', value: totalFeatures, icon: Map },\n { label: 'Owner', value: roadmap.ownerId, icon: Users },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-sm\">\n <s.icon className=\"h-4 w-4\" />\n {s.label}\n </div>\n <p className=\"text-xl font-semibold mt-1\">{s.value}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Feature Requests ({features.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {features.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No feature requests yet.</p>\n )}\n {features.map((f) => (\n <div key={f.id} className=\"flex items-center justify-between rounded-md border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{f.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n Votes: {f.voteCount} &middot; Business Value: {f.businessValue ?? 'N/A'}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\" className=\"capitalize text-xs\">\n {f.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Roadmap Items ({roadmapItems.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {roadmapItems.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No roadmap items linked.</p>\n )}\n {roadmapItems.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between rounded-md border p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">{item.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n {item.startDate\n ? new Date(item.startDate).toLocaleDateString()\n : 'No start date'}\n {' - '}\n {item.endDate ? new Date(item.endDate).toLocaleDateString() : 'No end date'}\n </p>\n </div>\n <Badge variant=\"outline\" className=\"capitalize text-xs\">\n {item.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Recent Releases</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {releases.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No releases yet.</p>\n )}\n {releases.map((r) => (\n <div key={r.id} className=\"border-b pb-2 last:border-0 last:pb-0\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium\">{r.version}</span>\n <Badge variant=\"outline\" className=\"text-xs capitalize\">\n {r.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n <p className=\"text-xs text-muted-foreground\">{r.name}</p>\n <div className=\"flex items-center gap-1 mt-1 text-xs text-muted-foreground\">\n <Calendar className=\"h-3 w-3\" />\n {r.plannedDate ? new Date(r.plannedDate).toLocaleDateString() : 'No date set'}\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(roadmap.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(roadmap.updatedAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Owner</span>\n <span>{roadmap.ownerId}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Visibility</span>\n <span>{roadmap.isPublic ? 'Public' : 'Private'}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsProductDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Package, GitBranch, Map, Star, Users, Calendar, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router-dom';\n\nconst GET_ROADMAP = gql`\n query GetRoadmapDetail($id: ID!) {\n planmagnetRoadmap(id: $id) {\n id\n workspaceId\n name\n description\n type\n isPublic\n ownerId\n createdAt\n updatedAt\n items {\n id\n title\n description\n status\n startDate\n endDate\n color\n swimlane\n sortOrder\n }\n }\n }\n`;\n\nconst LIST_FEATURE_REQUESTS_FOR_PRODUCT = gql`\n query ListFeatureRequestsForProduct($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n title\n description\n status\n priorityScore\n voteCount\n businessValue\n createdAt\n }\n totalCount\n }\n }\n`;\n\nconst LIST_RELEASES_FOR_PRODUCT = gql`\n query ListReleasesForProduct($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetReleases(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n name\n version\n status\n plannedDate\n }\n totalCount\n }\n }\n`;\n\ninterface RoadmapItemNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n startDate: string | null;\n endDate: string | null;\n}\n\ninterface FeatureRequestNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n priorityScore: number | null;\n voteCount: number;\n businessValue: number | null;\n}\n\ninterface ReleaseNode {\n id: string;\n name: string;\n version: string;\n status: string;\n plannedDate: string | null;\n}\n\nexport function ProductsProductDetailPagePage() {\n const { workspaceId } = usePlanMagnet();\n const params = useParams<{ id: string }>();\n const roadmapId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_ROADMAP, {\n variables: { id: roadmapId },\n skip: !roadmapId,\n });\n\n const {\n data: featuresData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_FEATURE_REQUESTS_FOR_PRODUCT, {\n variables: { workspaceId, pagination: { first: 20 } },\n skip: !workspaceId,\n });\n\n const {\n data: releasesData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_RELEASES_FOR_PRODUCT, {\n variables: { workspaceId, pagination: { first: 20 } },\n skip: !workspaceId,\n });\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load product: {error.message}</div>;\n }\n\n const roadmap = data?.planmagnetRoadmap;\n if (!roadmap) return <div className=\"p-6 text-muted-foreground\">Product not found.</div>;\n\n const roadmapItems: RoadmapItemNode[] = roadmap.items ?? [];\n const features: FeatureRequestNode[] = featuresData?.planmagnetFeatureRequests?.items ?? [];\n const releases: ReleaseNode[] = releasesData?.planmagnetReleases?.items ?? [];\n const totalFeatures = featuresData?.planmagnetFeatureRequests?.totalCount ?? 0;\n const totalReleases = releasesData?.planmagnetReleases?.totalCount ?? 0;\n\n const typeColor: Record<string, string> = {\n TIMELINE: 'bg-green-500',\n GANTT: 'bg-blue-500',\n SWIMLANE: 'bg-yellow-500',\n CUSTOM: 'bg-purple-500',\n };\n\n const statusColor: Record<string, string> = {\n SUBMITTED: 'bg-gray-100 text-gray-800',\n UNDER_REVIEW: 'bg-yellow-100 text-yellow-800',\n PLANNED: 'bg-blue-100 text-blue-800',\n IN_PROGRESS: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-green-100 text-green-800',\n DECLINED: 'bg-red-100 text-red-800',\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className={`h-3 w-3 rounded-full ${typeColor[roadmap.type] ?? 'bg-gray-400'}`} />\n <div>\n <h1 className=\"text-2xl font-semibold\">{roadmap.name}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {roadmap.description ?? 'No description'}\n </p>\n </div>\n </div>\n <Badge className=\"capitalize\">{roadmap.type.toLowerCase()}</Badge>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {[\n { label: 'Roadmap Items', value: roadmapItems.length, icon: Star },\n { label: 'Releases', value: totalReleases, icon: GitBranch },\n { label: 'Features', value: totalFeatures, icon: Map },\n { label: 'Owner', value: roadmap.ownerId, icon: Users },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-sm\">\n <s.icon className=\"h-4 w-4\" />\n {s.label}\n </div>\n <p className=\"text-xl font-semibold mt-1\">{s.value}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Feature Requests ({features.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {features.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No feature requests yet.</p>\n )}\n {features.map((f) => (\n <div key={f.id} className=\"flex items-center justify-between rounded-md border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{f.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n Votes: {f.voteCount} &middot; Business Value: {f.businessValue ?? 'N/A'}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\" className=\"capitalize text-xs\">\n {f.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Roadmap Items ({roadmapItems.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {roadmapItems.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No roadmap items linked.</p>\n )}\n {roadmapItems.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between rounded-md border p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">{item.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n {item.startDate\n ? new Date(item.startDate).toLocaleDateString()\n : 'No start date'}\n {' - '}\n {item.endDate ? new Date(item.endDate).toLocaleDateString() : 'No end date'}\n </p>\n </div>\n <Badge variant=\"outline\" className=\"capitalize text-xs\">\n {item.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Recent Releases</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {releases.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No releases yet.</p>\n )}\n {releases.map((r) => (\n <div key={r.id} className=\"border-b pb-2 last:border-0 last:pb-0\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium\">{r.version}</span>\n <Badge variant=\"outline\" className=\"text-xs capitalize\">\n {r.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n <p className=\"text-xs text-muted-foreground\">{r.name}</p>\n <div className=\"flex items-center gap-1 mt-1 text-xs text-muted-foreground\">\n <Calendar className=\"h-3 w-3\" />\n {r.plannedDate ? new Date(r.plannedDate).toLocaleDateString() : 'No date set'}\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(roadmap.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(roadmap.updatedAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Owner</span>\n <span>{roadmap.ownerId}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Visibility</span>\n <span>{roadmap.isPublic ? 'Public' : 'Private'}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -76,7 +76,7 @@ function S() {
76
76
  D.message
77
77
  ]
78
78
  });
79
- let A = T?.roadmaps?.items ?? [], j = T?.roadmaps?.totalCount ?? 0, M = O?.featureRequests?.totalCount ?? 0, N = k?.releases?.totalCount ?? 0, P = (e) => {
79
+ let A = T?.planmagnetRoadmaps?.items ?? [], j = T?.planmagnetRoadmaps?.totalCount ?? 0, M = O?.planmagnetFeatureRequests?.totalCount ?? 0, N = k?.planmagnetReleases?.totalCount ?? 0, P = (e) => {
80
80
  switch (e) {
81
81
  case "TIMELINE": return "default";
82
82
  case "GANTT": return "secondary";
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsProductsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Package, Plus, TrendingUp, Calendar, GitBranch, Map, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst LIST_ROADMAPS = gql`\n query ListRoadmaps($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetRoadmaps(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n workspaceId\n name\n description\n type\n isPublic\n ownerId\n metadata\n createdBy\n createdAt\n updatedAt\n updatedBy\n items {\n id\n status\n }\n }\n totalCount\n }\n }\n`;\n\nconst LIST_FEATURE_REQUESTS = gql`\n query ListFeatureRequestsForProducts($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {\n totalCount\n }\n }\n`;\n\nconst LIST_RELEASES = gql`\n query ListReleasesForProducts($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetReleases(workspaceId: $workspaceId, pagination: $pagination) {\n totalCount\n }\n }\n`;\n\ninterface RoadmapNode {\n id: string;\n name: string;\n description: string | null;\n type: string;\n isPublic: boolean;\n ownerId: string;\n createdAt: string;\n updatedAt: string;\n items: { id: string; status: string }[];\n}\n\nexport function ProductsProductsPagePage() {\n const { workspaceId } = usePlanMagnet();\n const { t } = useI18n();\n const [view, setView] = useState<'grid' | 'list'>('grid');\n\n const {\n data: roadmapsData,\n loading: roadmapsLoading,\n error: roadmapsError,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } = useQuery<Record<string, any>>(LIST_ROADMAPS, {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n variables: { workspaceId, pagination: { first: 50 } },\n skip: !workspaceId,\n });\n\n const {\n data: featuresData,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_FEATURE_REQUESTS, {\n variables: { workspaceId, pagination: { first: 1 } },\n skip: !workspaceId,\n });\n\n const {\n data: releasesData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_RELEASES, {\n variables: { workspaceId, pagination: { first: 1 } },\n skip: !workspaceId,\n });\n\n if (roadmapsLoading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (roadmapsError) {\n return (\n <div className=\"p-6 text-destructive\">\n {t('planmagnet.products.errorLoading', 'Failed to load products')}: {roadmapsError.message}\n </div>\n );\n }\n\n const roadmaps: RoadmapNode[] = roadmapsData?.roadmaps?.items ?? [];\n const totalRoadmaps = roadmapsData?.roadmaps?.totalCount ?? 0;\n const totalFeatures = featuresData?.featureRequests?.totalCount ?? 0;\n const totalReleases = releasesData?.releases?.totalCount ?? 0;\n\n const typeLabel = (t: string) => {\n switch (t) {\n case 'TIMELINE':\n return 'default';\n case 'GANTT':\n return 'secondary';\n case 'SWIMLANE':\n return 'outline';\n default:\n return 'outline';\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold flex items-center gap-2\">\n <Package className=\"h-6 w-6\" /> {t('planmagnet.products.title', 'Products')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {t(\n 'planmagnet.products.subtitle',\n 'Manage your product portfolio and track feature development'\n )}\n </p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"h-4 w-4 mr-1\" />\n {t('planmagnet.products.actions.newProduct', 'New Product')}\n </Button>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n {[\n {\n label: t('planmagnet.products.metrics.totalProducts', 'Total Products'),\n val: totalRoadmaps,\n icon: Package,\n color: 'text-blue-500',\n },\n {\n label: t('planmagnet.products.metrics.activeFeatures', 'Active Features'),\n val: totalFeatures,\n icon: GitBranch,\n color: 'text-green-500',\n },\n {\n label: t('planmagnet.products.metrics.releases', 'Releases'),\n val: totalReleases,\n icon: Calendar,\n color: 'text-purple-500',\n },\n {\n label: t('planmagnet.products.metrics.roadmaps', 'Roadmaps'),\n val: totalRoadmaps,\n icon: Map,\n color: 'text-orange-500',\n },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm text-muted-foreground\">{s.label}</p>\n <p className=\"text-2xl font-bold\">{s.val}</p>\n </div>\n <s.icon className={`h-8 w-8 ${s.color} opacity-60`} />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-2\">\n {roadmaps.map((roadmap) => (\n <Card key={roadmap.id} className=\"hover:shadow-md transition-shadow\">\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{roadmap.name}</CardTitle>\n <Badge variant={typeLabel(roadmap.type)}>{roadmap.type.toLowerCase()}</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {roadmap.description ??\n t('planmagnet.products.shared.noDescription', 'No description')}\n </p>\n <div className=\"grid grid-cols-3 gap-2 text-center\">\n <div className=\"rounded border p-2\">\n <p className=\"text-lg font-bold\">{roadmap.items?.length ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">\n {t('planmagnet.products.shared.items', 'Items')}\n </p>\n </div>\n <div className=\"rounded border p-2\">\n <p className=\"text-lg font-bold\">\n {roadmap.isPublic\n ? t('planmagnet.products.shared.public', 'Public')\n : t('planmagnet.products.shared.private', 'Private')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {t('planmagnet.products.shared.visibility', 'Visibility')}\n </p>\n </div>\n <div className=\"rounded border p-2\">\n <p className=\"text-lg font-bold\">{roadmap.type.toLowerCase()}</p>\n <p className=\"text-xs text-muted-foreground\">\n {t('planmagnet.products.shared.type', 'Type')}\n </p>\n </div>\n </div>\n <div className=\"flex items-center justify-between text-xs text-muted-foreground\">\n <span>\n {t('planmagnet.products.shared.owner', 'Owner')}: {roadmap.ownerId}\n </span>\n <span>\n {t('planmagnet.products.shared.updated', 'Updated')}{' '}\n {new Date(roadmap.updatedAt).toLocaleDateString()}\n </span>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsProductsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Package, Plus, TrendingUp, Calendar, GitBranch, Map, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst LIST_ROADMAPS = gql`\n query ListRoadmaps($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetRoadmaps(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n workspaceId\n name\n description\n type\n isPublic\n ownerId\n metadata\n createdBy\n createdAt\n updatedAt\n updatedBy\n items {\n id\n status\n }\n }\n totalCount\n }\n }\n`;\n\nconst LIST_FEATURE_REQUESTS = gql`\n query ListFeatureRequestsForProducts($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {\n totalCount\n }\n }\n`;\n\nconst LIST_RELEASES = gql`\n query ListReleasesForProducts($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetReleases(workspaceId: $workspaceId, pagination: $pagination) {\n totalCount\n }\n }\n`;\n\ninterface RoadmapNode {\n id: string;\n name: string;\n description: string | null;\n type: string;\n isPublic: boolean;\n ownerId: string;\n createdAt: string;\n updatedAt: string;\n items: { id: string; status: string }[];\n}\n\nexport function ProductsProductsPagePage() {\n const { workspaceId } = usePlanMagnet();\n const { t } = useI18n();\n const [view, setView] = useState<'grid' | 'list'>('grid');\n\n const {\n data: roadmapsData,\n loading: roadmapsLoading,\n error: roadmapsError,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } = useQuery<Record<string, any>>(LIST_ROADMAPS, {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n variables: { workspaceId, pagination: { first: 50 } },\n skip: !workspaceId,\n });\n\n const {\n data: featuresData,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_FEATURE_REQUESTS, {\n variables: { workspaceId, pagination: { first: 1 } },\n skip: !workspaceId,\n });\n\n const {\n data: releasesData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_RELEASES, {\n variables: { workspaceId, pagination: { first: 1 } },\n skip: !workspaceId,\n });\n\n if (roadmapsLoading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (roadmapsError) {\n return (\n <div className=\"p-6 text-destructive\">\n {t('planmagnet.products.errorLoading', 'Failed to load products')}: {roadmapsError.message}\n </div>\n );\n }\n\n const roadmaps: RoadmapNode[] = roadmapsData?.planmagnetRoadmaps?.items ?? [];\n const totalRoadmaps = roadmapsData?.planmagnetRoadmaps?.totalCount ?? 0;\n const totalFeatures = featuresData?.planmagnetFeatureRequests?.totalCount ?? 0;\n const totalReleases = releasesData?.planmagnetReleases?.totalCount ?? 0;\n\n const typeLabel = (t: string) => {\n switch (t) {\n case 'TIMELINE':\n return 'default';\n case 'GANTT':\n return 'secondary';\n case 'SWIMLANE':\n return 'outline';\n default:\n return 'outline';\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold flex items-center gap-2\">\n <Package className=\"h-6 w-6\" /> {t('planmagnet.products.title', 'Products')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {t(\n 'planmagnet.products.subtitle',\n 'Manage your product portfolio and track feature development'\n )}\n </p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"h-4 w-4 mr-1\" />\n {t('planmagnet.products.actions.newProduct', 'New Product')}\n </Button>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n {[\n {\n label: t('planmagnet.products.metrics.totalProducts', 'Total Products'),\n val: totalRoadmaps,\n icon: Package,\n color: 'text-blue-500',\n },\n {\n label: t('planmagnet.products.metrics.activeFeatures', 'Active Features'),\n val: totalFeatures,\n icon: GitBranch,\n color: 'text-green-500',\n },\n {\n label: t('planmagnet.products.metrics.releases', 'Releases'),\n val: totalReleases,\n icon: Calendar,\n color: 'text-purple-500',\n },\n {\n label: t('planmagnet.products.metrics.roadmaps', 'Roadmaps'),\n val: totalRoadmaps,\n icon: Map,\n color: 'text-orange-500',\n },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm text-muted-foreground\">{s.label}</p>\n <p className=\"text-2xl font-bold\">{s.val}</p>\n </div>\n <s.icon className={`h-8 w-8 ${s.color} opacity-60`} />\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-2\">\n {roadmaps.map((roadmap) => (\n <Card key={roadmap.id} className=\"hover:shadow-md transition-shadow\">\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{roadmap.name}</CardTitle>\n <Badge variant={typeLabel(roadmap.type)}>{roadmap.type.toLowerCase()}</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {roadmap.description ??\n t('planmagnet.products.shared.noDescription', 'No description')}\n </p>\n <div className=\"grid grid-cols-3 gap-2 text-center\">\n <div className=\"rounded border p-2\">\n <p className=\"text-lg font-bold\">{roadmap.items?.length ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">\n {t('planmagnet.products.shared.items', 'Items')}\n </p>\n </div>\n <div className=\"rounded border p-2\">\n <p className=\"text-lg font-bold\">\n {roadmap.isPublic\n ? t('planmagnet.products.shared.public', 'Public')\n : t('planmagnet.products.shared.private', 'Private')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {t('planmagnet.products.shared.visibility', 'Visibility')}\n </p>\n </div>\n <div className=\"rounded border p-2\">\n <p className=\"text-lg font-bold\">{roadmap.type.toLowerCase()}</p>\n <p className=\"text-xs text-muted-foreground\">\n {t('planmagnet.products.shared.type', 'Type')}\n </p>\n </div>\n </div>\n <div className=\"flex items-center justify-between text-xs text-muted-foreground\">\n <span>\n {t('planmagnet.products.shared.owner', 'Owner')}: {roadmap.ownerId}\n </span>\n <span>\n {t('planmagnet.products.shared.updated', 'Updated')}{' '}\n {new Date(roadmap.updatedAt).toLocaleDateString()}\n </span>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -9,7 +9,7 @@ import { Badge as u, Button as d, Card as f, CardContent as p, CardHeader as m,
9
9
  var g = o`
10
10
  query ListReleases(
11
11
  $workspaceId: ID!
12
- $filter: ReleaseFilter
12
+ $filter: PlanmagnetReleaseFilter
13
13
  $pagination: PlanmagnetPaginationInput
14
14
  ) {
15
15
  planmagnetReleases(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsReleasesPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsReleasesPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { GitBranch, Calendar, Package, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst LIST_RELEASES = gql`\n query ListReleases(\n $workspaceId: ID!\n $filter: ReleaseFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetReleases(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n workspaceId\n projectId\n name\n description\n version\n status\n plannedDate\n releasedAt\n releaseNotes\n ownerId\n tags\n createdAt\n updatedAt\n }\n totalCount\n }\n }\n`;\n\ninterface ReleaseNode {\n id: string;\n name: string;\n description: string | null;\n version: string;\n status: string;\n plannedDate: string | null;\n releasedAt: string | null;\n releaseNotes: string | null;\n ownerId: string;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nconst statusTabs = [\n 'all',\n 'DRAFT',\n 'PLANNING',\n 'IN_PROGRESS',\n 'READY',\n 'RELEASED',\n 'CANCELLED',\n] as const;\nconst statusColor: Record<string, string> = {\n DRAFT: 'bg-gray-100 text-gray-800',\n PLANNING: 'bg-yellow-100 text-yellow-800',\n IN_PROGRESS: 'bg-blue-100 text-blue-800',\n READY: 'bg-orange-100 text-orange-800',\n RELEASED: 'bg-green-100 text-green-800',\n CANCELLED: 'bg-red-100 text-red-800',\n ROLLED_BACK: 'bg-red-100 text-red-800',\n};\n\nexport function ProductsReleasesPagePage() {\n const { workspaceId } = usePlanMagnet();\n const [activeTab, setActiveTab] = useState<string>('all');\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_RELEASES, {\n variables: {\n workspaceId,\n filter: activeTab !== 'all' ? { status: activeTab } : undefined,\n pagination: { first: 50 },\n },\n skip: !workspaceId,\n });\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load releases: {error.message}</div>;\n }\n\n const releases: ReleaseNode[] = data?.planmagnetReleases?.items ?? [];\n const totalCount = data?.planmagnetReleases?.totalCount ?? 0;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Releases</h1>\n <p className=\"text-sm text-muted-foreground\">{totalCount} releases across all products</p>\n </div>\n <Button size=\"sm\">\n <GitBranch className=\"mr-1 h-4 w-4\" />\n New Release\n </Button>\n </div>\n\n <div className=\"flex gap-1 border-b pb-1\">\n {statusTabs.map((tab) => (\n <button\n key={tab}\n onClick={() => setActiveTab(tab)}\n className={`px-3 py-1.5 text-sm rounded-md capitalize ${activeTab === tab ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted'}`}\n >\n {tab === 'all' ? 'All' : tab.toLowerCase().replace('_', ' ')}\n </button>\n ))}\n </div>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {releases.map((release) => (\n <Card key={release.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-lg font-semibold\">{release.version}</span>\n </div>\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${statusColor[release.status] ?? 'bg-gray-100'}`}\n >\n {release.status.toLowerCase().replace('_', ' ')}\n </span>\n </div>\n <CardTitle className=\"text-sm font-medium\">{release.name}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {release.description ?? 'No description'}\n </p>\n\n <div className=\"flex items-center gap-4 text-sm\">\n <div className=\"flex items-center gap-1 text-muted-foreground\">\n <Package className=\"h-3.5 w-3.5\" />\n Owner: {release.ownerId}\n </div>\n <div className=\"flex items-center gap-1 text-muted-foreground\">\n <Calendar className=\"h-3.5 w-3.5\" />\n {release.plannedDate\n ? new Date(release.plannedDate).toLocaleDateString()\n : 'No date'}\n </div>\n </div>\n\n {release.tags.length > 0 && (\n <div className=\"flex gap-1 flex-wrap\">\n {release.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n\n {release.releaseNotes && (\n <div className=\"border-t pt-2\">\n <p className=\"text-xs font-medium text-muted-foreground mb-1\">\n Release Notes Preview\n </p>\n <p className=\"text-xs text-muted-foreground line-clamp-3\">\n {release.releaseNotes}\n </p>\n </div>\n )}\n </CardContent>\n </Card>\n ))}\n </div>\n {releases.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">No releases match this filter.</p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsReleasesPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsReleasesPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { GitBranch, Calendar, Package, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst LIST_RELEASES = gql`\n query ListReleases(\n $workspaceId: ID!\n $filter: PlanmagnetReleaseFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetReleases(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n workspaceId\n projectId\n name\n description\n version\n status\n plannedDate\n releasedAt\n releaseNotes\n ownerId\n tags\n createdAt\n updatedAt\n }\n totalCount\n }\n }\n`;\n\ninterface ReleaseNode {\n id: string;\n name: string;\n description: string | null;\n version: string;\n status: string;\n plannedDate: string | null;\n releasedAt: string | null;\n releaseNotes: string | null;\n ownerId: string;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nconst statusTabs = [\n 'all',\n 'DRAFT',\n 'PLANNING',\n 'IN_PROGRESS',\n 'READY',\n 'RELEASED',\n 'CANCELLED',\n] as const;\nconst statusColor: Record<string, string> = {\n DRAFT: 'bg-gray-100 text-gray-800',\n PLANNING: 'bg-yellow-100 text-yellow-800',\n IN_PROGRESS: 'bg-blue-100 text-blue-800',\n READY: 'bg-orange-100 text-orange-800',\n RELEASED: 'bg-green-100 text-green-800',\n CANCELLED: 'bg-red-100 text-red-800',\n ROLLED_BACK: 'bg-red-100 text-red-800',\n};\n\nexport function ProductsReleasesPagePage() {\n const { workspaceId } = usePlanMagnet();\n const [activeTab, setActiveTab] = useState<string>('all');\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_RELEASES, {\n variables: {\n workspaceId,\n filter: activeTab !== 'all' ? { status: activeTab } : undefined,\n pagination: { first: 50 },\n },\n skip: !workspaceId,\n });\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load releases: {error.message}</div>;\n }\n\n const releases: ReleaseNode[] = data?.planmagnetReleases?.items ?? [];\n const totalCount = data?.planmagnetReleases?.totalCount ?? 0;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Releases</h1>\n <p className=\"text-sm text-muted-foreground\">{totalCount} releases across all products</p>\n </div>\n <Button size=\"sm\">\n <GitBranch className=\"mr-1 h-4 w-4\" />\n New Release\n </Button>\n </div>\n\n <div className=\"flex gap-1 border-b pb-1\">\n {statusTabs.map((tab) => (\n <button\n key={tab}\n onClick={() => setActiveTab(tab)}\n className={`px-3 py-1.5 text-sm rounded-md capitalize ${activeTab === tab ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted'}`}\n >\n {tab === 'all' ? 'All' : tab.toLowerCase().replace('_', ' ')}\n </button>\n ))}\n </div>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {releases.map((release) => (\n <Card key={release.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-lg font-semibold\">{release.version}</span>\n </div>\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${statusColor[release.status] ?? 'bg-gray-100'}`}\n >\n {release.status.toLowerCase().replace('_', ' ')}\n </span>\n </div>\n <CardTitle className=\"text-sm font-medium\">{release.name}</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {release.description ?? 'No description'}\n </p>\n\n <div className=\"flex items-center gap-4 text-sm\">\n <div className=\"flex items-center gap-1 text-muted-foreground\">\n <Package className=\"h-3.5 w-3.5\" />\n Owner: {release.ownerId}\n </div>\n <div className=\"flex items-center gap-1 text-muted-foreground\">\n <Calendar className=\"h-3.5 w-3.5\" />\n {release.plannedDate\n ? new Date(release.plannedDate).toLocaleDateString()\n : 'No date'}\n </div>\n </div>\n\n {release.tags.length > 0 && (\n <div className=\"flex gap-1 flex-wrap\">\n {release.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n\n {release.releaseNotes && (\n <div className=\"border-t pt-2\">\n <p className=\"text-xs font-medium text-muted-foreground mb-1\">\n Release Notes Preview\n </p>\n <p className=\"text-xs text-muted-foreground line-clamp-3\">\n {release.releaseNotes}\n </p>\n </div>\n )}\n </CardContent>\n </Card>\n ))}\n </div>\n {releases.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">No releases match this filter.</p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -68,7 +68,7 @@ function v() {
68
68
  className: "p-6 text-muted-foreground",
69
69
  children: "Roadmap not found."
70
70
  });
71
- let w = C.items ?? [], T = S?.featureRequests?.items ?? [], E = w.filter((e) => e.status === "COMPLETED" || e.status === "DONE"), D = w.length > 0 ? Math.round(E.length / w.length * 100) : 0, O = {};
71
+ let w = C.items ?? [], T = S?.planmagnetFeatureRequests?.items ?? [], E = w.filter((e) => e.status === "COMPLETED" || e.status === "DONE"), D = w.length > 0 ? Math.round(E.length / w.length * 100) : 0, O = {};
72
72
  for (let e of w) O[e.status] = (O[e.status] || 0) + 1;
73
73
  let k = {};
74
74
  for (let e of T) k[e.status] = (k[e.status] || 0) + 1;