@burdenoff/microfe-planmagnet 2026.1002.2 → 2026.1008.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/wspace-operations.d.ts +574 -0
- package/dist/generated/wspace-operations.d.ts.map +1 -1
- package/dist/generated/wspace-types.d.ts +486 -16
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/dist/index.js +8 -8
- package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoot.js +174 -168
- package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +63 -59
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/board/BulkActions.d.ts.map +1 -1
- package/dist/planmagnet/board/BulkActions.js +106 -55
- package/dist/planmagnet/board/BulkActions.js.map +1 -1
- package/dist/planmagnet/controls/Pickers.d.ts +3 -3
- package/dist/planmagnet/controls/Pickers.js.map +1 -1
- package/dist/planmagnet/dashboards/DashboardUI.d.ts +0 -4
- package/dist/planmagnet/dashboards/DashboardUI.d.ts.map +1 -1
- package/dist/planmagnet/dashboards/DashboardUI.js +6 -13
- package/dist/planmagnet/dashboards/DashboardUI.js.map +1 -1
- package/dist/planmagnet/dashboards/cards/SprintCapacityCard.d.ts.map +1 -1
- package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js +70 -59
- package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js.map +1 -1
- package/dist/planmagnet/forecast/ResourceForecastCard.js +1 -1
- package/dist/planmagnet/forecast/ResourceForecastCard.js.map +1 -1
- package/dist/planmagnet/index.d.ts +1 -1
- package/dist/planmagnet/index.d.ts.map +1 -1
- package/dist/planmagnet/pages/DashboardsProjectManagerPagePage.d.ts +3 -3
- package/dist/planmagnet/pages/DashboardsProjectManagerPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DashboardsProjectManagerPagePage.js +26 -19
- package/dist/planmagnet/pages/DashboardsProjectManagerPagePage.js.map +1 -1
- package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.d.ts +2 -2
- package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.js +18 -12
- package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsCustomerSatisfactionPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsCustomerSatisfactionPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsCustomerSatisfactionPagePage.js +251 -0
- package/dist/planmagnet/pages/ProductsCustomerSatisfactionPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +94 -92
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +116 -114
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +174 -159
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +159 -145
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js +161 -143
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +157 -155
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TeamInviteMemberPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TeamInviteMemberPagePage.js +142 -82
- package/dist/planmagnet/pages/TeamInviteMemberPagePage.js.map +1 -1
- package/dist/planmagnet/pages/analytics/DeliveryKpisCard.d.ts.map +1 -1
- package/dist/planmagnet/pages/analytics/DeliveryKpisCard.js +97 -99
- package/dist/planmagnet/pages/analytics/DeliveryKpisCard.js.map +1 -1
- package/dist/planmagnet/product/FeatureLinkPanels.js +66 -66
- package/dist/planmagnet/product/ProductFeatureControls.js +38 -38
- package/dist/planmagnet/product/ProductFeatureLinkCard.js +8 -8
- package/dist/planmagnet/product/ProductLineControls.js +83 -83
- package/dist/planmagnet/product/ProductTeamsPanel.js +6 -6
- package/dist/planmagnet/product/routes.d.ts +6 -0
- package/dist/planmagnet/product/routes.d.ts.map +1 -1
- package/dist/planmagnet/product/routes.js +2 -2
- package/dist/planmagnet/product/routes.js.map +1 -1
- package/dist/planmagnet/satisfaction/DeliverySatisfactionSection.d.ts +20 -0
- package/dist/planmagnet/satisfaction/DeliverySatisfactionSection.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/DeliverySatisfactionSection.js +55 -0
- package/dist/planmagnet/satisfaction/DeliverySatisfactionSection.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionFigures.d.ts +30 -0
- package/dist/planmagnet/satisfaction/SatisfactionFigures.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionFigures.js +282 -0
- package/dist/planmagnet/satisfaction/SatisfactionFigures.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionImportDialog.d.ts +9 -0
- package/dist/planmagnet/satisfaction/SatisfactionImportDialog.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionImportDialog.js +390 -0
- package/dist/planmagnet/satisfaction/SatisfactionImportDialog.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionLinkFields.d.ts +17 -0
- package/dist/planmagnet/satisfaction/SatisfactionLinkFields.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionLinkFields.js +77 -0
- package/dist/planmagnet/satisfaction/SatisfactionLinkFields.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionReleaseSelect.d.ts +18 -0
- package/dist/planmagnet/satisfaction/SatisfactionReleaseSelect.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionReleaseSelect.js +87 -0
- package/dist/planmagnet/satisfaction/SatisfactionReleaseSelect.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponseDialog.d.ts +13 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponseDialog.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponseDialog.js +326 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponseDialog.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponsesTable.d.ts +23 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponsesTable.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponsesTable.js +258 -0
- package/dist/planmagnet/satisfaction/SatisfactionResponsesTable.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionSummaryCard.d.ts +6 -0
- package/dist/planmagnet/satisfaction/SatisfactionSummaryCard.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionSummaryCard.js +56 -0
- package/dist/planmagnet/satisfaction/SatisfactionSummaryCard.js.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionWindowPicker.d.ts +16 -0
- package/dist/planmagnet/satisfaction/SatisfactionWindowPicker.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/SatisfactionWindowPicker.js +102 -0
- package/dist/planmagnet/satisfaction/SatisfactionWindowPicker.js.map +1 -0
- package/dist/planmagnet/satisfaction/satisfaction.d.ts +154 -0
- package/dist/planmagnet/satisfaction/satisfaction.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/satisfaction.js +210 -0
- package/dist/planmagnet/satisfaction/satisfaction.js.map +1 -0
- package/dist/planmagnet/satisfaction/satisfactionCsv.d.ts +108 -0
- package/dist/planmagnet/satisfaction/satisfactionCsv.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/satisfactionCsv.js +211 -0
- package/dist/planmagnet/satisfaction/satisfactionCsv.js.map +1 -0
- package/dist/planmagnet/satisfaction/satisfactionForm.d.ts +90 -0
- package/dist/planmagnet/satisfaction/satisfactionForm.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/satisfactionForm.js +90 -0
- package/dist/planmagnet/satisfaction/satisfactionForm.js.map +1 -0
- package/dist/planmagnet/satisfaction/satisfactionOperations.d.ts +116 -0
- package/dist/planmagnet/satisfaction/satisfactionOperations.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/satisfactionOperations.js +86 -0
- package/dist/planmagnet/satisfaction/satisfactionOperations.js.map +1 -0
- package/dist/planmagnet/satisfaction/useSatisfactionAccess.d.ts +20 -0
- package/dist/planmagnet/satisfaction/useSatisfactionAccess.d.ts.map +1 -0
- package/dist/planmagnet/satisfaction/useSatisfactionAccess.js +31 -0
- package/dist/planmagnet/satisfaction/useSatisfactionAccess.js.map +1 -0
- package/dist/planmagnet/skills/RequiredSkillsEditor.d.ts +35 -0
- package/dist/planmagnet/skills/RequiredSkillsEditor.d.ts.map +1 -0
- package/dist/planmagnet/skills/RequiredSkillsEditor.js +124 -0
- package/dist/planmagnet/skills/RequiredSkillsEditor.js.map +1 -0
- package/dist/planmagnet/skills/SkillChips.d.ts +30 -0
- package/dist/planmagnet/skills/SkillChips.d.ts.map +1 -0
- package/dist/planmagnet/skills/SkillChips.js +57 -0
- package/dist/planmagnet/skills/SkillChips.js.map +1 -0
- package/dist/planmagnet/skills/SkillCoverageCard.d.ts +24 -0
- package/dist/planmagnet/skills/SkillCoverageCard.d.ts.map +1 -0
- package/dist/planmagnet/skills/SkillCoverageCard.js +256 -0
- package/dist/planmagnet/skills/SkillCoverageCard.js.map +1 -0
- package/dist/planmagnet/skills/WorkItemRequiredSkills.d.ts +10 -0
- package/dist/planmagnet/skills/WorkItemRequiredSkills.d.ts.map +1 -0
- package/dist/planmagnet/skills/WorkItemRequiredSkills.js +65 -0
- package/dist/planmagnet/skills/WorkItemRequiredSkills.js.map +1 -0
- package/dist/planmagnet/skills/skillOperations.d.ts +52 -0
- package/dist/planmagnet/skills/skillOperations.d.ts.map +1 -0
- package/dist/planmagnet/skills/skillOperations.js +44 -0
- package/dist/planmagnet/skills/skillOperations.js.map +1 -0
- package/dist/planmagnet/skills/skillTags.d.ts +103 -0
- package/dist/planmagnet/skills/skillTags.d.ts.map +1 -0
- package/dist/planmagnet/skills/skillTags.js +115 -0
- package/dist/planmagnet/skills/skillTags.js.map +1 -0
- package/dist/planmagnet/skills/useRequiredSkillsDraft.d.ts +28 -0
- package/dist/planmagnet/skills/useRequiredSkillsDraft.d.ts.map +1 -0
- package/dist/planmagnet/skills/useRequiredSkillsDraft.js +30 -0
- package/dist/planmagnet/skills/useRequiredSkillsDraft.js.map +1 -0
- package/dist/planmagnet/team/PendingInvitations.d.ts +27 -0
- package/dist/planmagnet/team/PendingInvitations.d.ts.map +1 -0
- package/dist/planmagnet/team/PendingInvitations.js +220 -0
- package/dist/planmagnet/team/PendingInvitations.js.map +1 -0
- package/dist/planmagnet/team/invitationEmail.d.ts +35 -0
- package/dist/planmagnet/team/invitationEmail.d.ts.map +1 -0
- package/dist/planmagnet/team/invitationEmail.js +73 -0
- package/dist/planmagnet/team/invitationEmail.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { useTr as t } from "../../shared/hooks/useTr.js";
|
|
3
|
+
import { isForbidden as n } from "../utils/graphqlErrors.js";
|
|
4
|
+
import { ErrorLine as r } from "../controls/ConfirmButton.js";
|
|
5
|
+
import { METRIC_TYPES as i, pageStateFromSearch as a, windowInput as o } from "../satisfaction/satisfaction.js";
|
|
6
|
+
import { SatisfactionFigureCards as s } from "../satisfaction/SatisfactionFigures.js";
|
|
7
|
+
import { CUSTOMER_SATISFACTION_SUMMARY as c, SATISFACTION_QUERIES as l } from "../satisfaction/satisfactionOperations.js";
|
|
8
|
+
import { ProductSelect as ee, ProjectSelect as te } from "../controls/Pickers.js";
|
|
9
|
+
import { writeUrlParam as u } from "../utils/urlParam.js";
|
|
10
|
+
import { SatisfactionReleaseSelect as d, useSatisfactionReleaseOptions as f } from "../satisfaction/SatisfactionReleaseSelect.js";
|
|
11
|
+
import { SatisfactionImportDialog as p } from "../satisfaction/SatisfactionImportDialog.js";
|
|
12
|
+
import { SatisfactionResponseDialog as m } from "../satisfaction/SatisfactionResponseDialog.js";
|
|
13
|
+
import { SatisfactionResponsesTable as h } from "../satisfaction/SatisfactionResponsesTable.js";
|
|
14
|
+
import { SatisfactionWindowPicker as g } from "../satisfaction/SatisfactionWindowPicker.js";
|
|
15
|
+
import { useSatisfactionAccess as _ } from "../satisfaction/useSatisfactionAccess.js";
|
|
16
|
+
import { useEffect as v, useMemo as y, useState as b } from "react";
|
|
17
|
+
import { Info as x, Loader2 as S, MessageSquareHeart as C, X as ne } from "lucide-react";
|
|
18
|
+
import { useLocation as re } from "react-router";
|
|
19
|
+
import { useApolloClient as ie, useQuery as w } from "@apollo/client/react";
|
|
20
|
+
import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
|
|
21
|
+
import { Button as O, Card as k, CardContent as A, CardHeader as j, CardTitle as M } from "@burdenoff/fe-libs/ui";
|
|
22
|
+
import { clearGraphqlFetchResponseCache as N } from "@burdenoff/fe-libs/shared/graphql";
|
|
23
|
+
//#region src/planmagnet/pages/ProductsCustomerSatisfactionPagePage.tsx
|
|
24
|
+
function P() {
|
|
25
|
+
let P = t(), { workspaceId: F } = e(), I = _(), L = ie(), { releases: R } = f(), { search: ae } = re(), [z, B] = b(() => a(ae, /* @__PURE__ */ new Date())), [V, H] = b(() => o(z.window) ?? { preset: "LAST_90_DAYS" }), [U, W] = b(null), G = (e) => {
|
|
26
|
+
let t = {
|
|
27
|
+
...z,
|
|
28
|
+
...e
|
|
29
|
+
};
|
|
30
|
+
B(t);
|
|
31
|
+
let n = o(t.window);
|
|
32
|
+
n && H(n);
|
|
33
|
+
};
|
|
34
|
+
v(() => {
|
|
35
|
+
let e = V.preset;
|
|
36
|
+
u("window", e === "LAST_90_DAYS" ? null : e), u("from", e === "CUSTOM" ? V.from ?? null : null), u("to", e === "CUSTOM" ? V.to ?? null : null), u("product", z.productId || null), u("release", z.releaseId || null), u("project", z.projectId || null), u("metric", z.metricType || null);
|
|
37
|
+
}, [
|
|
38
|
+
V,
|
|
39
|
+
z.productId,
|
|
40
|
+
z.releaseId,
|
|
41
|
+
z.projectId,
|
|
42
|
+
z.metricType
|
|
43
|
+
]);
|
|
44
|
+
let K = y(() => ({
|
|
45
|
+
...z.productId ? { productId: z.productId } : {},
|
|
46
|
+
...z.releaseId ? { releaseId: z.releaseId } : {},
|
|
47
|
+
...z.projectId ? { projectId: z.projectId } : {}
|
|
48
|
+
}), [
|
|
49
|
+
z.productId,
|
|
50
|
+
z.releaseId,
|
|
51
|
+
z.projectId
|
|
52
|
+
]), q = y(() => ({
|
|
53
|
+
...K,
|
|
54
|
+
...z.metricType ? { metricType: z.metricType } : {},
|
|
55
|
+
window: V
|
|
56
|
+
}), [
|
|
57
|
+
K,
|
|
58
|
+
z.metricType,
|
|
59
|
+
V
|
|
60
|
+
]), J = {
|
|
61
|
+
productId: z.productId,
|
|
62
|
+
releaseId: z.releaseId,
|
|
63
|
+
projectId: z.projectId
|
|
64
|
+
}, Y = !!(z.productId || z.releaseId || z.projectId), { data: X, previousData: oe, loading: Z, error: Q } = w(c, {
|
|
65
|
+
variables: {
|
|
66
|
+
workspaceId: F ?? "",
|
|
67
|
+
filter: {
|
|
68
|
+
...K,
|
|
69
|
+
window: V
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
skip: !F
|
|
73
|
+
}), $ = (X ?? oe)?.planmagnetCustomerSatisfaction ?? null, se = Z && !X && !!$, ce = () => {
|
|
74
|
+
N(), L.refetchQueries({ include: l });
|
|
75
|
+
};
|
|
76
|
+
return F ? /* @__PURE__ */ D("div", {
|
|
77
|
+
className: "space-y-6 p-6",
|
|
78
|
+
"data-testid": "satisfaction-page",
|
|
79
|
+
children: [
|
|
80
|
+
/* @__PURE__ */ D("div", {
|
|
81
|
+
className: "flex flex-wrap items-start justify-between gap-4",
|
|
82
|
+
children: [/* @__PURE__ */ D("div", {
|
|
83
|
+
className: "max-w-3xl",
|
|
84
|
+
children: [/* @__PURE__ */ D("h1", {
|
|
85
|
+
className: "flex items-center gap-2 text-2xl font-semibold",
|
|
86
|
+
children: [/* @__PURE__ */ E(C, {
|
|
87
|
+
className: "h-6 w-6",
|
|
88
|
+
"aria-hidden": !0
|
|
89
|
+
}), P("planmagnet.satisfaction.title", "Customer satisfaction")]
|
|
90
|
+
}), /* @__PURE__ */ E("p", {
|
|
91
|
+
className: "mt-1 text-sm text-muted-foreground",
|
|
92
|
+
children: P("planmagnet.satisfaction.subtitle", "CSAT and NPS from what customers said about your products, releases and projects. Responses are entered by your team or imported from a CSV file.")
|
|
93
|
+
})]
|
|
94
|
+
}), I.canCreate && /* @__PURE__ */ D("div", {
|
|
95
|
+
className: "flex flex-wrap items-center gap-2",
|
|
96
|
+
children: [/* @__PURE__ */ E(p, {
|
|
97
|
+
links: J,
|
|
98
|
+
onImported: (e) => W({
|
|
99
|
+
tone: "status",
|
|
100
|
+
message: P("planmagnet.satisfaction.notice.imported", "Import done — new responses: {{created}}; already imported and skipped: {{skipped}}.", {
|
|
101
|
+
created: e.createdCount,
|
|
102
|
+
skipped: e.skippedCount
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
}), /* @__PURE__ */ E(m, {
|
|
106
|
+
links: J,
|
|
107
|
+
onSaved: () => W({
|
|
108
|
+
tone: "status",
|
|
109
|
+
message: P("planmagnet.satisfaction.notice.created", "The response was added.")
|
|
110
|
+
})
|
|
111
|
+
})]
|
|
112
|
+
})]
|
|
113
|
+
}),
|
|
114
|
+
I.readOnly && /* @__PURE__ */ D("p", {
|
|
115
|
+
className: "flex items-center gap-1.5 text-xs text-muted-foreground",
|
|
116
|
+
"data-testid": "satisfaction-read-only",
|
|
117
|
+
children: [/* @__PURE__ */ E(x, {
|
|
118
|
+
className: "h-3.5 w-3.5 shrink-0",
|
|
119
|
+
"aria-hidden": !0
|
|
120
|
+
}), P("planmagnet.satisfaction.readOnly", "You can read customer satisfaction. Adding, importing, editing and deleting responses is for product managers.")]
|
|
121
|
+
}),
|
|
122
|
+
/* @__PURE__ */ D("div", {
|
|
123
|
+
className: "space-y-3 rounded-lg border p-3",
|
|
124
|
+
"data-testid": "satisfaction-filters",
|
|
125
|
+
children: [/* @__PURE__ */ E(g, {
|
|
126
|
+
value: z.window,
|
|
127
|
+
onChange: (e) => G({ window: e }),
|
|
128
|
+
current: $?.window ?? null
|
|
129
|
+
}), /* @__PURE__ */ D("div", {
|
|
130
|
+
className: "flex flex-col gap-2 sm:flex-row sm:items-center",
|
|
131
|
+
children: [/* @__PURE__ */ D("div", {
|
|
132
|
+
className: "grid flex-1 gap-2 sm:grid-cols-3",
|
|
133
|
+
children: [
|
|
134
|
+
/* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E("label", {
|
|
135
|
+
htmlFor: "satisfaction-filter-product",
|
|
136
|
+
className: "sr-only",
|
|
137
|
+
children: P("planmagnet.satisfaction.filters.product", "Product")
|
|
138
|
+
}), /* @__PURE__ */ E(ee, {
|
|
139
|
+
id: "satisfaction-filter-product",
|
|
140
|
+
value: z.productId,
|
|
141
|
+
noneLabel: P("planmagnet.satisfaction.filters.allProducts", "All products"),
|
|
142
|
+
onChange: (e) => {
|
|
143
|
+
let t = R.find((e) => e.id === z.releaseId);
|
|
144
|
+
G({
|
|
145
|
+
productId: e,
|
|
146
|
+
releaseId: !t || !t.productId || !e || t.productId === e ? z.releaseId : ""
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
})] }),
|
|
150
|
+
/* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E("label", {
|
|
151
|
+
htmlFor: "satisfaction-filter-release",
|
|
152
|
+
className: "sr-only",
|
|
153
|
+
children: P("planmagnet.satisfaction.filters.release", "Release")
|
|
154
|
+
}), /* @__PURE__ */ E(d, {
|
|
155
|
+
id: "satisfaction-filter-release",
|
|
156
|
+
value: z.releaseId,
|
|
157
|
+
productId: z.productId,
|
|
158
|
+
noneLabel: P("planmagnet.satisfaction.filters.allReleases", "All releases"),
|
|
159
|
+
onChange: (e) => G({ releaseId: e })
|
|
160
|
+
})] }),
|
|
161
|
+
/* @__PURE__ */ D("div", { children: [/* @__PURE__ */ E("label", {
|
|
162
|
+
htmlFor: "satisfaction-filter-project",
|
|
163
|
+
className: "sr-only",
|
|
164
|
+
children: P("planmagnet.satisfaction.filters.project", "Project")
|
|
165
|
+
}), /* @__PURE__ */ E(te, {
|
|
166
|
+
id: "satisfaction-filter-project",
|
|
167
|
+
value: z.projectId,
|
|
168
|
+
noneLabel: P("planmagnet.satisfaction.filters.allProjects", "All projects"),
|
|
169
|
+
onChange: (e) => G({ projectId: e })
|
|
170
|
+
})] })
|
|
171
|
+
]
|
|
172
|
+
}), Y && /* @__PURE__ */ D(O, {
|
|
173
|
+
size: "sm",
|
|
174
|
+
variant: "ghost",
|
|
175
|
+
onClick: () => G({
|
|
176
|
+
productId: "",
|
|
177
|
+
releaseId: "",
|
|
178
|
+
projectId: ""
|
|
179
|
+
}),
|
|
180
|
+
"data-testid": "satisfaction-filters-clear",
|
|
181
|
+
children: [/* @__PURE__ */ E(ne, { className: "mr-1 h-4 w-4" }), P("planmagnet.satisfaction.filters.clear", "Clear filters")]
|
|
182
|
+
})]
|
|
183
|
+
})]
|
|
184
|
+
}),
|
|
185
|
+
z.productId && /* @__PURE__ */ E("p", {
|
|
186
|
+
className: "-mt-3 text-xs text-muted-foreground",
|
|
187
|
+
children: P("planmagnet.satisfaction.filters.productNote", "A product counts its own responses and those about its releases that name no product of their own.")
|
|
188
|
+
}),
|
|
189
|
+
U && /* @__PURE__ */ D("div", {
|
|
190
|
+
role: U.tone,
|
|
191
|
+
className: `flex items-start justify-between gap-3 rounded-md p-3 text-sm ${U.tone === "alert" ? "bg-status-error-bg-subtle text-status-error-text" : "bg-muted"}`,
|
|
192
|
+
"data-testid": "satisfaction-notice",
|
|
193
|
+
children: [/* @__PURE__ */ E("span", { children: U.message }), /* @__PURE__ */ E("button", {
|
|
194
|
+
type: "button",
|
|
195
|
+
className: "shrink-0 text-xs text-muted-foreground hover:underline",
|
|
196
|
+
onClick: () => W(null),
|
|
197
|
+
children: P("planmagnet.satisfaction.dismiss", "Dismiss")
|
|
198
|
+
})]
|
|
199
|
+
}),
|
|
200
|
+
Z && !$ ? /* @__PURE__ */ D("div", {
|
|
201
|
+
className: "flex items-center gap-2 text-sm text-muted-foreground",
|
|
202
|
+
children: [/* @__PURE__ */ E(S, { className: "h-4 w-4 animate-spin" }), P("planmagnet.satisfaction.loading", "Loading…")]
|
|
203
|
+
}) : Q && !$ ? /* @__PURE__ */ E(r, { message: n(Q) ? P("planmagnet.satisfaction.errors.readForbidden", "Your role cannot see customer satisfaction in this workspace.") : P("planmagnet.satisfaction.errors.load", "Customer satisfaction could not be loaded: {{message}}", { message: Q.message }) }) : $ ? /* @__PURE__ */ D(T, { children: [
|
|
204
|
+
Q && /* @__PURE__ */ E(r, { message: P("planmagnet.satisfaction.errors.refresh", "The figures could not be refreshed: {{message}}", { message: Q.message }) }),
|
|
205
|
+
se && /* @__PURE__ */ D("p", {
|
|
206
|
+
className: "flex items-center gap-2 text-xs text-muted-foreground",
|
|
207
|
+
role: "status",
|
|
208
|
+
children: [/* @__PURE__ */ E(S, {
|
|
209
|
+
className: "h-3.5 w-3.5 animate-spin",
|
|
210
|
+
"aria-hidden": !0
|
|
211
|
+
}), P("planmagnet.satisfaction.updating", "Updating the figures…")]
|
|
212
|
+
}),
|
|
213
|
+
/* @__PURE__ */ E(s, { summary: $ })
|
|
214
|
+
] }) : null,
|
|
215
|
+
/* @__PURE__ */ D(k, { children: [/* @__PURE__ */ D(j, {
|
|
216
|
+
className: "flex flex-row flex-wrap items-center justify-between gap-2 space-y-0",
|
|
217
|
+
children: [/* @__PURE__ */ E(M, { children: P("planmagnet.satisfaction.list.title", "Responses") }), /* @__PURE__ */ E("div", {
|
|
218
|
+
className: "flex items-center gap-1 rounded-md border p-0.5",
|
|
219
|
+
role: "group",
|
|
220
|
+
"aria-label": P("planmagnet.satisfaction.list.metricFilter", "Metric"),
|
|
221
|
+
children: ["", ...i].map((e) => /* @__PURE__ */ E("button", {
|
|
222
|
+
type: "button",
|
|
223
|
+
"aria-pressed": z.metricType === e,
|
|
224
|
+
onClick: () => G({ metricType: e }),
|
|
225
|
+
"data-testid": `satisfaction-metric-${e || "ALL"}`,
|
|
226
|
+
className: `rounded px-2.5 py-1 text-xs ${z.metricType === e ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"}`,
|
|
227
|
+
children: e || P("planmagnet.satisfaction.list.allMetrics", "All")
|
|
228
|
+
}, e || "all"))
|
|
229
|
+
})]
|
|
230
|
+
}), /* @__PURE__ */ E(A, { children: /* @__PURE__ */ E(h, {
|
|
231
|
+
workspaceId: F,
|
|
232
|
+
filter: q,
|
|
233
|
+
access: I,
|
|
234
|
+
links: J,
|
|
235
|
+
filtered: Y || !!z.metricType,
|
|
236
|
+
onNotice: (e, t) => W({
|
|
237
|
+
message: e,
|
|
238
|
+
tone: t
|
|
239
|
+
}),
|
|
240
|
+
onStale: ce
|
|
241
|
+
}, JSON.stringify(q)) })] })
|
|
242
|
+
]
|
|
243
|
+
}) : /* @__PURE__ */ E("div", {
|
|
244
|
+
className: "p-6 text-sm text-muted-foreground",
|
|
245
|
+
children: P("planmagnet.satisfaction.noWorkspace", "Choose a workspace to see customer satisfaction.")
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
//#endregion
|
|
249
|
+
export { P as ProductsCustomerSatisfactionPagePage };
|
|
250
|
+
|
|
251
|
+
//# sourceMappingURL=ProductsCustomerSatisfactionPagePage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProductsCustomerSatisfactionPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsCustomerSatisfactionPagePage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { useLocation } from 'react-router';\nimport { useApolloClient, useQuery } from '@apollo/client/react';\nimport { clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql';\nimport { Button, Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { Info, Loader2, MessageSquareHeart, X } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { ErrorLine } from '../controls/ConfirmButton';\nimport { ProductSelect, ProjectSelect } from '../controls/Pickers';\nimport { isForbidden } from '../utils/graphqlErrors';\nimport { writeUrlParam } from '../utils/urlParam';\nimport { SatisfactionFigureCards } from '../satisfaction/SatisfactionFigures';\nimport { SatisfactionImportDialog } from '../satisfaction/SatisfactionImportDialog';\nimport {\n SatisfactionReleaseSelect,\n useSatisfactionReleaseOptions,\n} from '../satisfaction/SatisfactionReleaseSelect';\nimport { SatisfactionResponseDialog } from '../satisfaction/SatisfactionResponseDialog';\nimport {\n SatisfactionResponsesTable,\n type ResponsesFilter,\n} from '../satisfaction/SatisfactionResponsesTable';\nimport { SatisfactionWindowPicker } from '../satisfaction/SatisfactionWindowPicker';\nimport {\n DEFAULT_WINDOW_PRESET,\n METRIC_TYPES,\n pageStateFromSearch,\n windowInput,\n type SatisfactionPageState,\n type SatisfactionWindowInput,\n} from '../satisfaction/satisfaction';\nimport {\n CUSTOMER_SATISFACTION_SUMMARY,\n SATISFACTION_QUERIES,\n type CustomerSatisfactionSummaryData,\n} from '../satisfaction/satisfactionOperations';\nimport { useSatisfactionAccess } from '../satisfaction/useSatisfactionAccess';\n\n/**\n * Customer satisfaction (ANR-21): CSAT — the default delivery KPI — and NPS from customers' answers about products,\n * releases and projects, with the responses behind them. Answers are entered one at a time by team members or\n * imported from a CSV file; product managers add, edit, delete and import, everyone else reads.\n *\n * Every figure shows its response count and its window, and a metric with fewer than five responses reads \"Not enough\n * responses\" — never 0. The window (trailing 30 / 90 / 180 / 365 days, 90 by default, or custom UTC days) and the\n * product / release / project filters apply to the figures and the list alike, and live in the URL so a view is\n * linkable and the product, release and delivery-KPI cards can open it filtered.\n */\n\ntype Notice = { message: string; tone: 'status' | 'alert' };\n\nexport function ProductsCustomerSatisfactionPagePage() {\n const tr = useTr();\n const { workspaceId } = usePlanMagnet();\n const access = useSatisfactionAccess();\n const client = useApolloClient();\n const { releases } = useSatisfactionReleaseOptions();\n // The router's location, not window.location: a link from a product, release or delivery card arrives through the\n // host's navigate(). Later changes are written back with replaceState (utils/urlParam.ts), never as history entries.\n const { search } = useLocation();\n const [state, setState] = useState<SatisfactionPageState>(() =>\n pageStateFromSearch(search, new Date())\n );\n // The last window the service can take: a custom pair being typed never reaches a query half-done.\n const [lastWindow, setLastWindow] = useState<SatisfactionWindowInput>(\n () => windowInput(state.window) ?? { preset: DEFAULT_WINDOW_PRESET }\n );\n // What the last write did — kept above the figures and the list, which a refetch redraws.\n const [notice, setNotice] = useState<Notice | null>(null);\n\n const change = (patch: Partial<SatisfactionPageState>) => {\n const next = { ...state, ...patch };\n setState(next);\n const valid = windowInput(next.window);\n if (valid) setLastWindow(valid);\n };\n\n // The URL carries the view (see pageStateFromSearch); the default window is left out.\n useEffect(() => {\n const preset = lastWindow.preset;\n writeUrlParam('window', preset === DEFAULT_WINDOW_PRESET ? null : preset);\n writeUrlParam('from', preset === 'CUSTOM' ? (lastWindow.from ?? null) : null);\n writeUrlParam('to', preset === 'CUSTOM' ? (lastWindow.to ?? null) : null);\n writeUrlParam('product', state.productId || null);\n writeUrlParam('release', state.releaseId || null);\n writeUrlParam('project', state.projectId || null);\n writeUrlParam('metric', state.metricType || null);\n }, [lastWindow, state.productId, state.releaseId, state.projectId, state.metricType]);\n\n const scopeFilter = useMemo(\n () => ({\n ...(state.productId ? { productId: state.productId } : {}),\n ...(state.releaseId ? { releaseId: state.releaseId } : {}),\n ...(state.projectId ? { projectId: state.projectId } : {}),\n }),\n [state.productId, state.releaseId, state.projectId]\n );\n const responsesFilter: ResponsesFilter = useMemo(\n () => ({\n ...scopeFilter,\n ...(state.metricType ? { metricType: state.metricType } : {}),\n window: lastWindow,\n }),\n [scopeFilter, state.metricType, lastWindow]\n );\n const links = {\n productId: state.productId,\n releaseId: state.releaseId,\n projectId: state.projectId,\n };\n const scoped = !!(state.productId || state.releaseId || state.projectId);\n\n const { data, previousData, loading, error } = useQuery<CustomerSatisfactionSummaryData>(\n CUSTOMER_SATISFACTION_SUMMARY,\n {\n variables: { workspaceId: workspaceId ?? '', filter: { ...scopeFilter, window: lastWindow } },\n skip: !workspaceId,\n }\n );\n // A new window or filter keeps the figures on screen (each states its own window) until the new ones arrive.\n const summary = (data ?? previousData)?.planmagnetCustomerSatisfaction ?? null;\n const updating = loading && !data && !!summary;\n\n // A response deleted elsewhere: drop the 15 s fetch replay, then re-read what is on screen.\n const refreshAfterStale = () => {\n clearGraphqlFetchResponseCache();\n void client.refetchQueries({ include: SATISFACTION_QUERIES });\n };\n\n const onProduct = (productId: string) => {\n const release = releases.find((r) => r.id === state.releaseId);\n // A release of another product cannot be about this one: the release filter goes with the old product.\n const keepRelease =\n !release || !release.productId || !productId || release.productId === productId;\n change({ productId, releaseId: keepRelease ? state.releaseId : '' });\n };\n\n if (!workspaceId) {\n return (\n <div className=\"p-6 text-sm text-muted-foreground\">\n {tr(\n 'planmagnet.satisfaction.noWorkspace',\n 'Choose a workspace to see customer satisfaction.'\n )}\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\" data-testid=\"satisfaction-page\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"max-w-3xl\">\n <h1 className=\"flex items-center gap-2 text-2xl font-semibold\">\n <MessageSquareHeart className=\"h-6 w-6\" aria-hidden={true} />\n {tr('planmagnet.satisfaction.title', 'Customer satisfaction')}\n </h1>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {tr(\n 'planmagnet.satisfaction.subtitle',\n 'CSAT and NPS from what customers said about your products, releases and projects. Responses are entered by your team or imported from a CSV file.'\n )}\n </p>\n </div>\n {access.canCreate && (\n <div className=\"flex flex-wrap items-center gap-2\">\n <SatisfactionImportDialog\n links={links}\n onImported={(result) =>\n setNotice({\n tone: 'status',\n message: tr(\n 'planmagnet.satisfaction.notice.imported',\n 'Import done — new responses: {{created}}; already imported and skipped: {{skipped}}.',\n { created: result.createdCount, skipped: result.skippedCount }\n ),\n })\n }\n />\n <SatisfactionResponseDialog\n links={links}\n onSaved={() =>\n setNotice({\n tone: 'status',\n message: tr('planmagnet.satisfaction.notice.created', 'The response was added.'),\n })\n }\n />\n </div>\n )}\n </div>\n\n {access.readOnly && (\n <p\n className=\"flex items-center gap-1.5 text-xs text-muted-foreground\"\n data-testid=\"satisfaction-read-only\"\n >\n <Info className=\"h-3.5 w-3.5 shrink-0\" aria-hidden={true} />\n {tr(\n 'planmagnet.satisfaction.readOnly',\n 'You can read customer satisfaction. Adding, importing, editing and deleting responses is for product managers.'\n )}\n </p>\n )}\n\n <div className=\"space-y-3 rounded-lg border p-3\" data-testid=\"satisfaction-filters\">\n <SatisfactionWindowPicker\n value={state.window}\n onChange={(window) => change({ window })}\n current={summary?.window ?? null}\n />\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <div className=\"grid flex-1 gap-2 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"satisfaction-filter-product\" className=\"sr-only\">\n {tr('planmagnet.satisfaction.filters.product', 'Product')}\n </label>\n <ProductSelect\n id=\"satisfaction-filter-product\"\n value={state.productId}\n noneLabel={tr('planmagnet.satisfaction.filters.allProducts', 'All products')}\n onChange={onProduct}\n />\n </div>\n <div>\n <label htmlFor=\"satisfaction-filter-release\" className=\"sr-only\">\n {tr('planmagnet.satisfaction.filters.release', 'Release')}\n </label>\n <SatisfactionReleaseSelect\n id=\"satisfaction-filter-release\"\n value={state.releaseId}\n productId={state.productId}\n noneLabel={tr('planmagnet.satisfaction.filters.allReleases', 'All releases')}\n onChange={(releaseId) => change({ releaseId })}\n />\n </div>\n <div>\n <label htmlFor=\"satisfaction-filter-project\" className=\"sr-only\">\n {tr('planmagnet.satisfaction.filters.project', 'Project')}\n </label>\n <ProjectSelect\n id=\"satisfaction-filter-project\"\n value={state.projectId}\n noneLabel={tr('planmagnet.satisfaction.filters.allProjects', 'All projects')}\n onChange={(projectId) => change({ projectId })}\n />\n </div>\n </div>\n {scoped && (\n <Button\n size=\"sm\"\n variant=\"ghost\"\n onClick={() => change({ productId: '', releaseId: '', projectId: '' })}\n data-testid=\"satisfaction-filters-clear\"\n >\n <X className=\"mr-1 h-4 w-4\" />\n {tr('planmagnet.satisfaction.filters.clear', 'Clear filters')}\n </Button>\n )}\n </div>\n </div>\n {state.productId && (\n <p className=\"-mt-3 text-xs text-muted-foreground\">\n {tr(\n 'planmagnet.satisfaction.filters.productNote',\n 'A product counts its own responses and those about its releases that name no product of their own.'\n )}\n </p>\n )}\n\n {notice && (\n <div\n role={notice.tone}\n className={`flex items-start justify-between gap-3 rounded-md p-3 text-sm ${\n notice.tone === 'alert'\n ? 'bg-status-error-bg-subtle text-status-error-text'\n : 'bg-muted'\n }`}\n data-testid=\"satisfaction-notice\"\n >\n <span>{notice.message}</span>\n <button\n type=\"button\"\n className=\"shrink-0 text-xs text-muted-foreground hover:underline\"\n onClick={() => setNotice(null)}\n >\n {tr('planmagnet.satisfaction.dismiss', 'Dismiss')}\n </button>\n </div>\n )}\n\n {loading && !summary ? (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n {tr('planmagnet.satisfaction.loading', 'Loading…')}\n </div>\n ) : error && !summary ? (\n <ErrorLine\n message={\n isForbidden(error)\n ? tr(\n 'planmagnet.satisfaction.errors.readForbidden',\n 'Your role cannot see customer satisfaction in this workspace.'\n )\n : tr(\n 'planmagnet.satisfaction.errors.load',\n 'Customer satisfaction could not be loaded: {{message}}',\n { message: error.message }\n )\n }\n />\n ) : summary ? (\n <>\n {error && (\n <ErrorLine\n message={tr(\n 'planmagnet.satisfaction.errors.refresh',\n 'The figures could not be refreshed: {{message}}',\n { message: error.message }\n )}\n />\n )}\n {updating && (\n <p className=\"flex items-center gap-2 text-xs text-muted-foreground\" role=\"status\">\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" aria-hidden={true} />\n {tr('planmagnet.satisfaction.updating', 'Updating the figures…')}\n </p>\n )}\n <SatisfactionFigureCards summary={summary} />\n </>\n ) : null}\n\n <Card>\n <CardHeader className=\"flex flex-row flex-wrap items-center justify-between gap-2 space-y-0\">\n <CardTitle>{tr('planmagnet.satisfaction.list.title', 'Responses')}</CardTitle>\n <div\n className=\"flex items-center gap-1 rounded-md border p-0.5\"\n role=\"group\"\n aria-label={tr('planmagnet.satisfaction.list.metricFilter', 'Metric')}\n >\n {(['', ...METRIC_TYPES] as const).map((metric) => (\n <button\n key={metric || 'all'}\n type=\"button\"\n aria-pressed={state.metricType === metric}\n onClick={() => change({ metricType: metric })}\n data-testid={`satisfaction-metric-${metric || 'ALL'}`}\n className={`rounded px-2.5 py-1 text-xs ${\n state.metricType === metric\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted'\n }`}\n >\n {metric || tr('planmagnet.satisfaction.list.allMetrics', 'All')}\n </button>\n ))}\n </div>\n </CardHeader>\n <CardContent>\n <SatisfactionResponsesTable\n key={JSON.stringify(responsesFilter)}\n workspaceId={workspaceId}\n filter={responsesFilter}\n access={access}\n links={links}\n filtered={scoped || !!state.metricType}\n onNotice={(message, tone) => setNotice({ message, tone })}\n onStale={refreshAfterStale}\n />\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsProductDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductDetailPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProductsProductDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductDetailPagePage.tsx"],"names":[],"mappings":"AAkIA,wBAAgB,6BAA6B,4CA6N5C"}
|
|
@@ -8,13 +8,14 @@ import { ProductAboutPanel as o, ProductCompetitorsPanel as s, ProductFeatureReq
|
|
|
8
8
|
import { ProductFeaturesPanel as p } from "../product/ProductFeatureControls.js";
|
|
9
9
|
import { ProductLinePicker as m } from "../product/ProductLineControls.js";
|
|
10
10
|
import { ProductTeamsPanel as h } from "../product/ProductTeamsPanel.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
11
|
+
import { SatisfactionSummaryCard as g } from "../satisfaction/SatisfactionSummaryCard.js";
|
|
12
|
+
import { Calendar as _, ListTree as v, Loader2 as y, Package as b, Pencil as x, Target as S, Users as C } from "lucide-react";
|
|
13
|
+
import { gql as w } from "@apollo/client";
|
|
14
|
+
import { useQuery as T } from "@apollo/client/react";
|
|
15
|
+
import { jsx as E, jsxs as D } from "react/jsx-runtime";
|
|
16
|
+
import { Badge as O, Button as k, Card as A, CardContent as j, CardHeader as M, CardTitle as N } from "@burdenoff/fe-libs/ui";
|
|
16
17
|
//#region src/planmagnet/pages/ProductsProductDetailPagePage.tsx
|
|
17
|
-
var
|
|
18
|
+
var P = w`
|
|
18
19
|
query GetProductDetail($id: ID!) {
|
|
19
20
|
planmagnetProduct(id: $id) {
|
|
20
21
|
id
|
|
@@ -69,178 +70,179 @@ var N = C`
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
`;
|
|
72
|
-
function
|
|
73
|
-
let
|
|
74
|
-
variables: { id:
|
|
75
|
-
skip: !
|
|
73
|
+
function F() {
|
|
74
|
+
let w = e(), F = i("productId") ?? "", { data: I, loading: L, error: R } = T(P, {
|
|
75
|
+
variables: { id: F },
|
|
76
|
+
skip: !F
|
|
76
77
|
});
|
|
77
|
-
if (!
|
|
78
|
+
if (!F) return /* @__PURE__ */ E("div", {
|
|
78
79
|
className: "p-6 text-muted-foreground",
|
|
79
80
|
children: "No product selected. Open one from the list."
|
|
80
81
|
});
|
|
81
|
-
if (
|
|
82
|
+
if (L && !I) return /* @__PURE__ */ E("div", {
|
|
82
83
|
className: "flex items-center justify-center p-12",
|
|
83
|
-
children: /* @__PURE__ */
|
|
84
|
+
children: /* @__PURE__ */ E(y, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
84
85
|
});
|
|
85
|
-
if (
|
|
86
|
+
if (R) return /* @__PURE__ */ D("div", {
|
|
86
87
|
className: "p-6 text-destructive",
|
|
87
|
-
children: ["Failed to load product: ",
|
|
88
|
+
children: ["Failed to load product: ", R.message]
|
|
88
89
|
});
|
|
89
|
-
let
|
|
90
|
-
return
|
|
90
|
+
let z = I?.planmagnetProduct;
|
|
91
|
+
return z ? /* @__PURE__ */ D("div", {
|
|
91
92
|
className: "space-y-6 p-6",
|
|
92
93
|
children: [
|
|
93
|
-
/* @__PURE__ */
|
|
94
|
+
/* @__PURE__ */ D("div", {
|
|
94
95
|
className: "flex items-start justify-between gap-4",
|
|
95
|
-
children: [/* @__PURE__ */
|
|
96
|
+
children: [/* @__PURE__ */ D("div", { children: [/* @__PURE__ */ D("div", {
|
|
96
97
|
className: "flex items-center gap-2",
|
|
97
98
|
children: [
|
|
98
|
-
/* @__PURE__ */
|
|
99
|
-
/* @__PURE__ */
|
|
99
|
+
/* @__PURE__ */ E(b, { className: "h-5 w-5 text-primary" }),
|
|
100
|
+
/* @__PURE__ */ E("h1", {
|
|
100
101
|
className: "text-2xl font-semibold",
|
|
101
|
-
children:
|
|
102
|
+
children: z.name
|
|
102
103
|
}),
|
|
103
|
-
|
|
104
|
+
z.archivedAt && /* @__PURE__ */ E(O, {
|
|
104
105
|
variant: "outline",
|
|
105
106
|
children: "Archived"
|
|
106
107
|
}),
|
|
107
|
-
/* @__PURE__ */
|
|
108
|
+
/* @__PURE__ */ E(O, {
|
|
108
109
|
variant: "secondary",
|
|
109
110
|
className: "capitalize",
|
|
110
|
-
children: n(
|
|
111
|
+
children: n(z.lifecycleStage)
|
|
111
112
|
})
|
|
112
113
|
]
|
|
113
|
-
}), /* @__PURE__ */
|
|
114
|
+
}), /* @__PURE__ */ E("p", {
|
|
114
115
|
className: "text-sm text-muted-foreground mt-1",
|
|
115
|
-
children:
|
|
116
|
-
})] }), /* @__PURE__ */
|
|
116
|
+
children: z.vision || "No vision set"
|
|
117
|
+
})] }), /* @__PURE__ */ E("div", {
|
|
117
118
|
className: "flex flex-col items-end gap-2",
|
|
118
|
-
children: /* @__PURE__ */
|
|
119
|
+
children: /* @__PURE__ */ D("div", {
|
|
119
120
|
className: "flex gap-2",
|
|
120
|
-
children: [/* @__PURE__ */
|
|
121
|
+
children: [/* @__PURE__ */ D(k, {
|
|
121
122
|
size: "sm",
|
|
122
123
|
variant: "outline",
|
|
123
|
-
onClick: () =>
|
|
124
|
-
children: [/* @__PURE__ */
|
|
125
|
-
}), /* @__PURE__ */
|
|
126
|
-
productId:
|
|
127
|
-
name:
|
|
128
|
-
archivedAt:
|
|
124
|
+
onClick: () => w(r("/products/edit-product-page", z.id)),
|
|
125
|
+
children: [/* @__PURE__ */ E(x, { className: "mr-1 h-4 w-4" }), "Edit"]
|
|
126
|
+
}), /* @__PURE__ */ E(l, {
|
|
127
|
+
productId: z.id,
|
|
128
|
+
name: z.name,
|
|
129
|
+
archivedAt: z.archivedAt
|
|
129
130
|
})]
|
|
130
131
|
})
|
|
131
132
|
})]
|
|
132
133
|
}),
|
|
133
|
-
/* @__PURE__ */
|
|
134
|
+
/* @__PURE__ */ D("div", {
|
|
134
135
|
className: "grid grid-cols-2 md:grid-cols-4 gap-4",
|
|
135
136
|
children: [
|
|
136
|
-
/* @__PURE__ */
|
|
137
|
+
/* @__PURE__ */ E(A, { children: /* @__PURE__ */ D(j, {
|
|
137
138
|
className: "pt-4",
|
|
138
|
-
children: [/* @__PURE__ */
|
|
139
|
+
children: [/* @__PURE__ */ D("div", {
|
|
139
140
|
className: "flex items-center gap-2 text-muted-foreground text-xs",
|
|
140
|
-
children: [/* @__PURE__ */
|
|
141
|
-
}),
|
|
141
|
+
children: [/* @__PURE__ */ E(S, { className: "h-4 w-4" }), "North Star"]
|
|
142
|
+
}), z.northStar ? /* @__PURE__ */ D("p", {
|
|
142
143
|
className: "text-sm font-bold mt-1 truncate",
|
|
143
144
|
children: [
|
|
144
|
-
|
|
145
|
+
z.northStar.current,
|
|
145
146
|
" / ",
|
|
146
|
-
|
|
147
|
+
z.northStar.target,
|
|
147
148
|
" ",
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
z.northStar.unit,
|
|
150
|
+
z.northStar.progressRatio == null ? "" : ` (${Math.round(z.northStar.progressRatio * 100)}%)`
|
|
150
151
|
]
|
|
151
|
-
}) : /* @__PURE__ */
|
|
152
|
+
}) : /* @__PURE__ */ E("p", {
|
|
152
153
|
className: "text-sm font-medium mt-1 text-muted-foreground",
|
|
153
154
|
children: "Not set"
|
|
154
155
|
})]
|
|
155
156
|
}) }),
|
|
156
|
-
/* @__PURE__ */
|
|
157
|
+
/* @__PURE__ */ E(A, { children: /* @__PURE__ */ D(j, {
|
|
157
158
|
className: "pt-4",
|
|
158
|
-
children: [/* @__PURE__ */
|
|
159
|
+
children: [/* @__PURE__ */ D("div", {
|
|
159
160
|
className: "flex items-center gap-2 text-muted-foreground text-xs",
|
|
160
|
-
children: [/* @__PURE__ */
|
|
161
|
-
}), /* @__PURE__ */
|
|
161
|
+
children: [/* @__PURE__ */ E(C, { className: "h-4 w-4" }), "Owner"]
|
|
162
|
+
}), /* @__PURE__ */ E("p", {
|
|
162
163
|
className: "text-sm font-bold mt-1 truncate",
|
|
163
|
-
children: /* @__PURE__ */
|
|
164
|
+
children: /* @__PURE__ */ E(t, { id: z.ownerId })
|
|
164
165
|
})]
|
|
165
166
|
}) }),
|
|
166
|
-
/* @__PURE__ */
|
|
167
|
+
/* @__PURE__ */ E(A, { children: /* @__PURE__ */ D(j, {
|
|
167
168
|
className: "pt-4",
|
|
168
|
-
children: [/* @__PURE__ */
|
|
169
|
+
children: [/* @__PURE__ */ D("div", {
|
|
169
170
|
className: "flex items-center gap-2 text-muted-foreground text-xs",
|
|
170
|
-
children: [/* @__PURE__ */
|
|
171
|
-
}), /* @__PURE__ */
|
|
171
|
+
children: [/* @__PURE__ */ E(_, { className: "h-4 w-4" }), "Created"]
|
|
172
|
+
}), /* @__PURE__ */ E("p", {
|
|
172
173
|
className: "text-xl font-bold mt-1",
|
|
173
|
-
children: new Date(
|
|
174
|
+
children: new Date(z.createdAt).toLocaleDateString()
|
|
174
175
|
})]
|
|
175
176
|
}) }),
|
|
176
|
-
/* @__PURE__ */
|
|
177
|
+
/* @__PURE__ */ E(A, { children: /* @__PURE__ */ D(j, {
|
|
177
178
|
className: "pt-4",
|
|
178
|
-
children: [/* @__PURE__ */
|
|
179
|
+
children: [/* @__PURE__ */ D("div", {
|
|
179
180
|
className: "flex items-center gap-2 text-muted-foreground text-xs",
|
|
180
|
-
children: [/* @__PURE__ */
|
|
181
|
-
}), /* @__PURE__ */
|
|
181
|
+
children: [/* @__PURE__ */ E(_, { className: "h-4 w-4" }), "Updated"]
|
|
182
|
+
}), /* @__PURE__ */ E("p", {
|
|
182
183
|
className: "text-xl font-bold mt-1",
|
|
183
|
-
children: new Date(
|
|
184
|
+
children: new Date(z.updatedAt).toLocaleDateString()
|
|
184
185
|
})]
|
|
185
186
|
}) })
|
|
186
187
|
]
|
|
187
188
|
}),
|
|
188
|
-
|
|
189
|
+
z.description && /* @__PURE__ */ E(A, { children: /* @__PURE__ */ E(j, {
|
|
189
190
|
className: "pt-4 text-sm text-muted-foreground",
|
|
190
|
-
children:
|
|
191
|
+
children: z.description
|
|
191
192
|
}) }),
|
|
192
|
-
/* @__PURE__ */
|
|
193
|
+
/* @__PURE__ */ D("div", {
|
|
193
194
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
194
|
-
children: [/* @__PURE__ */
|
|
195
|
+
children: [/* @__PURE__ */ D(A, { children: [/* @__PURE__ */ E(M, { children: /* @__PURE__ */ E(N, { children: "Product line" }) }), /* @__PURE__ */ E(j, { children: /* @__PURE__ */ E(m, { productId: z.id }) })] }), /* @__PURE__ */ D(A, {
|
|
195
196
|
className: "lg:col-span-2",
|
|
196
|
-
children: [/* @__PURE__ */
|
|
197
|
+
children: [/* @__PURE__ */ E(M, { children: /* @__PURE__ */ E(N, { children: "Teams" }) }), /* @__PURE__ */ E(j, { children: /* @__PURE__ */ E(h, { productId: z.id }) })]
|
|
197
198
|
})]
|
|
198
199
|
}),
|
|
199
|
-
/* @__PURE__ */
|
|
200
|
+
/* @__PURE__ */ D(A, { children: [/* @__PURE__ */ D(M, {
|
|
200
201
|
className: "flex flex-row items-center justify-between gap-2",
|
|
201
|
-
children: [/* @__PURE__ */
|
|
202
|
+
children: [/* @__PURE__ */ E(N, { children: "Features" }), /* @__PURE__ */ D(k, {
|
|
202
203
|
size: "sm",
|
|
203
204
|
variant: "ghost",
|
|
204
|
-
onClick: () =>
|
|
205
|
+
onClick: () => w("/products/product-hierarchy-page"),
|
|
205
206
|
"data-testid": "product-view-hierarchy",
|
|
206
|
-
children: [/* @__PURE__ */
|
|
207
|
+
children: [/* @__PURE__ */ E(v, { className: "mr-1 h-4 w-4" }), "View hierarchy"]
|
|
207
208
|
})]
|
|
208
|
-
}), /* @__PURE__ */
|
|
209
|
-
productId:
|
|
210
|
-
productArchived: !!
|
|
209
|
+
}), /* @__PURE__ */ E(j, { children: /* @__PURE__ */ E(p, {
|
|
210
|
+
productId: z.id,
|
|
211
|
+
productArchived: !!z.archivedAt
|
|
211
212
|
}) })] }),
|
|
212
|
-
/* @__PURE__ */ E(
|
|
213
|
+
/* @__PURE__ */ E(g, { scope: { productId: z.id } }),
|
|
214
|
+
/* @__PURE__ */ D("div", {
|
|
213
215
|
className: "grid grid-cols-1 lg:grid-cols-2 gap-6",
|
|
214
|
-
children: [/* @__PURE__ */
|
|
215
|
-
productId:
|
|
216
|
-
competitors:
|
|
216
|
+
children: [/* @__PURE__ */ E(o, { product: z }), /* @__PURE__ */ D(A, { children: [/* @__PURE__ */ E(M, { children: /* @__PURE__ */ E(N, { children: "Competitive landscape" }) }), /* @__PURE__ */ E(j, { children: /* @__PURE__ */ E(s, {
|
|
217
|
+
productId: z.id,
|
|
218
|
+
competitors: z.competitors
|
|
217
219
|
}) })] })]
|
|
218
220
|
}),
|
|
219
|
-
/* @__PURE__ */
|
|
221
|
+
/* @__PURE__ */ D("div", {
|
|
220
222
|
className: "grid grid-cols-1 lg:grid-cols-2 gap-6",
|
|
221
223
|
children: [
|
|
222
|
-
/* @__PURE__ */
|
|
223
|
-
productId:
|
|
224
|
-
roadmaps:
|
|
224
|
+
/* @__PURE__ */ D(A, { children: [/* @__PURE__ */ E(M, { children: /* @__PURE__ */ E(N, { children: "Roadmaps" }) }), /* @__PURE__ */ E(j, { children: /* @__PURE__ */ E(f, {
|
|
225
|
+
productId: z.id,
|
|
226
|
+
roadmaps: z.roadmaps
|
|
225
227
|
}) })] }),
|
|
226
|
-
/* @__PURE__ */
|
|
227
|
-
productId:
|
|
228
|
-
releases:
|
|
228
|
+
/* @__PURE__ */ D(A, { children: [/* @__PURE__ */ E(M, { children: /* @__PURE__ */ E(N, { children: "Releases" }) }), /* @__PURE__ */ E(j, { children: /* @__PURE__ */ E(d, {
|
|
229
|
+
productId: z.id,
|
|
230
|
+
releases: z.releases
|
|
229
231
|
}) })] }),
|
|
230
|
-
/* @__PURE__ */
|
|
231
|
-
productId:
|
|
232
|
-
featureRequests:
|
|
232
|
+
/* @__PURE__ */ D(A, { children: [/* @__PURE__ */ E(M, { children: /* @__PURE__ */ E(N, { children: "Feature requests" }) }), /* @__PURE__ */ E(j, { children: /* @__PURE__ */ E(c, {
|
|
233
|
+
productId: z.id,
|
|
234
|
+
featureRequests: z.featureRequests
|
|
233
235
|
}) })] }),
|
|
234
|
-
/* @__PURE__ */
|
|
235
|
-
productId:
|
|
236
|
-
objectives:
|
|
236
|
+
/* @__PURE__ */ D(A, { children: [/* @__PURE__ */ E(M, { children: /* @__PURE__ */ E(N, { children: "OKR Objectives" }) }), /* @__PURE__ */ E(j, { children: /* @__PURE__ */ E(u, {
|
|
237
|
+
productId: z.id,
|
|
238
|
+
objectives: z.objectives
|
|
237
239
|
}) })] })
|
|
238
240
|
]
|
|
239
241
|
})
|
|
240
242
|
]
|
|
241
|
-
}) : /* @__PURE__ */
|
|
243
|
+
}) : /* @__PURE__ */ E(a, {});
|
|
242
244
|
}
|
|
243
245
|
//#endregion
|
|
244
|
-
export {
|
|
246
|
+
export { F as ProductsProductDetailPagePage };
|
|
245
247
|
|
|
246
248
|
//# sourceMappingURL=ProductsProductDetailPagePage.js.map
|