@burdenoff/microfe-planmagnet 2026.509.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/dev/main.d.ts +2 -0
- package/dist/dev/main.d.ts.map +1 -0
- package/dist/generated/global-operations.d.ts +2 -0
- package/dist/generated/global-operations.d.ts.map +1 -0
- package/dist/generated/global-types.d.ts +21253 -0
- package/dist/generated/global-types.d.ts.map +1 -0
- package/dist/generated/wspace-operations.d.ts +8370 -0
- package/dist/generated/wspace-operations.d.ts.map +1 -0
- package/dist/generated/wspace-types.d.ts +4182 -0
- package/dist/generated/wspace-types.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/node_modules/@apollo/client/errors/CombinedGraphQLErrors.js +24 -0
- package/dist/node_modules/@apollo/client/errors/CombinedGraphQLErrors.js.map +1 -0
- package/dist/node_modules/@apollo/client/errors/utils.js +16 -0
- package/dist/node_modules/@apollo/client/errors/utils.js.map +1 -0
- package/dist/planmagnet/PlanMagnetRoot.d.ts +5 -0
- package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -0
- package/dist/planmagnet/PlanMagnetRoot.js +292 -0
- package/dist/planmagnet/PlanMagnetRoot.js.map +1 -0
- package/dist/planmagnet/PlanMagnetRoutes.d.ts +2 -0
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -0
- package/dist/planmagnet/PlanMagnetRoutes.js +211 -0
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -0
- package/dist/planmagnet/index.d.ts +5 -0
- package/dist/planmagnet/index.d.ts.map +1 -0
- package/dist/planmagnet/index.js +3 -0
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +312 -0
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -0
- package/dist/planmagnet/pages/CollaborationCollaborationPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/CollaborationCollaborationPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/CollaborationCollaborationPagePage.js +110 -0
- package/dist/planmagnet/pages/CollaborationCollaborationPagePage.js.map +1 -0
- package/dist/planmagnet/pages/CollaborationCreateChannelPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/CollaborationCreateChannelPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/CollaborationCreateChannelPagePage.js +96 -0
- package/dist/planmagnet/pages/CollaborationCreateChannelPagePage.js.map +1 -0
- package/dist/planmagnet/pages/CollaborationFilesPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/CollaborationFilesPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/CollaborationFilesPagePage.js +112 -0
- package/dist/planmagnet/pages/CollaborationFilesPagePage.js.map +1 -0
- package/dist/planmagnet/pages/CollaborationMessagesPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/CollaborationMessagesPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/CollaborationMessagesPagePage.js +141 -0
- package/dist/planmagnet/pages/CollaborationMessagesPagePage.js.map +1 -0
- package/dist/planmagnet/pages/CollaborationNewDiscussionPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/CollaborationNewDiscussionPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/CollaborationNewDiscussionPagePage.js +124 -0
- package/dist/planmagnet/pages/CollaborationNewDiscussionPagePage.js.map +1 -0
- package/dist/planmagnet/pages/DashboardDashboardPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/DashboardDashboardPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js +350 -0
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js.map +1 -0
- package/dist/planmagnet/pages/MeetingsScheduleMeetingPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/MeetingsScheduleMeetingPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/MeetingsScheduleMeetingPagePage.js +290 -0
- package/dist/planmagnet/pages/MeetingsScheduleMeetingPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsCreateProductPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsCreateProductPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsCreateProductPagePage.js +189 -0
- package/dist/planmagnet/pages/ProductsCreateProductPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.js +151 -0
- package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js +257 -0
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.js +221 -0
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js +284 -0
- package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +291 -0
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +257 -0
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +222 -0
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +288 -0
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsProductsPagePage.js +225 -0
- package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +262 -0
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsReleasesPagePage.js +162 -0
- package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +342 -0
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +233 -0
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +193 -0
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js +116 -0
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.js +199 -0
- package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +197 -0
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +173 -0
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js +119 -0
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +257 -0
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +222 -0
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.js +142 -0
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +363 -0
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +176 -0
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ReportsCreateReportPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ReportsCreateReportPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ReportsCreateReportPagePage.js +173 -0
- package/dist/planmagnet/pages/ReportsCreateReportPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ReportsReportsPagePage.js +221 -0
- package/dist/planmagnet/pages/ReportsReportsPagePage.js.map +1 -0
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +282 -0
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +227 -0
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js +234 -0
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +232 -0
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TasksTasksPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TasksTasksPagePage.js +206 -0
- package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TeamCreateTeamPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TeamCreateTeamPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TeamCreateTeamPagePage.js +138 -0
- package/dist/planmagnet/pages/TeamCreateTeamPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TeamInviteMemberPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TeamInviteMemberPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TeamInviteMemberPagePage.js +161 -0
- package/dist/planmagnet/pages/TeamInviteMemberPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TeamTeamPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TeamTeamPagePage.js +203 -0
- package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -0
- package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.d.ts +2 -0
- package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.js +97 -0
- package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.js.map +1 -0
- package/dist/planmagnet/providers/PlanMagnetProvider.d.ts +31 -0
- package/dist/planmagnet/providers/PlanMagnetProvider.d.ts.map +1 -0
- package/dist/planmagnet/providers/PlanMagnetProvider.js +119 -0
- package/dist/planmagnet/providers/PlanMagnetProvider.js.map +1 -0
- package/dist/planmagnet/store/planmagnetStore.d.ts +30 -0
- package/dist/planmagnet/store/planmagnetStore.d.ts.map +1 -0
- package/dist/planmagnet/store/planmagnetStore.js +42 -0
- package/dist/planmagnet/store/planmagnetStore.js.map +1 -0
- package/dist/planmagnet/types/index.d.ts +317 -0
- package/dist/planmagnet/types/index.d.ts.map +1 -0
- package/dist/planmagnet/utils/cn.d.ts +4 -0
- package/dist/planmagnet/utils/cn.d.ts.map +1 -0
- package/dist/planmagnet/utils/navigation.d.ts +2 -0
- package/dist/planmagnet/utils/navigation.d.ts.map +1 -0
- package/dist/planmagnet/utils/navigation.js +8 -0
- package/dist/planmagnet/utils/navigation.js.map +1 -0
- package/package.json +107 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { useState as t } from "react";
|
|
3
|
+
import { Calendar as n, GitBranch as r, Loader2 as i, Package as a } from "lucide-react";
|
|
4
|
+
import { gql as o } from "@apollo/client";
|
|
5
|
+
import { useQuery as s } from "@apollo/client/react";
|
|
6
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
7
|
+
import { Badge as u, Button as d, Card as f, CardContent as p, CardHeader as m, CardTitle as h } from "@burdenoff/fe-libs/ui";
|
|
8
|
+
//#region src/planmagnet/pages/ProductsReleasesPagePage.tsx
|
|
9
|
+
var g = o`
|
|
10
|
+
query ListReleases($workspaceId: ID!, $filter: ReleaseFilter, $pagination: PaginationInput) {
|
|
11
|
+
planmagnetReleases(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {
|
|
12
|
+
items {
|
|
13
|
+
id
|
|
14
|
+
workspaceId
|
|
15
|
+
projectId
|
|
16
|
+
name
|
|
17
|
+
description
|
|
18
|
+
version
|
|
19
|
+
status
|
|
20
|
+
plannedDate
|
|
21
|
+
releasedAt
|
|
22
|
+
releaseNotes
|
|
23
|
+
ownerId
|
|
24
|
+
tags
|
|
25
|
+
createdAt
|
|
26
|
+
updatedAt
|
|
27
|
+
}
|
|
28
|
+
totalCount
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
`, _ = [
|
|
32
|
+
"all",
|
|
33
|
+
"DRAFT",
|
|
34
|
+
"PLANNING",
|
|
35
|
+
"IN_PROGRESS",
|
|
36
|
+
"READY",
|
|
37
|
+
"RELEASED",
|
|
38
|
+
"CANCELLED"
|
|
39
|
+
], v = {
|
|
40
|
+
DRAFT: "bg-gray-100 text-gray-800",
|
|
41
|
+
PLANNING: "bg-yellow-100 text-yellow-800",
|
|
42
|
+
IN_PROGRESS: "bg-blue-100 text-blue-800",
|
|
43
|
+
READY: "bg-orange-100 text-orange-800",
|
|
44
|
+
RELEASED: "bg-green-100 text-green-800",
|
|
45
|
+
CANCELLED: "bg-red-100 text-red-800",
|
|
46
|
+
ROLLED_BACK: "bg-red-100 text-red-800"
|
|
47
|
+
};
|
|
48
|
+
function y() {
|
|
49
|
+
let { workspaceId: o } = e(), [y, b] = t("all"), { data: x, loading: S, error: C } = s(g, {
|
|
50
|
+
variables: {
|
|
51
|
+
workspaceId: o,
|
|
52
|
+
filter: y === "all" ? void 0 : { status: y },
|
|
53
|
+
pagination: { first: 50 }
|
|
54
|
+
},
|
|
55
|
+
skip: !o
|
|
56
|
+
});
|
|
57
|
+
if (S) return /* @__PURE__ */ c("div", {
|
|
58
|
+
className: "flex items-center justify-center p-12",
|
|
59
|
+
children: /* @__PURE__ */ c(i, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
60
|
+
});
|
|
61
|
+
if (C) return /* @__PURE__ */ l("div", {
|
|
62
|
+
className: "p-6 text-destructive",
|
|
63
|
+
children: ["Failed to load releases: ", C.message]
|
|
64
|
+
});
|
|
65
|
+
let w = x?.planmagnetReleases?.items ?? [], T = x?.planmagnetReleases?.totalCount ?? 0;
|
|
66
|
+
return /* @__PURE__ */ l("div", {
|
|
67
|
+
className: "space-y-6 p-6",
|
|
68
|
+
children: [
|
|
69
|
+
/* @__PURE__ */ l("div", {
|
|
70
|
+
className: "flex items-center justify-between",
|
|
71
|
+
children: [/* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("h1", {
|
|
72
|
+
className: "text-2xl font-semibold",
|
|
73
|
+
children: "Releases"
|
|
74
|
+
}), /* @__PURE__ */ l("p", {
|
|
75
|
+
className: "text-sm text-muted-foreground",
|
|
76
|
+
children: [T, " releases across all products"]
|
|
77
|
+
})] }), /* @__PURE__ */ l(d, {
|
|
78
|
+
size: "sm",
|
|
79
|
+
children: [/* @__PURE__ */ c(r, { className: "mr-1 h-4 w-4" }), "New Release"]
|
|
80
|
+
})]
|
|
81
|
+
}),
|
|
82
|
+
/* @__PURE__ */ c("div", {
|
|
83
|
+
className: "flex gap-1 border-b pb-1",
|
|
84
|
+
children: _.map((e) => /* @__PURE__ */ c("button", {
|
|
85
|
+
onClick: () => b(e),
|
|
86
|
+
className: `px-3 py-1.5 text-sm rounded-md capitalize ${y === e ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"}`,
|
|
87
|
+
children: e === "all" ? "All" : e.toLowerCase().replace("_", " ")
|
|
88
|
+
}, e))
|
|
89
|
+
}),
|
|
90
|
+
/* @__PURE__ */ c("div", {
|
|
91
|
+
className: "grid grid-cols-1 md:grid-cols-2 gap-4",
|
|
92
|
+
children: w.map((e) => /* @__PURE__ */ l(f, { children: [/* @__PURE__ */ l(m, {
|
|
93
|
+
className: "pb-2",
|
|
94
|
+
children: [/* @__PURE__ */ l("div", {
|
|
95
|
+
className: "flex items-center justify-between",
|
|
96
|
+
children: [/* @__PURE__ */ c("div", {
|
|
97
|
+
className: "flex items-center gap-2",
|
|
98
|
+
children: /* @__PURE__ */ c("span", {
|
|
99
|
+
className: "text-lg font-semibold",
|
|
100
|
+
children: e.version
|
|
101
|
+
})
|
|
102
|
+
}), /* @__PURE__ */ c("span", {
|
|
103
|
+
className: `text-xs px-2 py-0.5 rounded-full capitalize ${v[e.status] ?? "bg-gray-100"}`,
|
|
104
|
+
children: e.status.toLowerCase().replace("_", " ")
|
|
105
|
+
})]
|
|
106
|
+
}), /* @__PURE__ */ c(h, {
|
|
107
|
+
className: "text-sm font-medium",
|
|
108
|
+
children: e.name
|
|
109
|
+
})]
|
|
110
|
+
}), /* @__PURE__ */ l(p, {
|
|
111
|
+
className: "space-y-3",
|
|
112
|
+
children: [
|
|
113
|
+
/* @__PURE__ */ c("p", {
|
|
114
|
+
className: "text-sm text-muted-foreground",
|
|
115
|
+
children: e.description ?? "No description"
|
|
116
|
+
}),
|
|
117
|
+
/* @__PURE__ */ l("div", {
|
|
118
|
+
className: "flex items-center gap-4 text-sm",
|
|
119
|
+
children: [/* @__PURE__ */ l("div", {
|
|
120
|
+
className: "flex items-center gap-1 text-muted-foreground",
|
|
121
|
+
children: [
|
|
122
|
+
/* @__PURE__ */ c(a, { className: "h-3.5 w-3.5" }),
|
|
123
|
+
"Owner: ",
|
|
124
|
+
e.ownerId
|
|
125
|
+
]
|
|
126
|
+
}), /* @__PURE__ */ l("div", {
|
|
127
|
+
className: "flex items-center gap-1 text-muted-foreground",
|
|
128
|
+
children: [/* @__PURE__ */ c(n, { className: "h-3.5 w-3.5" }), e.plannedDate ? new Date(e.plannedDate).toLocaleDateString() : "No date"]
|
|
129
|
+
})]
|
|
130
|
+
}),
|
|
131
|
+
e.tags.length > 0 && /* @__PURE__ */ c("div", {
|
|
132
|
+
className: "flex gap-1 flex-wrap",
|
|
133
|
+
children: e.tags.map((e) => /* @__PURE__ */ c(u, {
|
|
134
|
+
variant: "outline",
|
|
135
|
+
className: "text-xs",
|
|
136
|
+
children: e
|
|
137
|
+
}, e))
|
|
138
|
+
}),
|
|
139
|
+
e.releaseNotes && /* @__PURE__ */ l("div", {
|
|
140
|
+
className: "border-t pt-2",
|
|
141
|
+
children: [/* @__PURE__ */ c("p", {
|
|
142
|
+
className: "text-xs font-medium text-muted-foreground mb-1",
|
|
143
|
+
children: "Release Notes Preview"
|
|
144
|
+
}), /* @__PURE__ */ c("p", {
|
|
145
|
+
className: "text-xs text-muted-foreground line-clamp-3",
|
|
146
|
+
children: e.releaseNotes
|
|
147
|
+
})]
|
|
148
|
+
})
|
|
149
|
+
]
|
|
150
|
+
})] }, e.id))
|
|
151
|
+
}),
|
|
152
|
+
w.length === 0 && /* @__PURE__ */ c("p", {
|
|
153
|
+
className: "text-center text-muted-foreground py-8",
|
|
154
|
+
children: "No releases match this filter."
|
|
155
|
+
})
|
|
156
|
+
]
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
//#endregion
|
|
160
|
+
export { y as ProductsReleasesPagePage };
|
|
161
|
+
|
|
162
|
+
//# sourceMappingURL=ProductsReleasesPagePage.js.map
|
|
@@ -0,0 +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($workspaceId: ID!, $filter: ReleaseFilter, $pagination: PaginationInput) {\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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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProductsRoadmapAnalyticsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.tsx"],"names":[],"mappings":"AAwDA,wBAAgB,gCAAgC,4CAuV/C"}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { AlertTriangle as t, BarChart3 as n, CheckCircle as r, Loader2 as i, Target as a, TrendingUp as o } from "lucide-react";
|
|
3
|
+
import { useParams as s } from "react-router-dom";
|
|
4
|
+
import { gql as c } from "@apollo/client";
|
|
5
|
+
import { useQuery as l } from "@apollo/client/react";
|
|
6
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
7
|
+
import { Card as f, CardContent as p, CardHeader as m, CardTitle as h } from "@burdenoff/fe-libs/ui";
|
|
8
|
+
//#region src/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.tsx
|
|
9
|
+
var g = c`
|
|
10
|
+
query GetRoadmapAnalytics($id: ID!) {
|
|
11
|
+
planmagnetRoadmap(id: $id) {
|
|
12
|
+
id
|
|
13
|
+
name
|
|
14
|
+
description
|
|
15
|
+
type
|
|
16
|
+
isPublic
|
|
17
|
+
ownerId
|
|
18
|
+
createdAt
|
|
19
|
+
updatedAt
|
|
20
|
+
items {
|
|
21
|
+
id
|
|
22
|
+
title
|
|
23
|
+
status
|
|
24
|
+
startDate
|
|
25
|
+
endDate
|
|
26
|
+
swimlane
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`, _ = c`
|
|
31
|
+
query ListFeatureRequestsForRoadmapAnalytics($workspaceId: ID!, $pagination: PaginationInput) {
|
|
32
|
+
planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {
|
|
33
|
+
items {
|
|
34
|
+
id
|
|
35
|
+
status
|
|
36
|
+
voteCount
|
|
37
|
+
businessValue
|
|
38
|
+
priorityScore
|
|
39
|
+
}
|
|
40
|
+
totalCount
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
function v() {
|
|
45
|
+
let { workspaceId: c } = e(), v = s().id ?? "", { data: y, loading: b, error: x } = l(g, {
|
|
46
|
+
variables: { id: v },
|
|
47
|
+
skip: !v
|
|
48
|
+
}), { data: S } = l(_, {
|
|
49
|
+
variables: {
|
|
50
|
+
workspaceId: c,
|
|
51
|
+
pagination: { first: 100 }
|
|
52
|
+
},
|
|
53
|
+
skip: !c
|
|
54
|
+
});
|
|
55
|
+
if (b) return /* @__PURE__ */ u("div", {
|
|
56
|
+
className: "flex items-center justify-center p-12",
|
|
57
|
+
children: /* @__PURE__ */ u(i, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
58
|
+
});
|
|
59
|
+
if (x) return /* @__PURE__ */ d("div", {
|
|
60
|
+
className: "p-6 text-destructive",
|
|
61
|
+
children: ["Failed to load analytics: ", x.message]
|
|
62
|
+
});
|
|
63
|
+
let C = y?.planmagnetRoadmap;
|
|
64
|
+
if (!C) return /* @__PURE__ */ u("div", {
|
|
65
|
+
className: "p-6 text-muted-foreground",
|
|
66
|
+
children: "Roadmap not found."
|
|
67
|
+
});
|
|
68
|
+
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 = {};
|
|
69
|
+
for (let e of w) O[e.status] = (O[e.status] || 0) + 1;
|
|
70
|
+
let k = {};
|
|
71
|
+
for (let e of T) k[e.status] = (k[e.status] || 0) + 1;
|
|
72
|
+
let A = {};
|
|
73
|
+
for (let e of w) {
|
|
74
|
+
let t = e.swimlane ?? "Uncategorized";
|
|
75
|
+
A[t] = (A[t] || 0) + 1;
|
|
76
|
+
}
|
|
77
|
+
let j = /* @__PURE__ */ new Date(), M = w.filter((e) => e.endDate && new Date(e.endDate) < j && e.status !== "COMPLETED" && e.status !== "DONE"), N = Math.max(0, Math.round(D * .7 + (1 - M.length / Math.max(w.length, 1)) * 30)), P = {};
|
|
78
|
+
for (let e of w) if (e.endDate) {
|
|
79
|
+
let t = new Date(e.endDate).toLocaleDateString("en-US", {
|
|
80
|
+
month: "short",
|
|
81
|
+
year: "2-digit"
|
|
82
|
+
});
|
|
83
|
+
P[t] || (P[t] = {
|
|
84
|
+
planned: 0,
|
|
85
|
+
completed: 0
|
|
86
|
+
}), P[t].planned += 1, (e.status === "COMPLETED" || e.status === "DONE") && (P[t].completed += 1);
|
|
87
|
+
}
|
|
88
|
+
let F = Object.entries(P).map(([e, t]) => ({
|
|
89
|
+
month: e,
|
|
90
|
+
...t
|
|
91
|
+
})).slice(-6), I = Math.max(...F.map((e) => Math.max(e.planned, e.completed)), 1);
|
|
92
|
+
return /* @__PURE__ */ d("div", {
|
|
93
|
+
className: "space-y-6 p-6",
|
|
94
|
+
children: [
|
|
95
|
+
/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("h1", {
|
|
96
|
+
className: "text-2xl font-semibold",
|
|
97
|
+
children: "Roadmap Analytics"
|
|
98
|
+
}), /* @__PURE__ */ d("p", {
|
|
99
|
+
className: "text-sm text-muted-foreground",
|
|
100
|
+
children: [
|
|
101
|
+
C.name,
|
|
102
|
+
" - ",
|
|
103
|
+
C.type.toLowerCase()
|
|
104
|
+
]
|
|
105
|
+
})] }),
|
|
106
|
+
/* @__PURE__ */ d("div", {
|
|
107
|
+
className: "grid grid-cols-2 md:grid-cols-4 gap-4",
|
|
108
|
+
children: [
|
|
109
|
+
/* @__PURE__ */ u(f, { children: /* @__PURE__ */ d(p, {
|
|
110
|
+
className: "pt-4",
|
|
111
|
+
children: [
|
|
112
|
+
/* @__PURE__ */ d("div", {
|
|
113
|
+
className: "flex items-center gap-2 text-muted-foreground text-xs",
|
|
114
|
+
children: [/* @__PURE__ */ u(a, { className: "h-4 w-4" }), "Health Score"]
|
|
115
|
+
}),
|
|
116
|
+
/* @__PURE__ */ d("p", {
|
|
117
|
+
className: "text-2xl font-bold mt-1",
|
|
118
|
+
children: [N, "/100"]
|
|
119
|
+
}),
|
|
120
|
+
/* @__PURE__ */ u("div", {
|
|
121
|
+
className: "w-full h-2 rounded-full bg-muted mt-2 overflow-hidden",
|
|
122
|
+
children: /* @__PURE__ */ u("div", {
|
|
123
|
+
className: "h-full rounded-full bg-green-500",
|
|
124
|
+
style: { width: `${N}%` }
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
]
|
|
128
|
+
}) }),
|
|
129
|
+
/* @__PURE__ */ u(f, { children: /* @__PURE__ */ d(p, {
|
|
130
|
+
className: "pt-4",
|
|
131
|
+
children: [
|
|
132
|
+
/* @__PURE__ */ d("div", {
|
|
133
|
+
className: "flex items-center gap-2 text-muted-foreground text-xs",
|
|
134
|
+
children: [/* @__PURE__ */ u(o, { className: "h-4 w-4" }), "Progress"]
|
|
135
|
+
}),
|
|
136
|
+
/* @__PURE__ */ d("p", {
|
|
137
|
+
className: "text-2xl font-bold mt-1",
|
|
138
|
+
children: [D, "%"]
|
|
139
|
+
}),
|
|
140
|
+
/* @__PURE__ */ d("p", {
|
|
141
|
+
className: "text-xs text-muted-foreground mt-1",
|
|
142
|
+
children: [w.length, " items total"]
|
|
143
|
+
})
|
|
144
|
+
]
|
|
145
|
+
}) }),
|
|
146
|
+
/* @__PURE__ */ u(f, { children: /* @__PURE__ */ d(p, {
|
|
147
|
+
className: "pt-4",
|
|
148
|
+
children: [
|
|
149
|
+
/* @__PURE__ */ d("div", {
|
|
150
|
+
className: "flex items-center gap-2 text-muted-foreground text-xs",
|
|
151
|
+
children: [/* @__PURE__ */ u(r, { className: "h-4 w-4" }), "Completed"]
|
|
152
|
+
}),
|
|
153
|
+
/* @__PURE__ */ u("p", {
|
|
154
|
+
className: "text-2xl font-bold mt-1",
|
|
155
|
+
children: E.length
|
|
156
|
+
}),
|
|
157
|
+
/* @__PURE__ */ u("p", {
|
|
158
|
+
className: "text-xs text-muted-foreground mt-1",
|
|
159
|
+
children: "items completed"
|
|
160
|
+
})
|
|
161
|
+
]
|
|
162
|
+
}) }),
|
|
163
|
+
/* @__PURE__ */ u(f, { children: /* @__PURE__ */ d(p, {
|
|
164
|
+
className: "pt-4",
|
|
165
|
+
children: [
|
|
166
|
+
/* @__PURE__ */ d("div", {
|
|
167
|
+
className: "flex items-center gap-2 text-muted-foreground text-xs",
|
|
168
|
+
children: [/* @__PURE__ */ u(t, { className: "h-4 w-4" }), "Overdue"]
|
|
169
|
+
}),
|
|
170
|
+
/* @__PURE__ */ u("p", {
|
|
171
|
+
className: "text-2xl font-bold mt-1",
|
|
172
|
+
children: M.length
|
|
173
|
+
}),
|
|
174
|
+
/* @__PURE__ */ u("p", {
|
|
175
|
+
className: "text-xs text-muted-foreground mt-1",
|
|
176
|
+
children: "items past end date"
|
|
177
|
+
})
|
|
178
|
+
]
|
|
179
|
+
}) })
|
|
180
|
+
]
|
|
181
|
+
}),
|
|
182
|
+
/* @__PURE__ */ d("div", {
|
|
183
|
+
className: "grid grid-cols-1 lg:grid-cols-2 gap-6",
|
|
184
|
+
children: [/* @__PURE__ */ d(f, { children: [/* @__PURE__ */ u(m, { children: /* @__PURE__ */ d(h, {
|
|
185
|
+
className: "flex items-center gap-2",
|
|
186
|
+
children: [/* @__PURE__ */ u(n, { className: "h-4 w-4" }), "Item Velocity"]
|
|
187
|
+
}) }), /* @__PURE__ */ d(p, { children: [F.length === 0 ? /* @__PURE__ */ u("p", {
|
|
188
|
+
className: "text-sm text-muted-foreground text-center py-8",
|
|
189
|
+
children: "No timeline data available"
|
|
190
|
+
}) : /* @__PURE__ */ u("div", {
|
|
191
|
+
className: "flex items-end gap-2 h-48",
|
|
192
|
+
children: F.map((e) => /* @__PURE__ */ d("div", {
|
|
193
|
+
className: "flex-1 flex flex-col items-center gap-1",
|
|
194
|
+
children: [/* @__PURE__ */ d("div", {
|
|
195
|
+
className: "flex gap-0.5 items-end w-full justify-center h-40",
|
|
196
|
+
children: [/* @__PURE__ */ u("div", {
|
|
197
|
+
className: "w-3 bg-primary/30 rounded-t",
|
|
198
|
+
style: { height: `${e.planned / I * 100}%` },
|
|
199
|
+
title: `Planned: ${e.planned}`
|
|
200
|
+
}), /* @__PURE__ */ u("div", {
|
|
201
|
+
className: "w-3 bg-primary rounded-t",
|
|
202
|
+
style: { height: `${e.completed / I * 100}%` },
|
|
203
|
+
title: `Completed: ${e.completed}`
|
|
204
|
+
})]
|
|
205
|
+
}), /* @__PURE__ */ u("span", {
|
|
206
|
+
className: "text-xs text-muted-foreground",
|
|
207
|
+
children: e.month
|
|
208
|
+
})]
|
|
209
|
+
}, e.month))
|
|
210
|
+
}), /* @__PURE__ */ d("div", {
|
|
211
|
+
className: "flex gap-4 justify-center mt-3 text-xs",
|
|
212
|
+
children: [/* @__PURE__ */ d("span", {
|
|
213
|
+
className: "flex items-center gap-1",
|
|
214
|
+
children: [/* @__PURE__ */ u("span", { className: "h-2 w-2 rounded bg-primary/30" }), "Planned"]
|
|
215
|
+
}), /* @__PURE__ */ d("span", {
|
|
216
|
+
className: "flex items-center gap-1",
|
|
217
|
+
children: [/* @__PURE__ */ u("span", { className: "h-2 w-2 rounded bg-primary" }), "Completed"]
|
|
218
|
+
})]
|
|
219
|
+
})] })] }), /* @__PURE__ */ d(f, { children: [/* @__PURE__ */ u(m, { children: /* @__PURE__ */ u(h, { children: "Item Status Breakdown" }) }), /* @__PURE__ */ u(p, {
|
|
220
|
+
className: "space-y-3",
|
|
221
|
+
children: Object.entries(O).map(([e, t]) => {
|
|
222
|
+
let n = w.length, r = n > 0 ? Math.round(t / n * 100) : 0;
|
|
223
|
+
return /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ d("div", {
|
|
224
|
+
className: "flex justify-between text-sm mb-1",
|
|
225
|
+
children: [/* @__PURE__ */ u("span", {
|
|
226
|
+
className: "capitalize",
|
|
227
|
+
children: e.toLowerCase().replace("_", " ")
|
|
228
|
+
}), /* @__PURE__ */ d("span", {
|
|
229
|
+
className: "font-medium",
|
|
230
|
+
children: [
|
|
231
|
+
t,
|
|
232
|
+
" (",
|
|
233
|
+
r,
|
|
234
|
+
"%)"
|
|
235
|
+
]
|
|
236
|
+
})]
|
|
237
|
+
}), /* @__PURE__ */ u("div", {
|
|
238
|
+
className: "w-full h-2 rounded-full bg-muted overflow-hidden",
|
|
239
|
+
children: /* @__PURE__ */ u("div", {
|
|
240
|
+
className: `h-full rounded-full ${{
|
|
241
|
+
TODO: "bg-gray-400",
|
|
242
|
+
PLANNED: "bg-yellow-400",
|
|
243
|
+
IN_PROGRESS: "bg-blue-400",
|
|
244
|
+
BLOCKED: "bg-red-400",
|
|
245
|
+
DONE: "bg-green-400",
|
|
246
|
+
COMPLETED: "bg-green-400"
|
|
247
|
+
}[e] ?? "bg-gray-300"}`,
|
|
248
|
+
style: { width: `${r}%` }
|
|
249
|
+
})
|
|
250
|
+
})] }, e);
|
|
251
|
+
})
|
|
252
|
+
})] })]
|
|
253
|
+
}),
|
|
254
|
+
/* @__PURE__ */ d("div", {
|
|
255
|
+
className: "grid grid-cols-1 lg:grid-cols-2 gap-6",
|
|
256
|
+
children: [/* @__PURE__ */ d(f, { children: [/* @__PURE__ */ u(m, { children: /* @__PURE__ */ u(h, { children: "Feature Request Status" }) }), /* @__PURE__ */ u(p, {
|
|
257
|
+
className: "space-y-3",
|
|
258
|
+
children: Object.entries(k).length === 0 ? /* @__PURE__ */ u("p", {
|
|
259
|
+
className: "text-sm text-muted-foreground text-center py-4",
|
|
260
|
+
children: "No feature request data"
|
|
261
|
+
}) : Object.entries(k).map(([e, t]) => {
|
|
262
|
+
let n = T.length, r = n > 0 ? Math.round(t / n * 100) : 0;
|
|
263
|
+
return /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ d("div", {
|
|
264
|
+
className: "flex justify-between text-sm mb-1",
|
|
265
|
+
children: [/* @__PURE__ */ u("span", {
|
|
266
|
+
className: "capitalize",
|
|
267
|
+
children: e.toLowerCase().replace("_", " ")
|
|
268
|
+
}), /* @__PURE__ */ d("span", {
|
|
269
|
+
className: "font-medium",
|
|
270
|
+
children: [
|
|
271
|
+
t,
|
|
272
|
+
" (",
|
|
273
|
+
r,
|
|
274
|
+
"%)"
|
|
275
|
+
]
|
|
276
|
+
})]
|
|
277
|
+
}), /* @__PURE__ */ u("div", {
|
|
278
|
+
className: "w-full h-2 rounded-full bg-muted overflow-hidden",
|
|
279
|
+
children: /* @__PURE__ */ u("div", {
|
|
280
|
+
className: `h-full rounded-full ${{
|
|
281
|
+
SUBMITTED: "bg-gray-400",
|
|
282
|
+
UNDER_REVIEW: "bg-yellow-400",
|
|
283
|
+
PLANNED: "bg-blue-400",
|
|
284
|
+
IN_PROGRESS: "bg-purple-400",
|
|
285
|
+
COMPLETED: "bg-green-400",
|
|
286
|
+
DECLINED: "bg-red-400"
|
|
287
|
+
}[e] ?? "bg-gray-300"}`,
|
|
288
|
+
style: { width: `${r}%` }
|
|
289
|
+
})
|
|
290
|
+
})] }, e);
|
|
291
|
+
})
|
|
292
|
+
})] }), /* @__PURE__ */ d(f, { children: [/* @__PURE__ */ u(m, { children: /* @__PURE__ */ u(h, { children: "Swimlane Distribution" }) }), /* @__PURE__ */ u(p, {
|
|
293
|
+
className: "space-y-3",
|
|
294
|
+
children: Object.entries(A).length === 0 ? /* @__PURE__ */ u("p", {
|
|
295
|
+
className: "text-sm text-muted-foreground text-center py-4",
|
|
296
|
+
children: "No swimlane data"
|
|
297
|
+
}) : Object.entries(A).map(([e, t]) => {
|
|
298
|
+
let n = w.length, r = n > 0 ? Math.round(t / n * 100) : 0;
|
|
299
|
+
return /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ d("div", {
|
|
300
|
+
className: "flex justify-between text-sm mb-1",
|
|
301
|
+
children: [/* @__PURE__ */ u("span", { children: e }), /* @__PURE__ */ d("span", {
|
|
302
|
+
className: "font-medium",
|
|
303
|
+
children: [
|
|
304
|
+
t,
|
|
305
|
+
" (",
|
|
306
|
+
r,
|
|
307
|
+
"%)"
|
|
308
|
+
]
|
|
309
|
+
})]
|
|
310
|
+
}), /* @__PURE__ */ u("div", {
|
|
311
|
+
className: "w-full h-2 rounded-full bg-muted overflow-hidden",
|
|
312
|
+
children: /* @__PURE__ */ u("div", {
|
|
313
|
+
className: "h-full rounded-full bg-primary",
|
|
314
|
+
style: { width: `${r}%` }
|
|
315
|
+
})
|
|
316
|
+
})] }, e);
|
|
317
|
+
})
|
|
318
|
+
})] })]
|
|
319
|
+
}),
|
|
320
|
+
M.length > 0 && /* @__PURE__ */ d(f, { children: [/* @__PURE__ */ u(m, { children: /* @__PURE__ */ u(h, { children: "Overdue Items" }) }), /* @__PURE__ */ u(p, { children: /* @__PURE__ */ u("div", {
|
|
321
|
+
className: "space-y-3",
|
|
322
|
+
children: M.map((e) => /* @__PURE__ */ d("div", {
|
|
323
|
+
className: "flex items-center justify-between border rounded-md p-3",
|
|
324
|
+
children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
|
|
325
|
+
className: "text-sm font-medium",
|
|
326
|
+
children: e.title
|
|
327
|
+
}), /* @__PURE__ */ d("p", {
|
|
328
|
+
className: "text-xs text-muted-foreground",
|
|
329
|
+
children: ["Due: ", e.endDate ? new Date(e.endDate).toLocaleDateString() : "N/A"]
|
|
330
|
+
})] }), /* @__PURE__ */ u("span", {
|
|
331
|
+
className: "text-xs px-2 py-0.5 rounded-full bg-red-100 text-red-800 capitalize",
|
|
332
|
+
children: e.status.toLowerCase().replace("_", " ")
|
|
333
|
+
})]
|
|
334
|
+
}, e.id))
|
|
335
|
+
}) })] })
|
|
336
|
+
]
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
//#endregion
|
|
340
|
+
export { v as ProductsRoadmapAnalyticsPagePage };
|
|
341
|
+
|
|
342
|
+
//# sourceMappingURL=ProductsRoadmapAnalyticsPagePage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProductsRoadmapAnalyticsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.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, Target, CheckCircle, AlertTriangle, BarChart3, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router-dom';\n\nconst GET_ROADMAP_ANALYTICS = gql`\n query GetRoadmapAnalytics($id: ID!) {\n planmagnetRoadmap(id: $id) {\n id\n name\n description\n type\n isPublic\n ownerId\n createdAt\n updatedAt\n items {\n id\n title\n status\n startDate\n endDate\n swimlane\n }\n }\n }\n`;\n\nconst LIST_FEATURE_REQUESTS_FOR_ANALYTICS = gql`\n query ListFeatureRequestsForRoadmapAnalytics($workspaceId: ID!, $pagination: PaginationInput) {\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 RoadmapItemNode {\n id: string;\n title: string;\n status: string;\n startDate: string | null;\n endDate: string | null;\n swimlane: string | null;\n}\n\nexport function ProductsRoadmapAnalyticsPagePage() {\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_ANALYTICS, {\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_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 roadmap = data?.planmagnetRoadmap;\n if (!roadmap) return <div className=\"p-6 text-muted-foreground\">Roadmap not found.</div>;\n\n const items: RoadmapItemNode[] = roadmap.items ?? [];\n const allFeatures = featuresData?.featureRequests?.items ?? [];\n\n const completedItems = items.filter((i) => i.status === 'COMPLETED' || i.status === 'DONE');\n const progress = items.length > 0 ? Math.round((completedItems.length / items.length) * 100) : 0;\n\n // Items by status\n const itemsByStatus: Record<string, number> = {};\n for (const item of items) {\n itemsByStatus[item.status] = (itemsByStatus[item.status] || 0) + 1;\n }\n\n // Feature status distribution\n const featuresByStatus: Record<string, number> = {};\n for (const f of allFeatures) {\n featuresByStatus[f.status] = (featuresByStatus[f.status] || 0) + 1;\n }\n\n // Items by swimlane\n const itemsBySwimlane: Record<string, number> = {};\n for (const item of items) {\n const lane = item.swimlane ?? 'Uncategorized';\n itemsBySwimlane[lane] = (itemsBySwimlane[lane] || 0) + 1;\n }\n\n // Items with overdue end dates\n const now = new Date();\n const overdueItems = items.filter(\n (i) => i.endDate && new Date(i.endDate) < now && i.status !== 'COMPLETED' && i.status !== 'DONE'\n );\n\n // Health score based on progress and overdue items\n const healthScore = Math.max(\n 0,\n Math.round(progress * 0.7 + (1 - overdueItems.length / Math.max(items.length, 1)) * 30)\n );\n\n // Monthly velocity (items completed by month based on endDate)\n const velocityMap: Record<string, { planned: number; completed: number }> = {};\n for (const item of items) {\n if (item.endDate) {\n const month = new Date(item.endDate).toLocaleDateString('en-US', {\n month: 'short',\n year: '2-digit',\n });\n if (!velocityMap[month]) velocityMap[month] = { planned: 0, completed: 0 };\n velocityMap[month].planned += 1;\n if (item.status === 'COMPLETED' || item.status === 'DONE') {\n velocityMap[month].completed += 1;\n }\n }\n }\n const velocityData = Object.entries(velocityMap)\n .map(([month, vals]) => ({ month, ...vals }))\n .slice(-6);\n const maxVal = Math.max(...velocityData.map((d) => Math.max(d.planned, d.completed)), 1);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Roadmap Analytics</h1>\n <p className=\"text-sm text-muted-foreground\">\n {roadmap.name} - {roadmap.type.toLowerCase()}\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 Health Score\n </div>\n <p className=\"text-2xl font-bold mt-1\">{healthScore}/100</p>\n <div className=\"w-full h-2 rounded-full bg-muted mt-2 overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-green-500\"\n style={{ width: `${healthScore}%` }}\n />\n </div>\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 Progress\n </div>\n <p className=\"text-2xl font-bold mt-1\">{progress}%</p>\n <p className=\"text-xs text-muted-foreground mt-1\">{items.length} items total</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 <CheckCircle className=\"h-4 w-4\" />\n Completed\n </div>\n <p className=\"text-2xl font-bold mt-1\">{completedItems.length}</p>\n <p className=\"text-xs text-muted-foreground mt-1\">items completed</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 <AlertTriangle className=\"h-4 w-4\" />\n Overdue\n </div>\n <p className=\"text-2xl font-bold mt-1\">{overdueItems.length}</p>\n <p className=\"text-xs text-muted-foreground mt-1\">items past end date</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 Item Velocity\n </CardTitle>\n </CardHeader>\n <CardContent>\n {velocityData.length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-8\">\n No timeline data available\n </p>\n ) : (\n <div className=\"flex items-end gap-2 h-48\">\n {velocityData.map((d) => (\n <div key={d.month} className=\"flex-1 flex flex-col items-center gap-1\">\n <div className=\"flex gap-0.5 items-end w-full justify-center h-40\">\n <div\n className=\"w-3 bg-primary/30 rounded-t\"\n style={{ height: `${(d.planned / maxVal) * 100}%` }}\n title={`Planned: ${d.planned}`}\n />\n <div\n className=\"w-3 bg-primary rounded-t\"\n style={{ height: `${(d.completed / maxVal) * 100}%` }}\n title={`Completed: ${d.completed}`}\n />\n </div>\n <span className=\"text-xs text-muted-foreground\">{d.month}</span>\n </div>\n ))}\n </div>\n )}\n <div className=\"flex gap-4 justify-center mt-3 text-xs\">\n <span className=\"flex items-center gap-1\">\n <span className=\"h-2 w-2 rounded bg-primary/30\" />\n Planned\n </span>\n <span className=\"flex items-center gap-1\">\n <span className=\"h-2 w-2 rounded bg-primary\" />\n Completed\n </span>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Item Status Breakdown</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(itemsByStatus).map(([status, count]) => {\n const total = items.length;\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\n const colors: Record<string, string> = {\n TODO: 'bg-gray-400',\n PLANNED: 'bg-yellow-400',\n IN_PROGRESS: 'bg-blue-400',\n BLOCKED: 'bg-red-400',\n DONE: 'bg-green-400',\n COMPLETED: 'bg-green-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 </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle>Feature Request Status</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(featuresByStatus).length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-4\">\n No feature request data\n </p>\n ) : (\n Object.entries(featuresByStatus).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 };\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 )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Swimlane Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(itemsBySwimlane).length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-4\">No swimlane data</p>\n ) : (\n Object.entries(itemsBySwimlane).map(([lane, count]) => {\n const total = items.length;\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\n return (\n <div key={lane}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span>{lane}</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 bg-primary\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </CardContent>\n </Card>\n </div>\n\n {overdueItems.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Overdue Items</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {overdueItems.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between border rounded-md p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">{item.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n Due: {item.endDate ? new Date(item.endDate).toLocaleDateString() : 'N/A'}\n </p>\n </div>\n <span className=\"text-xs px-2 py-0.5 rounded-full bg-red-100 text-red-800 capitalize\">\n {item.status.toLowerCase().replace('_', ' ')}\n </span>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProductsRoadmapDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsRoadmapDetailPagePage.tsx"],"names":[],"mappings":"AA6DA,wBAAgB,6BAA6B,4CAuL5C"}
|