@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
|
@@ -4,8 +4,8 @@ import { throwOnError as n } from "../utils/mutationResult.js";
|
|
|
4
4
|
import { ErrorLine as r } from "../controls/ConfirmButton.js";
|
|
5
5
|
import { detailPath as i } from "../utils/rowNavigation.js";
|
|
6
6
|
import { TeamSelect as a } from "../controls/Pickers.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { refusalMessage as o } from "./refusal.js";
|
|
8
|
+
import { LINK_PRODUCT_TEAM as s, PRODUCT_TEAMS as c, UNLINK_PRODUCT_TEAM as l } from "./hierarchyOperations.js";
|
|
9
9
|
import { useState as u } from "react";
|
|
10
10
|
import { Plus as d, Users as f, X as p } from "lucide-react";
|
|
11
11
|
import { useMutation as m, useQuery as h } from "@apollo/client/react";
|
|
@@ -13,13 +13,13 @@ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
|
13
13
|
import { Button as v } from "@burdenoff/fe-libs/ui";
|
|
14
14
|
//#region src/planmagnet/product/ProductTeamsPanel.tsx
|
|
15
15
|
function y({ productId: y }) {
|
|
16
|
-
let b = t(), x = e(), { data: S, loading: C, error: w } = h(
|
|
16
|
+
let b = t(), x = e(), { data: S, loading: C, error: w } = h(c, {
|
|
17
17
|
variables: { productId: y },
|
|
18
18
|
skip: !y
|
|
19
19
|
}), T = {
|
|
20
20
|
refetchQueries: ["ProductTeams"],
|
|
21
21
|
awaitRefetchQueries: !0
|
|
22
|
-
}, [E, D] = m(
|
|
22
|
+
}, [E, D] = m(s, T), [O, k] = m(l, T), [A, j] = u(""), [M, N] = u(null), [P, F] = u(null), I = S?.planmagnetProduct ?? null, L = I?.teams ?? [], R = !!I?.archivedAt, z = b("planmagnet.productTeams.errors.forbidden", "Your role cannot change which teams a product has."), B = async () => {
|
|
23
23
|
if (A) {
|
|
24
24
|
F(null);
|
|
25
25
|
try {
|
|
@@ -28,7 +28,7 @@ function y({ productId: y }) {
|
|
|
28
28
|
teamId: A
|
|
29
29
|
} })), j("");
|
|
30
30
|
} catch (e) {
|
|
31
|
-
F(
|
|
31
|
+
F(o(e, b("planmagnet.productTeams.errors.link", "The team could not be linked."), z));
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
}, V = async (e) => {
|
|
@@ -39,7 +39,7 @@ function y({ productId: y }) {
|
|
|
39
39
|
teamId: e
|
|
40
40
|
} }));
|
|
41
41
|
} catch (e) {
|
|
42
|
-
F(
|
|
42
|
+
F(o(e, b("planmagnet.productTeams.errors.unlink", "The team could not be unlinked."), z));
|
|
43
43
|
} finally {
|
|
44
44
|
N(null);
|
|
45
45
|
}
|
|
@@ -11,4 +11,10 @@ export declare const PRIORITIZATION_PATH = "products/prioritization-page";
|
|
|
11
11
|
export declare const PRODUCT_LINES_PATH = "products/product-lines-page";
|
|
12
12
|
/** Product hierarchy page (PRM-01): Product line > Product > Feature > Epic > Story. */
|
|
13
13
|
export declare const PRODUCT_HIERARCHY_PATH = "products/product-hierarchy-page";
|
|
14
|
+
/**
|
|
15
|
+
* Customer satisfaction page (ANR-21): CSAT and NPS with their responses, add / edit / delete and the CSV import. It
|
|
16
|
+
* takes `?window=&from=&to=&product=&release=&project=&metric=` so a product, release or delivery-KPI card can open it
|
|
17
|
+
* already filtered (see satisfaction/satisfaction.ts `satisfactionPageLink`).
|
|
18
|
+
*/
|
|
19
|
+
export declare const CUSTOMER_SATISFACTION_PATH = "products/customer-satisfaction-page";
|
|
14
20
|
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/product/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,iCAAiC,CAAC;AAElE,iHAAiH;AACjH,eAAO,MAAM,kBAAkB,gCAAgC,CAAC;AAEhE,wFAAwF;AACxF,eAAO,MAAM,sBAAsB,oCAAoC,CAAC"}
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/product/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,iCAAiC,CAAC;AAElE,iHAAiH;AACjH,eAAO,MAAM,kBAAkB,gCAAgC,CAAC;AAEhE,wFAAwF;AACxF,eAAO,MAAM,sBAAsB,oCAAoC,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,wCAAwC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/planmagnet/product/routes.ts
|
|
2
|
-
var e = "products/prioritization-page", t = "products/product-lines-page", n = "products/product-hierarchy-page";
|
|
2
|
+
var e = "products/prioritization-page", t = "products/product-lines-page", n = "products/product-hierarchy-page", r = "products/customer-satisfaction-page";
|
|
3
3
|
//#endregion
|
|
4
|
-
export { e as PRIORITIZATION_PATH, n as PRODUCT_HIERARCHY_PATH, t as PRODUCT_LINES_PATH };
|
|
4
|
+
export { r as CUSTOMER_SATISFACTION_PATH, e as PRIORITIZATION_PATH, n as PRODUCT_HIERARCHY_PATH, t as PRODUCT_LINES_PATH };
|
|
5
5
|
|
|
6
6
|
//# sourceMappingURL=routes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","names":[],"sources":["../../../src/planmagnet/product/routes.ts"],"sourcesContent":["/**\n * Prioritization page route target (PRM-27), exported so the host shell can build a `NavItem` against the real,\n * routed path instead of a hand-typed string that can drift from `PlanMagnetRoutes.tsx` — same reasoning as\n * `integrations/routes.ts`. Relative, no leading slash — the same form `PlanMagnetRoot.tsx` passes through\n * `joinPath(basePath, ...)` for every other nav entry in this package, and the same form `path=\"…\"` carries in\n * `PlanMagnetRoutes.tsx`. This MFE also registers its own \"Products\" nav tree internally (PlanMagnetRoot.tsx),\n * so the export here is additionally consumed there.\n */\nexport const PRIORITIZATION_PATH = 'products/prioritization-page';\n\n/** Product lines page (PRM-01) — the top of the product hierarchy. Same relative form as PRIORITIZATION_PATH. */\nexport const PRODUCT_LINES_PATH = 'products/product-lines-page';\n\n/** Product hierarchy page (PRM-01): Product line > Product > Feature > Epic > Story. */\nexport const PRODUCT_HIERARCHY_PATH = 'products/product-hierarchy-page';\n"],"mappings":";AAQA,IAAa,IAAsB,gCAGtB,IAAqB,+BAGrB,IAAyB"}
|
|
1
|
+
{"version":3,"file":"routes.js","names":[],"sources":["../../../src/planmagnet/product/routes.ts"],"sourcesContent":["/**\n * Prioritization page route target (PRM-27), exported so the host shell can build a `NavItem` against the real,\n * routed path instead of a hand-typed string that can drift from `PlanMagnetRoutes.tsx` — same reasoning as\n * `integrations/routes.ts`. Relative, no leading slash — the same form `PlanMagnetRoot.tsx` passes through\n * `joinPath(basePath, ...)` for every other nav entry in this package, and the same form `path=\"…\"` carries in\n * `PlanMagnetRoutes.tsx`. This MFE also registers its own \"Products\" nav tree internally (PlanMagnetRoot.tsx),\n * so the export here is additionally consumed there.\n */\nexport const PRIORITIZATION_PATH = 'products/prioritization-page';\n\n/** Product lines page (PRM-01) — the top of the product hierarchy. Same relative form as PRIORITIZATION_PATH. */\nexport const PRODUCT_LINES_PATH = 'products/product-lines-page';\n\n/** Product hierarchy page (PRM-01): Product line > Product > Feature > Epic > Story. */\nexport const PRODUCT_HIERARCHY_PATH = 'products/product-hierarchy-page';\n\n/**\n * Customer satisfaction page (ANR-21): CSAT and NPS with their responses, add / edit / delete and the CSV import. It\n * takes `?window=&from=&to=&product=&release=&project=&metric=` so a product, release or delivery-KPI card can open it\n * already filtered (see satisfaction/satisfaction.ts `satisfactionPageLink`).\n */\nexport const CUSTOMER_SATISFACTION_PATH = 'products/customer-satisfaction-page';\n"],"mappings":";AAQA,IAAa,IAAsB,gCAGtB,IAAqB,+BAGrB,IAAyB,mCAOzB,IAA6B"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ANR-21 on the delivery KPIs card: CSAT — the default delivery KPI — and NPS next to the on-time rate and scope
|
|
3
|
+
* adherence, for the same scope (the workspace, or responses linked to the selected project) and the same UTC days as
|
|
4
|
+
* the rest of the card.
|
|
5
|
+
*
|
|
6
|
+
* It reads `planmagnetCustomerSatisfaction` with the card's own range as a CUSTOM window rather than selecting
|
|
7
|
+
* `planmagnetDeliveryKpis.customerSatisfaction` on the card's query. The figures are the same — the service resolves
|
|
8
|
+
* both from one summary over the same UTC days when dates are given — but a query of its own means a satisfaction
|
|
9
|
+
* failure (or a role that cannot read `planmagnet-product`) leaves the on-time and scope figures standing: a non-null
|
|
10
|
+
* field that fails nulls the whole `planmagnetDeliveryKpis`. A second `planmagnetDeliveryKpis` query just for this
|
|
11
|
+
* field would recompute every other KPI on the server.
|
|
12
|
+
*/
|
|
13
|
+
export declare function DeliverySatisfactionSection({ workspaceId, projectId, startDate, endDate, }: {
|
|
14
|
+
workspaceId: string;
|
|
15
|
+
projectId?: string;
|
|
16
|
+
/** ISO instants: the start of the range's first UTC day and the end of its last. */
|
|
17
|
+
startDate: string;
|
|
18
|
+
endDate: string;
|
|
19
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
//# sourceMappingURL=DeliverySatisfactionSection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeliverySatisfactionSection.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/satisfaction/DeliverySatisfactionSection.tsx"],"names":[],"mappings":"AAaA;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAAC,EAC1C,WAAW,EACX,SAAS,EACT,SAAS,EACT,OAAO,GACR,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,2CAiEA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { usePlanMagnetNavigate 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 { satisfactionPageLink as r, utcDay as i } from "./satisfaction.js";
|
|
5
|
+
import { CompactSatisfactionFigures as a } from "./SatisfactionFigures.js";
|
|
6
|
+
import { CUSTOMER_SATISFACTION_SUMMARY as o } from "./satisfactionOperations.js";
|
|
7
|
+
import { ArrowRight as s } from "lucide-react";
|
|
8
|
+
import { useQuery as c } from "@apollo/client/react";
|
|
9
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
10
|
+
import { Button as d } from "@burdenoff/fe-libs/ui";
|
|
11
|
+
//#region src/planmagnet/satisfaction/DeliverySatisfactionSection.tsx
|
|
12
|
+
function f({ workspaceId: f, projectId: p, startDate: m, endDate: h }) {
|
|
13
|
+
let g = t(), _ = e(), { data: v, loading: y, error: b } = c(o, { variables: {
|
|
14
|
+
workspaceId: f,
|
|
15
|
+
filter: {
|
|
16
|
+
...p ? { projectId: p } : {},
|
|
17
|
+
window: {
|
|
18
|
+
preset: "CUSTOM",
|
|
19
|
+
from: m,
|
|
20
|
+
to: h
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} }), x = v?.planmagnetCustomerSatisfaction, S = i(m), C = i(h);
|
|
24
|
+
return /* @__PURE__ */ u("div", {
|
|
25
|
+
"data-testid": "delivery-satisfaction",
|
|
26
|
+
children: [/* @__PURE__ */ u("div", {
|
|
27
|
+
className: "mb-1 flex items-center justify-between gap-2",
|
|
28
|
+
children: [/* @__PURE__ */ l("h3", {
|
|
29
|
+
className: "text-sm font-medium",
|
|
30
|
+
children: g("planmagnet.analytics.delivery.satisfaction", "Customer satisfaction")
|
|
31
|
+
}), /* @__PURE__ */ u(d, {
|
|
32
|
+
size: "sm",
|
|
33
|
+
variant: "ghost",
|
|
34
|
+
className: "h-7 px-2 text-xs",
|
|
35
|
+
onClick: () => _(r({ projectId: p }, S && C ? {
|
|
36
|
+
preset: "CUSTOM",
|
|
37
|
+
from: S,
|
|
38
|
+
to: C
|
|
39
|
+
} : void 0)),
|
|
40
|
+
"data-testid": "delivery-satisfaction-open",
|
|
41
|
+
children: [g("planmagnet.analytics.delivery.satisfactionOpen", "Responses"), /* @__PURE__ */ l(s, { className: "ml-1 h-3.5 w-3.5" })]
|
|
42
|
+
})]
|
|
43
|
+
}), y && !x ? /* @__PURE__ */ l("p", {
|
|
44
|
+
className: "py-2 text-sm text-muted-foreground",
|
|
45
|
+
children: g("planmagnet.satisfaction.loading", "Loading…")
|
|
46
|
+
}) : b && !x ? /* @__PURE__ */ l("p", {
|
|
47
|
+
className: "py-2 text-sm text-status-error-text",
|
|
48
|
+
children: n(b) ? g("planmagnet.satisfaction.errors.readForbidden", "Your role cannot see customer satisfaction in this workspace.") : g("planmagnet.satisfaction.errors.load", "Customer satisfaction could not be loaded: {{message}}", { message: b.message })
|
|
49
|
+
}) : x ? /* @__PURE__ */ l(a, { summary: x }) : null]
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { f as DeliverySatisfactionSection };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=DeliverySatisfactionSection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeliverySatisfactionSection.js","names":[],"sources":["../../../src/planmagnet/satisfaction/DeliverySatisfactionSection.tsx"],"sourcesContent":["import { useQuery } from '@apollo/client/react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { ArrowRight } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { isForbidden } from '../utils/graphqlErrors';\nimport { CompactSatisfactionFigures } from './SatisfactionFigures';\nimport { satisfactionPageLink, utcDay } from './satisfaction';\nimport {\n CUSTOMER_SATISFACTION_SUMMARY,\n type CustomerSatisfactionSummaryData,\n} from './satisfactionOperations';\n\n/**\n * ANR-21 on the delivery KPIs card: CSAT — the default delivery KPI — and NPS next to the on-time rate and scope\n * adherence, for the same scope (the workspace, or responses linked to the selected project) and the same UTC days as\n * the rest of the card.\n *\n * It reads `planmagnetCustomerSatisfaction` with the card's own range as a CUSTOM window rather than selecting\n * `planmagnetDeliveryKpis.customerSatisfaction` on the card's query. The figures are the same — the service resolves\n * both from one summary over the same UTC days when dates are given — but a query of its own means a satisfaction\n * failure (or a role that cannot read `planmagnet-product`) leaves the on-time and scope figures standing: a non-null\n * field that fails nulls the whole `planmagnetDeliveryKpis`. A second `planmagnetDeliveryKpis` query just for this\n * field would recompute every other KPI on the server.\n */\nexport function DeliverySatisfactionSection({\n workspaceId,\n projectId,\n startDate,\n endDate,\n}: {\n workspaceId: string;\n projectId?: string;\n /** ISO instants: the start of the range's first UTC day and the end of its last. */\n startDate: string;\n endDate: string;\n}) {\n const tr = useTr();\n const navigate = usePlanMagnetNavigate();\n const { data, loading, error } = useQuery<CustomerSatisfactionSummaryData>(\n CUSTOMER_SATISFACTION_SUMMARY,\n {\n variables: {\n workspaceId,\n filter: {\n ...(projectId ? { projectId } : {}),\n window: { preset: 'CUSTOM', from: startDate, to: endDate },\n },\n },\n }\n );\n const summary = data?.planmagnetCustomerSatisfaction;\n const from = utcDay(startDate);\n const to = utcDay(endDate);\n\n return (\n <div data-testid=\"delivery-satisfaction\">\n <div className=\"mb-1 flex items-center justify-between gap-2\">\n <h3 className=\"text-sm font-medium\">\n {tr('planmagnet.analytics.delivery.satisfaction', 'Customer satisfaction')}\n </h3>\n <Button\n size=\"sm\"\n variant=\"ghost\"\n className=\"h-7 px-2 text-xs\"\n onClick={() =>\n navigate(\n satisfactionPageLink(\n { projectId },\n from && to ? { preset: 'CUSTOM', from, to } : undefined\n )\n )\n }\n data-testid=\"delivery-satisfaction-open\"\n >\n {tr('planmagnet.analytics.delivery.satisfactionOpen', 'Responses')}\n <ArrowRight className=\"ml-1 h-3.5 w-3.5\" />\n </Button>\n </div>\n {loading && !summary ? (\n <p className=\"py-2 text-sm text-muted-foreground\">\n {tr('planmagnet.satisfaction.loading', 'Loading…')}\n </p>\n ) : error && !summary ? (\n <p className=\"py-2 text-sm text-status-error-text\">\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 </p>\n ) : summary ? (\n <CompactSatisfactionFigures summary={summary} />\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;AAyBA,SAAgB,EAA4B,EAC1C,gBACA,cACA,cACA,cAOC;CACD,IAAM,IAAK,GAAO,EACZ,IAAW,GAAuB,EAClC,EAAE,SAAM,YAAS,aAAU,EAC/B,GACA,EACE,WAAW;EACT;EACA,QAAQ;GACN,GAAI,IAAY,EAAE,cAAW,GAAG,EAAE;GAClC,QAAQ;IAAE,QAAQ;IAAU,MAAM;IAAW,IAAI;IAAS;GAC3D;EACF,EACF,CACF,EACK,IAAU,GAAM,gCAChB,IAAO,EAAO,EAAU,EACxB,IAAK,EAAO,EAAQ;AAE1B,QACE,kBAAC,OAAD;EAAK,eAAY;YAAjB,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cACX,EAAG,8CAA8C,wBAAwB;IACvE,CAAA,EACL,kBAAC,GAAD;IACE,MAAK;IACL,SAAQ;IACR,WAAU;IACV,eACE,EACE,EACE,EAAE,cAAW,EACb,KAAQ,IAAK;KAAE,QAAQ;KAAU;KAAM;KAAI,GAAG,KAAA,EAC/C,CACF;IAEH,eAAY;cAZd,CAcG,EAAG,kDAAkD,YAAY,EAClE,kBAAC,GAAD,EAAY,WAAU,oBAAqB,CAAA,CACpC;MACL;MACL,KAAW,CAAC,IACX,kBAAC,KAAD;GAAG,WAAU;aACV,EAAG,mCAAmC,WAAW;GAChD,CAAA,GACF,KAAS,CAAC,IACZ,kBAAC,KAAD;GAAG,WAAU;aACV,EAAY,EAAM,GACf,EACE,gDACA,gEACD,GACD,EACE,uCACA,0DACA,EAAE,SAAS,EAAM,SAAS,CAC3B;GACH,CAAA,GACF,IACF,kBAAC,GAAD,EAAqC,YAAW,CAAA,GAC9C,KACA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useTr } from '../../shared/hooks/useTr';
|
|
2
|
+
import { ScoreCategory } from './satisfaction';
|
|
3
|
+
import { CustomerSatisfactionSummary, SatisfactionWindowEcho } from './satisfactionOperations';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ANR-21 figures. Every figure carries its response count and the window it covers (owner decision 2026-10-02), and
|
|
7
|
+
* a metric below its minimum responses reads "Not enough responses" — never 0 and never a score computed from too few
|
|
8
|
+
* answers. CSAT and NPS are counted separately: twelve NPS answers never lend a score to four CSAT answers.
|
|
9
|
+
*/
|
|
10
|
+
type Tr = ReturnType<typeof useTr>;
|
|
11
|
+
export declare function responseCountText(tr: Tr, count: number): string;
|
|
12
|
+
export declare function windowText(tr: Tr, window: SatisfactionWindowEcho): string;
|
|
13
|
+
export declare function windowPresetLabel(tr: Tr, preset: SatisfactionWindowEcho['preset']): string;
|
|
14
|
+
/**
|
|
15
|
+
* Badge colours for one answer's category — semantic status tokens only, and only those the host shells' Tailwind
|
|
16
|
+
* theme maps (planmagnet-app styles.css @theme): `bg-status-*-bg`, `bg-status-*-bg-subtle`, `text-status-success-text`,
|
|
17
|
+
* `text-status-error-text`. A token missing there generates no CSS at all.
|
|
18
|
+
*/
|
|
19
|
+
export declare const CATEGORY_CLASS: Record<ScoreCategory, string>;
|
|
20
|
+
export declare function categoryLabel(tr: Tr, category: ScoreCategory): string;
|
|
21
|
+
/** The page's two figure cards, side by side. */
|
|
22
|
+
export declare function SatisfactionFigureCards({ summary }: {
|
|
23
|
+
summary: CustomerSatisfactionSummary;
|
|
24
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
/** CSAT and NPS as two small tiles with their counts, and the window under them — for cards on other pages. */
|
|
26
|
+
export declare function CompactSatisfactionFigures({ summary }: {
|
|
27
|
+
summary: CustomerSatisfactionSummary;
|
|
28
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=SatisfactionFigures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SatisfactionFigures.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/satisfaction/SatisfactionFigures.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAEjD,OAAO,EAAgD,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAClG,OAAO,KAAK,EAEV,2BAA2B,EAE3B,sBAAsB,EACvB,MAAM,0BAA0B,CAAC;AAElC;;;;GAIG;AAEH,KAAK,EAAE,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AAEnC,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAK/D;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAUzE;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,sBAAsB,CAAC,QAAQ,CAAC,GAAG,MAAM,CAa1F;AAUD;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAOxD,CAAC;AAEF,wBAAgB,aAAa,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,aAAa,GAAG,MAAM,CAarE;AA8JD,iDAAiD;AACjD,wBAAgB,uBAAuB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,2BAA2B,CAAA;CAAE,2CAO5F;AA+BD,+GAA+G;AAC/G,wBAAgB,0BAA0B,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,2BAA2B,CAAA;CAAE,2CAuC/F"}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { cn as t } from "../utils/cn.js";
|
|
3
|
+
import { formatCsat as n, formatNps as r, formatUtcDay as i, shareOf as a } from "./satisfaction.js";
|
|
4
|
+
import { Gauge as o, Smile as s } from "lucide-react";
|
|
5
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
6
|
+
import { Card as u, CardContent as d, CardHeader as f, CardTitle as p } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
//#region src/planmagnet/satisfaction/SatisfactionFigures.tsx
|
|
8
|
+
function m(e, t) {
|
|
9
|
+
return t === 0 ? e("planmagnet.satisfaction.responses.none", "No responses") : t === 1 ? e("planmagnet.satisfaction.responses.one", "1 response") : e("planmagnet.satisfaction.responses.many", "{{count}} responses", { count: t });
|
|
10
|
+
}
|
|
11
|
+
function h(e, t) {
|
|
12
|
+
let n = e("planmagnet.satisfaction.window.span", "{{from}} – {{to}}", {
|
|
13
|
+
from: i(t.from),
|
|
14
|
+
to: i(t.to)
|
|
15
|
+
});
|
|
16
|
+
return t.preset === "CUSTOM" ? n : e("planmagnet.satisfaction.window.presetWithSpan", "{{preset}} ({{span}})", {
|
|
17
|
+
preset: g(e, t.preset),
|
|
18
|
+
span: n
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function g(e, t) {
|
|
22
|
+
switch (t) {
|
|
23
|
+
case "LAST_30_DAYS": return e("planmagnet.satisfaction.window.last30", "Last 30 days");
|
|
24
|
+
case "LAST_90_DAYS": return e("planmagnet.satisfaction.window.last90", "Last 90 days");
|
|
25
|
+
case "LAST_180_DAYS": return e("planmagnet.satisfaction.window.last180", "Last 180 days");
|
|
26
|
+
case "LAST_365_DAYS": return e("planmagnet.satisfaction.window.last365", "Last 365 days");
|
|
27
|
+
default: return e("planmagnet.satisfaction.window.custom", "Custom");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function _(e, t) {
|
|
31
|
+
return e("planmagnet.satisfaction.notEnoughDetail", "{{count}} so far — a score needs at least {{minimum}} responses.", {
|
|
32
|
+
count: t.responseCount,
|
|
33
|
+
minimum: t.minimumResponses
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
var v = {
|
|
37
|
+
satisfied: "bg-status-success-bg-subtle text-status-success-text",
|
|
38
|
+
promoter: "bg-status-success-bg-subtle text-status-success-text",
|
|
39
|
+
passive: "bg-status-warning-bg-subtle text-foreground",
|
|
40
|
+
notSatisfied: "bg-status-error-bg-subtle text-status-error-text",
|
|
41
|
+
detractor: "bg-status-error-bg-subtle text-status-error-text"
|
|
42
|
+
};
|
|
43
|
+
function y(e, t) {
|
|
44
|
+
switch (t) {
|
|
45
|
+
case "satisfied": return e("planmagnet.satisfaction.category.satisfied", "Satisfied");
|
|
46
|
+
case "notSatisfied": return e("planmagnet.satisfaction.category.notSatisfied", "Not satisfied");
|
|
47
|
+
case "promoter": return e("planmagnet.satisfaction.category.promoter", "Promoter");
|
|
48
|
+
case "passive": return e("planmagnet.satisfaction.category.passive", "Passive");
|
|
49
|
+
default: return e("planmagnet.satisfaction.category.detractor", "Detractor");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function b({ nps: n }) {
|
|
53
|
+
let r = e(), i = [
|
|
54
|
+
{
|
|
55
|
+
key: "promoters",
|
|
56
|
+
label: r("planmagnet.satisfaction.nps.promoters", "Promoters (9–10)"),
|
|
57
|
+
count: n.promoterCount,
|
|
58
|
+
bar: "bg-status-success-bg"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
key: "passives",
|
|
62
|
+
label: r("planmagnet.satisfaction.nps.passives", "Passives (7–8)"),
|
|
63
|
+
count: n.passiveCount,
|
|
64
|
+
bar: "bg-status-warning-bg"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
key: "detractors",
|
|
68
|
+
label: r("planmagnet.satisfaction.nps.detractors", "Detractors (0–6)"),
|
|
69
|
+
count: n.detractorCount,
|
|
70
|
+
bar: "bg-status-error-bg"
|
|
71
|
+
}
|
|
72
|
+
], o = !n.notEnoughResponses && n.responseCount > 0;
|
|
73
|
+
return /* @__PURE__ */ l("div", {
|
|
74
|
+
className: "space-y-2",
|
|
75
|
+
"data-testid": "satisfaction-nps-split",
|
|
76
|
+
children: [o && /* @__PURE__ */ c("div", {
|
|
77
|
+
className: "flex h-2 w-full overflow-hidden rounded-full bg-muted",
|
|
78
|
+
"aria-hidden": !0,
|
|
79
|
+
children: i.map((e) => /* @__PURE__ */ c("div", {
|
|
80
|
+
className: e.bar,
|
|
81
|
+
style: { width: `${e.count * 100 / n.responseCount}%` }
|
|
82
|
+
}, e.key))
|
|
83
|
+
}), /* @__PURE__ */ c("dl", {
|
|
84
|
+
className: "grid grid-cols-3 gap-2 text-xs",
|
|
85
|
+
children: i.map((e) => /* @__PURE__ */ l("div", {
|
|
86
|
+
className: "min-w-0",
|
|
87
|
+
children: [/* @__PURE__ */ l("dt", {
|
|
88
|
+
className: "flex items-center gap-1.5 text-muted-foreground",
|
|
89
|
+
children: [/* @__PURE__ */ c("span", {
|
|
90
|
+
className: t("h-2 w-2 shrink-0 rounded-full", e.bar),
|
|
91
|
+
"aria-hidden": !0
|
|
92
|
+
}), /* @__PURE__ */ c("span", {
|
|
93
|
+
className: "truncate",
|
|
94
|
+
children: e.label
|
|
95
|
+
})]
|
|
96
|
+
}), /* @__PURE__ */ c("dd", {
|
|
97
|
+
className: "mt-0.5 font-medium tabular-nums",
|
|
98
|
+
children: o ? r("planmagnet.satisfaction.nps.countWithShare", "{{count}} ({{share}}%)", {
|
|
99
|
+
count: e.count,
|
|
100
|
+
share: a(e.count, n.responseCount)
|
|
101
|
+
}) : e.count
|
|
102
|
+
})]
|
|
103
|
+
}, e.key))
|
|
104
|
+
})]
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function x({ value: t, notEnough: n, testId: r }) {
|
|
108
|
+
let i = e();
|
|
109
|
+
return n || t === null ? /* @__PURE__ */ c("p", {
|
|
110
|
+
className: "text-lg font-semibold text-muted-foreground",
|
|
111
|
+
"data-testid": r,
|
|
112
|
+
children: i("planmagnet.satisfaction.notEnough", "Not enough responses")
|
|
113
|
+
}) : /* @__PURE__ */ c("p", {
|
|
114
|
+
className: "text-3xl font-semibold tabular-nums",
|
|
115
|
+
"data-testid": r,
|
|
116
|
+
children: t
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function S({ csat: t, window: r }) {
|
|
120
|
+
let i = e();
|
|
121
|
+
return /* @__PURE__ */ l(u, {
|
|
122
|
+
"data-testid": "satisfaction-csat-card",
|
|
123
|
+
children: [/* @__PURE__ */ l(f, {
|
|
124
|
+
className: "pb-2",
|
|
125
|
+
children: [/* @__PURE__ */ l(p, {
|
|
126
|
+
className: "flex items-center gap-2 text-base",
|
|
127
|
+
children: [/* @__PURE__ */ c(s, {
|
|
128
|
+
className: "h-4 w-4",
|
|
129
|
+
"aria-hidden": !0
|
|
130
|
+
}), i("planmagnet.satisfaction.csat.title", "CSAT")]
|
|
131
|
+
}), /* @__PURE__ */ c("p", {
|
|
132
|
+
className: "text-xs text-muted-foreground",
|
|
133
|
+
children: i("planmagnet.satisfaction.csat.subtitle", "Customer satisfaction: the share of answers that were a 4 or a 5 on a 1–5 scale. The default delivery KPI.")
|
|
134
|
+
})]
|
|
135
|
+
}), /* @__PURE__ */ l(d, {
|
|
136
|
+
className: "space-y-2",
|
|
137
|
+
children: [
|
|
138
|
+
/* @__PURE__ */ c(x, {
|
|
139
|
+
value: n(t.score),
|
|
140
|
+
notEnough: t.notEnoughResponses,
|
|
141
|
+
testId: "satisfaction-csat-value"
|
|
142
|
+
}),
|
|
143
|
+
/* @__PURE__ */ c("p", {
|
|
144
|
+
className: "text-sm text-muted-foreground",
|
|
145
|
+
children: t.notEnoughResponses ? _(i, t) : i("planmagnet.satisfaction.csat.satisfied", "{{satisfied}} of {{count}} answers were a 4 or a 5.", {
|
|
146
|
+
satisfied: t.satisfiedCount,
|
|
147
|
+
count: t.responseCount
|
|
148
|
+
})
|
|
149
|
+
}),
|
|
150
|
+
/* @__PURE__ */ l("p", {
|
|
151
|
+
className: "text-xs text-muted-foreground",
|
|
152
|
+
"data-testid": "satisfaction-csat-basis",
|
|
153
|
+
children: [
|
|
154
|
+
m(i, t.responseCount),
|
|
155
|
+
" · ",
|
|
156
|
+
h(i, r)
|
|
157
|
+
]
|
|
158
|
+
})
|
|
159
|
+
]
|
|
160
|
+
})]
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function C({ nps: t, window: n }) {
|
|
164
|
+
let i = e();
|
|
165
|
+
return /* @__PURE__ */ l(u, {
|
|
166
|
+
"data-testid": "satisfaction-nps-card",
|
|
167
|
+
children: [/* @__PURE__ */ l(f, {
|
|
168
|
+
className: "pb-2",
|
|
169
|
+
children: [/* @__PURE__ */ l(p, {
|
|
170
|
+
className: "flex items-center gap-2 text-base",
|
|
171
|
+
children: [/* @__PURE__ */ c(o, {
|
|
172
|
+
className: "h-4 w-4",
|
|
173
|
+
"aria-hidden": !0
|
|
174
|
+
}), i("planmagnet.satisfaction.nps.title", "NPS")]
|
|
175
|
+
}), /* @__PURE__ */ c("p", {
|
|
176
|
+
className: "text-xs text-muted-foreground",
|
|
177
|
+
children: i("planmagnet.satisfaction.nps.subtitle", "Net Promoter Score: % promoters (9–10) minus % detractors (0–6) on a 0–10 scale, from −100 to +100.")
|
|
178
|
+
})]
|
|
179
|
+
}), /* @__PURE__ */ l(d, {
|
|
180
|
+
className: "space-y-3",
|
|
181
|
+
children: [
|
|
182
|
+
/* @__PURE__ */ c(x, {
|
|
183
|
+
value: r(t.score),
|
|
184
|
+
notEnough: t.notEnoughResponses,
|
|
185
|
+
testId: "satisfaction-nps-value"
|
|
186
|
+
}),
|
|
187
|
+
t.notEnoughResponses && /* @__PURE__ */ c("p", {
|
|
188
|
+
className: "text-sm text-muted-foreground",
|
|
189
|
+
children: _(i, t)
|
|
190
|
+
}),
|
|
191
|
+
/* @__PURE__ */ c(b, { nps: t }),
|
|
192
|
+
/* @__PURE__ */ l("p", {
|
|
193
|
+
className: "text-xs text-muted-foreground",
|
|
194
|
+
"data-testid": "satisfaction-nps-basis",
|
|
195
|
+
children: [
|
|
196
|
+
m(i, t.responseCount),
|
|
197
|
+
" · ",
|
|
198
|
+
h(i, n)
|
|
199
|
+
]
|
|
200
|
+
})
|
|
201
|
+
]
|
|
202
|
+
})]
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
function w({ summary: e }) {
|
|
206
|
+
return /* @__PURE__ */ l("div", {
|
|
207
|
+
className: "grid gap-4 md:grid-cols-2",
|
|
208
|
+
"data-testid": "satisfaction-figures",
|
|
209
|
+
children: [/* @__PURE__ */ c(S, {
|
|
210
|
+
csat: e.csat,
|
|
211
|
+
window: e.window
|
|
212
|
+
}), /* @__PURE__ */ c(C, {
|
|
213
|
+
nps: e.nps,
|
|
214
|
+
window: e.window
|
|
215
|
+
})]
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
function T({ label: t, value: n, notEnough: r, responseCount: i, testId: a }) {
|
|
219
|
+
let o = e();
|
|
220
|
+
return /* @__PURE__ */ l("div", {
|
|
221
|
+
className: "rounded-md border p-3",
|
|
222
|
+
"data-testid": a,
|
|
223
|
+
children: [
|
|
224
|
+
/* @__PURE__ */ c("p", {
|
|
225
|
+
className: "text-xs text-muted-foreground",
|
|
226
|
+
children: t
|
|
227
|
+
}),
|
|
228
|
+
r || n === null ? /* @__PURE__ */ c("p", {
|
|
229
|
+
className: "mt-1 text-sm font-semibold text-muted-foreground",
|
|
230
|
+
children: o("planmagnet.satisfaction.notEnough", "Not enough responses")
|
|
231
|
+
}) : /* @__PURE__ */ c("p", {
|
|
232
|
+
className: "mt-1 text-2xl font-semibold tabular-nums",
|
|
233
|
+
children: n
|
|
234
|
+
}),
|
|
235
|
+
/* @__PURE__ */ c("p", {
|
|
236
|
+
className: "mt-0.5 text-xs text-muted-foreground",
|
|
237
|
+
children: m(o, i)
|
|
238
|
+
})
|
|
239
|
+
]
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
function E({ summary: t }) {
|
|
243
|
+
let i = e(), { csat: a, nps: o } = t;
|
|
244
|
+
return /* @__PURE__ */ l("div", {
|
|
245
|
+
className: "space-y-2",
|
|
246
|
+
children: [
|
|
247
|
+
/* @__PURE__ */ l("div", {
|
|
248
|
+
className: "grid grid-cols-2 gap-3",
|
|
249
|
+
children: [/* @__PURE__ */ c(T, {
|
|
250
|
+
label: i("planmagnet.satisfaction.csat.compactLabel", "CSAT (% of answers 4–5)"),
|
|
251
|
+
value: n(a.score),
|
|
252
|
+
notEnough: a.notEnoughResponses,
|
|
253
|
+
responseCount: a.responseCount,
|
|
254
|
+
testId: "satisfaction-compact-csat"
|
|
255
|
+
}), /* @__PURE__ */ c(T, {
|
|
256
|
+
label: i("planmagnet.satisfaction.nps.compactLabel", "NPS"),
|
|
257
|
+
value: r(o.score),
|
|
258
|
+
notEnough: o.notEnoughResponses,
|
|
259
|
+
responseCount: o.responseCount,
|
|
260
|
+
testId: "satisfaction-compact-nps"
|
|
261
|
+
})]
|
|
262
|
+
}),
|
|
263
|
+
!o.notEnoughResponses && /* @__PURE__ */ c("p", {
|
|
264
|
+
className: "text-xs text-muted-foreground",
|
|
265
|
+
children: i("planmagnet.satisfaction.nps.compactSplit", "NPS: {{promoters}} promoters · {{passives}} passives · {{detractors}} detractors", {
|
|
266
|
+
promoters: o.promoterCount,
|
|
267
|
+
passives: o.passiveCount,
|
|
268
|
+
detractors: o.detractorCount
|
|
269
|
+
})
|
|
270
|
+
}),
|
|
271
|
+
/* @__PURE__ */ c("p", {
|
|
272
|
+
className: "text-xs text-muted-foreground",
|
|
273
|
+
"data-testid": "satisfaction-compact-window",
|
|
274
|
+
children: h(i, t.window)
|
|
275
|
+
})
|
|
276
|
+
]
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
//#endregion
|
|
280
|
+
export { v as CATEGORY_CLASS, E as CompactSatisfactionFigures, w as SatisfactionFigureCards, y as categoryLabel, g as windowPresetLabel };
|
|
281
|
+
|
|
282
|
+
//# sourceMappingURL=SatisfactionFigures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SatisfactionFigures.js","names":[],"sources":["../../../src/planmagnet/satisfaction/SatisfactionFigures.tsx"],"sourcesContent":["import { Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { Gauge, Smile } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { cn } from '../utils/cn';\nimport { formatCsat, formatNps, formatUtcDay, shareOf, type ScoreCategory } from './satisfaction';\nimport type {\n CsatFigure,\n CustomerSatisfactionSummary,\n NpsFigure,\n SatisfactionWindowEcho,\n} from './satisfactionOperations';\n\n/**\n * ANR-21 figures. Every figure carries its response count and the window it covers (owner decision 2026-10-02), and\n * a metric below its minimum responses reads \"Not enough responses\" — never 0 and never a score computed from too few\n * answers. CSAT and NPS are counted separately: twelve NPS answers never lend a score to four CSAT answers.\n */\n\ntype Tr = ReturnType<typeof useTr>;\n\nexport function responseCountText(tr: Tr, count: number): string {\n if (count === 0) return tr('planmagnet.satisfaction.responses.none', 'No responses');\n return count === 1\n ? tr('planmagnet.satisfaction.responses.one', '1 response')\n : tr('planmagnet.satisfaction.responses.many', '{{count}} responses', { count });\n}\n\nexport function windowText(tr: Tr, window: SatisfactionWindowEcho): string {\n const span = tr('planmagnet.satisfaction.window.span', '{{from}} – {{to}}', {\n from: formatUtcDay(window.from),\n to: formatUtcDay(window.to),\n });\n if (window.preset === 'CUSTOM') return span;\n return tr('planmagnet.satisfaction.window.presetWithSpan', '{{preset}} ({{span}})', {\n preset: windowPresetLabel(tr, window.preset),\n span,\n });\n}\n\nexport function windowPresetLabel(tr: Tr, preset: SatisfactionWindowEcho['preset']): string {\n switch (preset) {\n case 'LAST_30_DAYS':\n return tr('planmagnet.satisfaction.window.last30', 'Last 30 days');\n case 'LAST_90_DAYS':\n return tr('planmagnet.satisfaction.window.last90', 'Last 90 days');\n case 'LAST_180_DAYS':\n return tr('planmagnet.satisfaction.window.last180', 'Last 180 days');\n case 'LAST_365_DAYS':\n return tr('planmagnet.satisfaction.window.last365', 'Last 365 days');\n default:\n return tr('planmagnet.satisfaction.window.custom', 'Custom');\n }\n}\n\nfunction notEnoughText(tr: Tr, figure: { responseCount: number; minimumResponses: number }) {\n return tr(\n 'planmagnet.satisfaction.notEnoughDetail',\n '{{count}} so far — a score needs at least {{minimum}} responses.',\n { count: figure.responseCount, minimum: figure.minimumResponses }\n );\n}\n\n/**\n * Badge colours for one answer's category — semantic status tokens only, and only those the host shells' Tailwind\n * theme maps (planmagnet-app styles.css @theme): `bg-status-*-bg`, `bg-status-*-bg-subtle`, `text-status-success-text`,\n * `text-status-error-text`. A token missing there generates no CSS at all.\n */\nexport const CATEGORY_CLASS: Record<ScoreCategory, string> = {\n satisfied: 'bg-status-success-bg-subtle text-status-success-text',\n promoter: 'bg-status-success-bg-subtle text-status-success-text',\n // The shells' Tailwind theme maps no warning TEXT colour (only its backgrounds), so a passive reads in the body colour.\n passive: 'bg-status-warning-bg-subtle text-foreground',\n notSatisfied: 'bg-status-error-bg-subtle text-status-error-text',\n detractor: 'bg-status-error-bg-subtle text-status-error-text',\n};\n\nexport function categoryLabel(tr: Tr, category: ScoreCategory): string {\n switch (category) {\n case 'satisfied':\n return tr('planmagnet.satisfaction.category.satisfied', 'Satisfied');\n case 'notSatisfied':\n return tr('planmagnet.satisfaction.category.notSatisfied', 'Not satisfied');\n case 'promoter':\n return tr('planmagnet.satisfaction.category.promoter', 'Promoter');\n case 'passive':\n return tr('planmagnet.satisfaction.category.passive', 'Passive');\n default:\n return tr('planmagnet.satisfaction.category.detractor', 'Detractor');\n }\n}\n\n/** Promoters / passives / detractors as a bar (only once there is a score) and as counts (always). */\nfunction NpsSplit({ nps }: { nps: NpsFigure }) {\n const tr = useTr();\n const parts = [\n {\n key: 'promoters',\n label: tr('planmagnet.satisfaction.nps.promoters', 'Promoters (9–10)'),\n count: nps.promoterCount,\n bar: 'bg-status-success-bg',\n },\n {\n key: 'passives',\n label: tr('planmagnet.satisfaction.nps.passives', 'Passives (7–8)'),\n count: nps.passiveCount,\n bar: 'bg-status-warning-bg',\n },\n {\n key: 'detractors',\n label: tr('planmagnet.satisfaction.nps.detractors', 'Detractors (0–6)'),\n count: nps.detractorCount,\n bar: 'bg-status-error-bg',\n },\n ];\n const showShares = !nps.notEnoughResponses && nps.responseCount > 0;\n return (\n <div className=\"space-y-2\" data-testid=\"satisfaction-nps-split\">\n {showShares && (\n <div className=\"flex h-2 w-full overflow-hidden rounded-full bg-muted\" aria-hidden={true}>\n {parts.map((part) => (\n <div\n key={part.key}\n className={part.bar}\n style={{ width: `${(part.count * 100) / nps.responseCount}%` }}\n />\n ))}\n </div>\n )}\n <dl className=\"grid grid-cols-3 gap-2 text-xs\">\n {parts.map((part) => (\n <div key={part.key} className=\"min-w-0\">\n <dt className=\"flex items-center gap-1.5 text-muted-foreground\">\n <span className={cn('h-2 w-2 shrink-0 rounded-full', part.bar)} aria-hidden={true} />\n <span className=\"truncate\">{part.label}</span>\n </dt>\n <dd className=\"mt-0.5 font-medium tabular-nums\">\n {showShares\n ? tr('planmagnet.satisfaction.nps.countWithShare', '{{count}} ({{share}}%)', {\n count: part.count,\n share: shareOf(part.count, nps.responseCount),\n })\n : part.count}\n </dd>\n </div>\n ))}\n </dl>\n </div>\n );\n}\n\nfunction FigureValue({\n value,\n notEnough,\n testId,\n}: {\n value: string | null;\n notEnough: boolean;\n testId: string;\n}) {\n const tr = useTr();\n if (notEnough || value === null) {\n return (\n <p className=\"text-lg font-semibold text-muted-foreground\" data-testid={testId}>\n {tr('planmagnet.satisfaction.notEnough', 'Not enough responses')}\n </p>\n );\n }\n return (\n <p className=\"text-3xl font-semibold tabular-nums\" data-testid={testId}>\n {value}\n </p>\n );\n}\n\nfunction CsatCard({ csat, window }: { csat: CsatFigure; window: SatisfactionWindowEcho }) {\n const tr = useTr();\n return (\n <Card data-testid=\"satisfaction-csat-card\">\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Smile className=\"h-4 w-4\" aria-hidden={true} />\n {tr('planmagnet.satisfaction.csat.title', 'CSAT')}\n </CardTitle>\n <p className=\"text-xs text-muted-foreground\">\n {tr(\n 'planmagnet.satisfaction.csat.subtitle',\n 'Customer satisfaction: the share of answers that were a 4 or a 5 on a 1–5 scale. The default delivery KPI.'\n )}\n </p>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <FigureValue\n value={formatCsat(csat.score)}\n notEnough={csat.notEnoughResponses}\n testId=\"satisfaction-csat-value\"\n />\n <p className=\"text-sm text-muted-foreground\">\n {csat.notEnoughResponses\n ? notEnoughText(tr, csat)\n : tr(\n 'planmagnet.satisfaction.csat.satisfied',\n '{{satisfied}} of {{count}} answers were a 4 or a 5.',\n { satisfied: csat.satisfiedCount, count: csat.responseCount }\n )}\n </p>\n <p className=\"text-xs text-muted-foreground\" data-testid=\"satisfaction-csat-basis\">\n {responseCountText(tr, csat.responseCount)} · {windowText(tr, window)}\n </p>\n </CardContent>\n </Card>\n );\n}\n\nfunction NpsCard({ nps, window }: { nps: NpsFigure; window: SatisfactionWindowEcho }) {\n const tr = useTr();\n return (\n <Card data-testid=\"satisfaction-nps-card\">\n <CardHeader className=\"pb-2\">\n <CardTitle className=\"flex items-center gap-2 text-base\">\n <Gauge className=\"h-4 w-4\" aria-hidden={true} />\n {tr('planmagnet.satisfaction.nps.title', 'NPS')}\n </CardTitle>\n <p className=\"text-xs text-muted-foreground\">\n {tr(\n 'planmagnet.satisfaction.nps.subtitle',\n 'Net Promoter Score: % promoters (9–10) minus % detractors (0–6) on a 0–10 scale, from −100 to +100.'\n )}\n </p>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <FigureValue\n value={formatNps(nps.score)}\n notEnough={nps.notEnoughResponses}\n testId=\"satisfaction-nps-value\"\n />\n {nps.notEnoughResponses && (\n <p className=\"text-sm text-muted-foreground\">{notEnoughText(tr, nps)}</p>\n )}\n <NpsSplit nps={nps} />\n <p className=\"text-xs text-muted-foreground\" data-testid=\"satisfaction-nps-basis\">\n {responseCountText(tr, nps.responseCount)} · {windowText(tr, window)}\n </p>\n </CardContent>\n </Card>\n );\n}\n\n/** The page's two figure cards, side by side. */\nexport function SatisfactionFigureCards({ summary }: { summary: CustomerSatisfactionSummary }) {\n return (\n <div className=\"grid gap-4 md:grid-cols-2\" data-testid=\"satisfaction-figures\">\n <CsatCard csat={summary.csat} window={summary.window} />\n <NpsCard nps={summary.nps} window={summary.window} />\n </div>\n );\n}\n\nfunction CompactTile({\n label,\n value,\n notEnough,\n responseCount,\n testId,\n}: {\n label: string;\n value: string | null;\n notEnough: boolean;\n responseCount: number;\n testId: string;\n}) {\n const tr = useTr();\n return (\n <div className=\"rounded-md border p-3\" data-testid={testId}>\n <p className=\"text-xs text-muted-foreground\">{label}</p>\n {notEnough || value === null ? (\n <p className=\"mt-1 text-sm font-semibold text-muted-foreground\">\n {tr('planmagnet.satisfaction.notEnough', 'Not enough responses')}\n </p>\n ) : (\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">{value}</p>\n )}\n <p className=\"mt-0.5 text-xs text-muted-foreground\">{responseCountText(tr, responseCount)}</p>\n </div>\n );\n}\n\n/** CSAT and NPS as two small tiles with their counts, and the window under them — for cards on other pages. */\nexport function CompactSatisfactionFigures({ summary }: { summary: CustomerSatisfactionSummary }) {\n const tr = useTr();\n const { csat, nps } = summary;\n return (\n <div className=\"space-y-2\">\n <div className=\"grid grid-cols-2 gap-3\">\n <CompactTile\n label={tr('planmagnet.satisfaction.csat.compactLabel', 'CSAT (% of answers 4–5)')}\n value={formatCsat(csat.score)}\n notEnough={csat.notEnoughResponses}\n responseCount={csat.responseCount}\n testId=\"satisfaction-compact-csat\"\n />\n <CompactTile\n label={tr('planmagnet.satisfaction.nps.compactLabel', 'NPS')}\n value={formatNps(nps.score)}\n notEnough={nps.notEnoughResponses}\n responseCount={nps.responseCount}\n testId=\"satisfaction-compact-nps\"\n />\n </div>\n {!nps.notEnoughResponses && (\n <p className=\"text-xs text-muted-foreground\">\n {tr(\n 'planmagnet.satisfaction.nps.compactSplit',\n 'NPS: {{promoters}} promoters · {{passives}} passives · {{detractors}} detractors',\n {\n promoters: nps.promoterCount,\n passives: nps.passiveCount,\n detractors: nps.detractorCount,\n }\n )}\n </p>\n )}\n <p className=\"text-xs text-muted-foreground\" data-testid=\"satisfaction-compact-window\">\n {windowText(tr, summary.window)}\n </p>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DefaultLinks } from './satisfactionCsv';
|
|
2
|
+
import { ImportCustomerSatisfactionResult } from './satisfactionOperations';
|
|
3
|
+
|
|
4
|
+
export declare function SatisfactionImportDialog({ links, onImported, }: {
|
|
5
|
+
/** Starting links for rows that name none — the page's product / release / project filters. */
|
|
6
|
+
links: DefaultLinks;
|
|
7
|
+
onImported: (result: ImportCustomerSatisfactionResult) => void;
|
|
8
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=SatisfactionImportDialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SatisfactionImportDialog.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/satisfaction/SatisfactionImportDialog.tsx"],"names":[],"mappings":"AA+BA,OAAO,EAQL,KAAK,YAAY,EAOlB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAGL,KAAK,gCAAgC,EACtC,MAAM,0BAA0B,CAAC;AAkPlC,wBAAgB,wBAAwB,CAAC,EACvC,KAAK,EACL,UAAU,GACX,EAAE;IACD,+FAA+F;IAC/F,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,CAAC,MAAM,EAAE,gCAAgC,KAAK,IAAI,CAAC;CAChE,2CA8aA"}
|