@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.
Files changed (165) hide show
  1. package/dist/generated/wspace-operations.d.ts +574 -0
  2. package/dist/generated/wspace-operations.d.ts.map +1 -1
  3. package/dist/generated/wspace-types.d.ts +486 -16
  4. package/dist/generated/wspace-types.d.ts.map +1 -1
  5. package/dist/index.js +8 -8
  6. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  7. package/dist/planmagnet/PlanMagnetRoot.js +174 -168
  8. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  9. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  10. package/dist/planmagnet/PlanMagnetRoutes.js +63 -59
  11. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  12. package/dist/planmagnet/board/BulkActions.d.ts.map +1 -1
  13. package/dist/planmagnet/board/BulkActions.js +106 -55
  14. package/dist/planmagnet/board/BulkActions.js.map +1 -1
  15. package/dist/planmagnet/controls/Pickers.d.ts +3 -3
  16. package/dist/planmagnet/controls/Pickers.js.map +1 -1
  17. package/dist/planmagnet/dashboards/DashboardUI.d.ts +0 -4
  18. package/dist/planmagnet/dashboards/DashboardUI.d.ts.map +1 -1
  19. package/dist/planmagnet/dashboards/DashboardUI.js +6 -13
  20. package/dist/planmagnet/dashboards/DashboardUI.js.map +1 -1
  21. package/dist/planmagnet/dashboards/cards/SprintCapacityCard.d.ts.map +1 -1
  22. package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js +70 -59
  23. package/dist/planmagnet/dashboards/cards/SprintCapacityCard.js.map +1 -1
  24. package/dist/planmagnet/forecast/ResourceForecastCard.js +1 -1
  25. package/dist/planmagnet/forecast/ResourceForecastCard.js.map +1 -1
  26. package/dist/planmagnet/index.d.ts +1 -1
  27. package/dist/planmagnet/index.d.ts.map +1 -1
  28. package/dist/planmagnet/pages/DashboardsProjectManagerPagePage.d.ts +3 -3
  29. package/dist/planmagnet/pages/DashboardsProjectManagerPagePage.d.ts.map +1 -1
  30. package/dist/planmagnet/pages/DashboardsProjectManagerPagePage.js +26 -19
  31. package/dist/planmagnet/pages/DashboardsProjectManagerPagePage.js.map +1 -1
  32. package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.d.ts +2 -2
  33. package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.d.ts.map +1 -1
  34. package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.js +18 -12
  35. package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.js.map +1 -1
  36. package/dist/planmagnet/pages/ProductsCustomerSatisfactionPagePage.d.ts +2 -0
  37. package/dist/planmagnet/pages/ProductsCustomerSatisfactionPagePage.d.ts.map +1 -0
  38. package/dist/planmagnet/pages/ProductsCustomerSatisfactionPagePage.js +251 -0
  39. package/dist/planmagnet/pages/ProductsCustomerSatisfactionPagePage.js.map +1 -0
  40. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  41. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +94 -92
  42. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  43. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
  44. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +116 -114
  45. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
  46. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  47. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +174 -159
  48. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  49. package/dist/planmagnet/pages/TasksCreateTaskPagePage.d.ts.map +1 -1
  50. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +159 -145
  51. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
  52. package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
  53. package/dist/planmagnet/pages/TasksEditTaskPagePage.js +161 -143
  54. package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
  55. package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
  56. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +157 -155
  57. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
  58. package/dist/planmagnet/pages/TeamInviteMemberPagePage.d.ts.map +1 -1
  59. package/dist/planmagnet/pages/TeamInviteMemberPagePage.js +142 -82
  60. package/dist/planmagnet/pages/TeamInviteMemberPagePage.js.map +1 -1
  61. package/dist/planmagnet/pages/analytics/DeliveryKpisCard.d.ts.map +1 -1
  62. package/dist/planmagnet/pages/analytics/DeliveryKpisCard.js +97 -99
  63. package/dist/planmagnet/pages/analytics/DeliveryKpisCard.js.map +1 -1
  64. package/dist/planmagnet/product/FeatureLinkPanels.js +66 -66
  65. package/dist/planmagnet/product/ProductFeatureControls.js +38 -38
  66. package/dist/planmagnet/product/ProductFeatureLinkCard.js +8 -8
  67. package/dist/planmagnet/product/ProductLineControls.js +83 -83
  68. package/dist/planmagnet/product/ProductTeamsPanel.js +6 -6
  69. package/dist/planmagnet/product/routes.d.ts +6 -0
  70. package/dist/planmagnet/product/routes.d.ts.map +1 -1
  71. package/dist/planmagnet/product/routes.js +2 -2
  72. package/dist/planmagnet/product/routes.js.map +1 -1
  73. package/dist/planmagnet/satisfaction/DeliverySatisfactionSection.d.ts +20 -0
  74. package/dist/planmagnet/satisfaction/DeliverySatisfactionSection.d.ts.map +1 -0
  75. package/dist/planmagnet/satisfaction/DeliverySatisfactionSection.js +55 -0
  76. package/dist/planmagnet/satisfaction/DeliverySatisfactionSection.js.map +1 -0
  77. package/dist/planmagnet/satisfaction/SatisfactionFigures.d.ts +30 -0
  78. package/dist/planmagnet/satisfaction/SatisfactionFigures.d.ts.map +1 -0
  79. package/dist/planmagnet/satisfaction/SatisfactionFigures.js +282 -0
  80. package/dist/planmagnet/satisfaction/SatisfactionFigures.js.map +1 -0
  81. package/dist/planmagnet/satisfaction/SatisfactionImportDialog.d.ts +9 -0
  82. package/dist/planmagnet/satisfaction/SatisfactionImportDialog.d.ts.map +1 -0
  83. package/dist/planmagnet/satisfaction/SatisfactionImportDialog.js +390 -0
  84. package/dist/planmagnet/satisfaction/SatisfactionImportDialog.js.map +1 -0
  85. package/dist/planmagnet/satisfaction/SatisfactionLinkFields.d.ts +17 -0
  86. package/dist/planmagnet/satisfaction/SatisfactionLinkFields.d.ts.map +1 -0
  87. package/dist/planmagnet/satisfaction/SatisfactionLinkFields.js +77 -0
  88. package/dist/planmagnet/satisfaction/SatisfactionLinkFields.js.map +1 -0
  89. package/dist/planmagnet/satisfaction/SatisfactionReleaseSelect.d.ts +18 -0
  90. package/dist/planmagnet/satisfaction/SatisfactionReleaseSelect.d.ts.map +1 -0
  91. package/dist/planmagnet/satisfaction/SatisfactionReleaseSelect.js +87 -0
  92. package/dist/planmagnet/satisfaction/SatisfactionReleaseSelect.js.map +1 -0
  93. package/dist/planmagnet/satisfaction/SatisfactionResponseDialog.d.ts +13 -0
  94. package/dist/planmagnet/satisfaction/SatisfactionResponseDialog.d.ts.map +1 -0
  95. package/dist/planmagnet/satisfaction/SatisfactionResponseDialog.js +326 -0
  96. package/dist/planmagnet/satisfaction/SatisfactionResponseDialog.js.map +1 -0
  97. package/dist/planmagnet/satisfaction/SatisfactionResponsesTable.d.ts +23 -0
  98. package/dist/planmagnet/satisfaction/SatisfactionResponsesTable.d.ts.map +1 -0
  99. package/dist/planmagnet/satisfaction/SatisfactionResponsesTable.js +258 -0
  100. package/dist/planmagnet/satisfaction/SatisfactionResponsesTable.js.map +1 -0
  101. package/dist/planmagnet/satisfaction/SatisfactionSummaryCard.d.ts +6 -0
  102. package/dist/planmagnet/satisfaction/SatisfactionSummaryCard.d.ts.map +1 -0
  103. package/dist/planmagnet/satisfaction/SatisfactionSummaryCard.js +56 -0
  104. package/dist/planmagnet/satisfaction/SatisfactionSummaryCard.js.map +1 -0
  105. package/dist/planmagnet/satisfaction/SatisfactionWindowPicker.d.ts +16 -0
  106. package/dist/planmagnet/satisfaction/SatisfactionWindowPicker.d.ts.map +1 -0
  107. package/dist/planmagnet/satisfaction/SatisfactionWindowPicker.js +102 -0
  108. package/dist/planmagnet/satisfaction/SatisfactionWindowPicker.js.map +1 -0
  109. package/dist/planmagnet/satisfaction/satisfaction.d.ts +154 -0
  110. package/dist/planmagnet/satisfaction/satisfaction.d.ts.map +1 -0
  111. package/dist/planmagnet/satisfaction/satisfaction.js +210 -0
  112. package/dist/planmagnet/satisfaction/satisfaction.js.map +1 -0
  113. package/dist/planmagnet/satisfaction/satisfactionCsv.d.ts +108 -0
  114. package/dist/planmagnet/satisfaction/satisfactionCsv.d.ts.map +1 -0
  115. package/dist/planmagnet/satisfaction/satisfactionCsv.js +211 -0
  116. package/dist/planmagnet/satisfaction/satisfactionCsv.js.map +1 -0
  117. package/dist/planmagnet/satisfaction/satisfactionForm.d.ts +90 -0
  118. package/dist/planmagnet/satisfaction/satisfactionForm.d.ts.map +1 -0
  119. package/dist/planmagnet/satisfaction/satisfactionForm.js +90 -0
  120. package/dist/planmagnet/satisfaction/satisfactionForm.js.map +1 -0
  121. package/dist/planmagnet/satisfaction/satisfactionOperations.d.ts +116 -0
  122. package/dist/planmagnet/satisfaction/satisfactionOperations.d.ts.map +1 -0
  123. package/dist/planmagnet/satisfaction/satisfactionOperations.js +86 -0
  124. package/dist/planmagnet/satisfaction/satisfactionOperations.js.map +1 -0
  125. package/dist/planmagnet/satisfaction/useSatisfactionAccess.d.ts +20 -0
  126. package/dist/planmagnet/satisfaction/useSatisfactionAccess.d.ts.map +1 -0
  127. package/dist/planmagnet/satisfaction/useSatisfactionAccess.js +31 -0
  128. package/dist/planmagnet/satisfaction/useSatisfactionAccess.js.map +1 -0
  129. package/dist/planmagnet/skills/RequiredSkillsEditor.d.ts +35 -0
  130. package/dist/planmagnet/skills/RequiredSkillsEditor.d.ts.map +1 -0
  131. package/dist/planmagnet/skills/RequiredSkillsEditor.js +124 -0
  132. package/dist/planmagnet/skills/RequiredSkillsEditor.js.map +1 -0
  133. package/dist/planmagnet/skills/SkillChips.d.ts +30 -0
  134. package/dist/planmagnet/skills/SkillChips.d.ts.map +1 -0
  135. package/dist/planmagnet/skills/SkillChips.js +57 -0
  136. package/dist/planmagnet/skills/SkillChips.js.map +1 -0
  137. package/dist/planmagnet/skills/SkillCoverageCard.d.ts +24 -0
  138. package/dist/planmagnet/skills/SkillCoverageCard.d.ts.map +1 -0
  139. package/dist/planmagnet/skills/SkillCoverageCard.js +256 -0
  140. package/dist/planmagnet/skills/SkillCoverageCard.js.map +1 -0
  141. package/dist/planmagnet/skills/WorkItemRequiredSkills.d.ts +10 -0
  142. package/dist/planmagnet/skills/WorkItemRequiredSkills.d.ts.map +1 -0
  143. package/dist/planmagnet/skills/WorkItemRequiredSkills.js +65 -0
  144. package/dist/planmagnet/skills/WorkItemRequiredSkills.js.map +1 -0
  145. package/dist/planmagnet/skills/skillOperations.d.ts +52 -0
  146. package/dist/planmagnet/skills/skillOperations.d.ts.map +1 -0
  147. package/dist/planmagnet/skills/skillOperations.js +44 -0
  148. package/dist/planmagnet/skills/skillOperations.js.map +1 -0
  149. package/dist/planmagnet/skills/skillTags.d.ts +103 -0
  150. package/dist/planmagnet/skills/skillTags.d.ts.map +1 -0
  151. package/dist/planmagnet/skills/skillTags.js +115 -0
  152. package/dist/planmagnet/skills/skillTags.js.map +1 -0
  153. package/dist/planmagnet/skills/useRequiredSkillsDraft.d.ts +28 -0
  154. package/dist/planmagnet/skills/useRequiredSkillsDraft.d.ts.map +1 -0
  155. package/dist/planmagnet/skills/useRequiredSkillsDraft.js +30 -0
  156. package/dist/planmagnet/skills/useRequiredSkillsDraft.js.map +1 -0
  157. package/dist/planmagnet/team/PendingInvitations.d.ts +27 -0
  158. package/dist/planmagnet/team/PendingInvitations.d.ts.map +1 -0
  159. package/dist/planmagnet/team/PendingInvitations.js +220 -0
  160. package/dist/planmagnet/team/PendingInvitations.js.map +1 -0
  161. package/dist/planmagnet/team/invitationEmail.d.ts +35 -0
  162. package/dist/planmagnet/team/invitationEmail.d.ts.map +1 -0
  163. package/dist/planmagnet/team/invitationEmail.js +73 -0
  164. package/dist/planmagnet/team/invitationEmail.js.map +1 -0
  165. package/package.json +1 -1
@@ -0,0 +1,87 @@
1
+ import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
2
+ import { useTr as t } from "../../shared/hooks/useTr.js";
3
+ import { releaseLabel as n, releasesForProduct as r } from "./satisfaction.js";
4
+ import { CUSTOMER_SATISFACTION_RELEASE_OPTIONS as i } from "./satisfactionOperations.js";
5
+ import { useEffect as a } from "react";
6
+ import { useQuery as o } from "@apollo/client/react";
7
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
8
+ //#region src/planmagnet/satisfaction/SatisfactionReleaseSelect.tsx
9
+ var l = 500, u = "w-full rounded-md border border-input bg-background px-3 py-2 text-sm disabled:opacity-60";
10
+ function d() {
11
+ let { workspaceId: t } = e(), { data: n, loading: r, error: s, fetchMore: c } = o(i, {
12
+ variables: {
13
+ workspaceId: t ?? "",
14
+ pagination: { first: 100 }
15
+ },
16
+ skip: !t
17
+ }), u = n?.planmagnetReleases.items ?? [], d = n?.planmagnetReleases.pageInfo.hasNextPage ?? !1, f = n?.planmagnetReleases.pageInfo.endCursor ?? null;
18
+ return a(() => {
19
+ !d || !f || u.length >= l || c({
20
+ variables: { pagination: {
21
+ first: 100,
22
+ after: f
23
+ } },
24
+ updateQuery: (e, { fetchMoreResult: t }) => {
25
+ let n = new Set(e.planmagnetReleases.items.map((e) => e.id));
26
+ return { planmagnetReleases: {
27
+ ...t.planmagnetReleases,
28
+ items: [...e.planmagnetReleases.items, ...t.planmagnetReleases.items.filter((e) => !n.has(e.id))]
29
+ } };
30
+ }
31
+ }).catch(() => {});
32
+ }, [
33
+ d,
34
+ f,
35
+ u.length,
36
+ c
37
+ ]), {
38
+ releases: u,
39
+ loading: r && !n,
40
+ error: s,
41
+ truncated: d && u.length >= l
42
+ };
43
+ }
44
+ function f({ id: e, value: i, onChange: a, productId: o, noneLabel: f, disabled: p = !1, invalid: m = !1 }) {
45
+ let h = t(), { releases: g, loading: _, error: v, truncated: y } = d(), b = r(g, o), x = i && !b.some((e) => e.id === i) ? g.find((e) => e.id === i) : void 0;
46
+ return /* @__PURE__ */ c("select", {
47
+ id: e,
48
+ className: u,
49
+ value: i,
50
+ disabled: p,
51
+ "aria-invalid": m || void 0,
52
+ onChange: (e) => a(e.target.value),
53
+ children: [
54
+ /* @__PURE__ */ s("option", {
55
+ value: "",
56
+ children: f
57
+ }),
58
+ x && /* @__PURE__ */ s("option", {
59
+ value: x.id,
60
+ children: n(x)
61
+ }),
62
+ b.map((e) => /* @__PURE__ */ s("option", {
63
+ value: e.id,
64
+ children: n(e)
65
+ }, e.id)),
66
+ _ && /* @__PURE__ */ s("option", {
67
+ value: "",
68
+ disabled: !0,
69
+ children: h("planmagnet.satisfaction.releases.loading", "Loading releases…")
70
+ }),
71
+ v && !_ && g.length === 0 && /* @__PURE__ */ s("option", {
72
+ value: "",
73
+ disabled: !0,
74
+ children: h("planmagnet.satisfaction.releases.error", "Releases could not be loaded")
75
+ }),
76
+ y && /* @__PURE__ */ s("option", {
77
+ value: "",
78
+ disabled: !0,
79
+ children: h("planmagnet.satisfaction.releases.truncated", "Only the first {{max}} releases are listed", { max: l })
80
+ })
81
+ ]
82
+ });
83
+ }
84
+ //#endregion
85
+ export { f as SatisfactionReleaseSelect, d as useSatisfactionReleaseOptions };
86
+
87
+ //# sourceMappingURL=SatisfactionReleaseSelect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SatisfactionReleaseSelect.js","names":[],"sources":["../../../src/planmagnet/satisfaction/SatisfactionReleaseSelect.tsx"],"sourcesContent":["import { useEffect } from 'react';\nimport { useQuery } from '@apollo/client/react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { releaseLabel, releasesForProduct } from './satisfaction';\nimport {\n CUSTOMER_SATISFACTION_RELEASE_OPTIONS,\n type CustomerSatisfactionReleaseOptionsData,\n} from './satisfactionOperations';\n\n/** Releases are read page by page up to this many, so the picker never silently stops at the first 100. */\nconst MAX_RELEASE_OPTIONS = 500;\n\nconst selectClass =\n 'w-full rounded-md border border-input bg-background px-3 py-2 text-sm disabled:opacity-60';\n\n/** Every release in the workspace (up to the cap), with its product. */\nexport function useSatisfactionReleaseOptions() {\n const { workspaceId } = usePlanMagnet();\n const { data, loading, error, fetchMore } = useQuery<CustomerSatisfactionReleaseOptionsData>(\n CUSTOMER_SATISFACTION_RELEASE_OPTIONS,\n {\n variables: { workspaceId: workspaceId ?? '', pagination: { first: 100 } },\n skip: !workspaceId,\n }\n );\n const loaded = data?.planmagnetReleases.items ?? [];\n const hasNextPage = data?.planmagnetReleases.pageInfo.hasNextPage ?? false;\n const endCursor = data?.planmagnetReleases.pageInfo.endCursor ?? null;\n useEffect(() => {\n if (!hasNextPage || !endCursor || loaded.length >= MAX_RELEASE_OPTIONS) return;\n fetchMore({\n variables: { pagination: { first: 100, after: endCursor } },\n // Several pickers share this query, and each one's effect asks for the next page: merging by id keeps a page\n // fetched twice from listing its releases twice.\n updateQuery: (prev, { fetchMoreResult }) => {\n const seen = new Set(prev.planmagnetReleases.items.map((r) => r.id));\n return {\n planmagnetReleases: {\n ...fetchMoreResult.planmagnetReleases,\n items: [\n ...prev.planmagnetReleases.items,\n ...fetchMoreResult.planmagnetReleases.items.filter((r) => !seen.has(r.id)),\n ],\n },\n };\n },\n }).catch(() => {\n // A failed extra page leaves the releases already loaded usable.\n });\n }, [hasNextPage, endCursor, loaded.length, fetchMore]);\n return {\n releases: loaded,\n loading: loading && !data,\n error,\n truncated: hasNextPage && loaded.length >= MAX_RELEASE_OPTIONS,\n };\n}\n\n/** A release select, narrowed to what `productId` allows. '' means \"no release\" (`noneLabel`). */\nexport function SatisfactionReleaseSelect({\n id,\n value,\n onChange,\n productId,\n noneLabel,\n disabled = false,\n invalid = false,\n}: {\n id?: string;\n value: string;\n onChange: (releaseId: string) => void;\n productId: string;\n noneLabel: string;\n disabled?: boolean;\n invalid?: boolean;\n}) {\n const tr = useTr();\n const { releases, loading, error, truncated } = useSatisfactionReleaseOptions();\n const options = releasesForProduct(releases, productId);\n // A chosen release the narrowed list no longer holds (the product changed) stays visible rather than blank.\n const current =\n value && !options.some((r) => r.id === value)\n ? releases.find((r) => r.id === value)\n : undefined;\n return (\n <select\n id={id}\n className={selectClass}\n value={value}\n disabled={disabled}\n aria-invalid={invalid || undefined}\n onChange={(e) => onChange(e.target.value)}\n >\n <option value=\"\">{noneLabel}</option>\n {current && <option value={current.id}>{releaseLabel(current)}</option>}\n {options.map((r) => (\n <option key={r.id} value={r.id}>\n {releaseLabel(r)}\n </option>\n ))}\n {loading && (\n <option value=\"\" disabled>\n {tr('planmagnet.satisfaction.releases.loading', 'Loading releases…')}\n </option>\n )}\n {error && !loading && releases.length === 0 && (\n <option value=\"\" disabled>\n {tr('planmagnet.satisfaction.releases.error', 'Releases could not be loaded')}\n </option>\n )}\n {truncated && (\n <option value=\"\" disabled>\n {tr(\n 'planmagnet.satisfaction.releases.truncated',\n 'Only the first {{max}} releases are listed',\n { max: MAX_RELEASE_OPTIONS }\n )}\n </option>\n )}\n </select>\n );\n}\n"],"mappings":";;;;;;;;AAWA,IAAM,IAAsB,KAEtB,IACJ;AAGF,SAAgB,IAAgC;CAC9C,IAAM,EAAE,mBAAgB,GAAe,EACjC,EAAE,SAAM,YAAS,UAAO,iBAAc,EAC1C,GACA;EACE,WAAW;GAAE,aAAa,KAAe;GAAI,YAAY,EAAE,OAAO,KAAK;GAAE;EACzE,MAAM,CAAC;EACR,CACF,EACK,IAAS,GAAM,mBAAmB,SAAS,EAAE,EAC7C,IAAc,GAAM,mBAAmB,SAAS,eAAe,IAC/D,IAAY,GAAM,mBAAmB,SAAS,aAAa;AAuBjE,QAtBA,QAAgB;AACV,GAAC,KAAe,CAAC,KAAa,EAAO,UAAU,KACnD,EAAU;GACR,WAAW,EAAE,YAAY;IAAE,OAAO;IAAK,OAAO;IAAW,EAAE;GAG3D,cAAc,GAAM,EAAE,yBAAsB;IAC1C,IAAM,IAAO,IAAI,IAAI,EAAK,mBAAmB,MAAM,KAAK,MAAM,EAAE,GAAG,CAAC;AACpE,WAAO,EACL,oBAAoB;KAClB,GAAG,EAAgB;KACnB,OAAO,CACL,GAAG,EAAK,mBAAmB,OAC3B,GAAG,EAAgB,mBAAmB,MAAM,QAAQ,MAAM,CAAC,EAAK,IAAI,EAAE,GAAG,CAAC,CAC3E;KACF,EACF;;GAEJ,CAAC,CAAC,YAAY,GAEb;IACD;EAAC;EAAa;EAAW,EAAO;EAAQ;EAAU,CAAC,EAC/C;EACL,UAAU;EACV,SAAS,KAAW,CAAC;EACrB;EACA,WAAW,KAAe,EAAO,UAAU;EAC5C;;AAIH,SAAgB,EAA0B,EACxC,OACA,UACA,aACA,cACA,cACA,cAAW,IACX,aAAU,MAST;CACD,IAAM,IAAK,GAAO,EACZ,EAAE,aAAU,YAAS,UAAO,iBAAc,GAA+B,EACzE,IAAU,EAAmB,GAAU,EAAU,EAEjD,IACJ,KAAS,CAAC,EAAQ,MAAM,MAAM,EAAE,OAAO,EAAM,GACzC,EAAS,MAAM,MAAM,EAAE,OAAO,EAAM,GACpC,KAAA;AACN,QACE,kBAAC,UAAD;EACM;EACJ,WAAW;EACJ;EACG;EACV,gBAAc,KAAW,KAAA;EACzB,WAAW,MAAM,EAAS,EAAE,OAAO,MAAM;YAN3C;GAQE,kBAAC,UAAD;IAAQ,OAAM;cAAI;IAAmB,CAAA;GACpC,KAAW,kBAAC,UAAD;IAAQ,OAAO,EAAQ;cAAK,EAAa,EAAQ;IAAU,CAAA;GACtE,EAAQ,KAAK,MACZ,kBAAC,UAAD;IAAmB,OAAO,EAAE;cACzB,EAAa,EAAE;IACT,EAFI,EAAE,GAEN,CACT;GACD,KACC,kBAAC,UAAD;IAAQ,OAAM;IAAG,UAAA;cACd,EAAG,4CAA4C,oBAAoB;IAC7D,CAAA;GAEV,KAAS,CAAC,KAAW,EAAS,WAAW,KACxC,kBAAC,UAAD;IAAQ,OAAM;IAAG,UAAA;cACd,EAAG,0CAA0C,+BAA+B;IACtE,CAAA;GAEV,KACC,kBAAC,UAAD;IAAQ,OAAM;IAAG,UAAA;cACd,EACC,8CACA,8CACA,EAAE,KAAK,GAAqB,CAC7B;IACM,CAAA;GAEJ"}
@@ -0,0 +1,13 @@
1
+ import { FormLinks } from './satisfactionForm';
2
+ import { SatisfactionResponse } from './satisfactionOperations';
3
+
4
+ export declare function SatisfactionResponseDialog({ response, links, onSaved, onStale, }: {
5
+ /** Edit this response; omitted, the dialog adds a new one. */
6
+ response?: SatisfactionResponse;
7
+ /** A new response's starting links — the page's product / release / project filters. */
8
+ links?: FormLinks;
9
+ onSaved?: (kind: 'created' | 'updated') => void;
10
+ /** The response no longer exists (deleted meanwhile): re-read what is on the page. */
11
+ onStale?: () => void;
12
+ }): import("react/jsx-runtime").JSX.Element;
13
+ //# sourceMappingURL=SatisfactionResponseDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SatisfactionResponseDialog.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/satisfaction/SatisfactionResponseDialog.tsx"],"names":[],"mappings":"AAoCA,OAAO,EAQL,KAAK,SAAS,EAGf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAC;AAkGlC,wBAAgB,0BAA0B,CAAC,EACzC,QAAQ,EACR,KAAK,EACL,OAAO,EACP,OAAO,GACR,EAAE;IACD,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,wFAAwF;IACxF,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,KAAK,IAAI,CAAC;IAChD,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,2CA2YA"}
@@ -0,0 +1,326 @@
1
+ import { useTr as e } from "../../shared/hooks/useTr.js";
2
+ import { throwOnError as t } from "../utils/mutationResult.js";
3
+ import { ErrorLine as n } from "../controls/ConfirmButton.js";
4
+ import { cn as r } from "../utils/cn.js";
5
+ import { COMMENT_MAX as i, METRIC_TYPES as ee, RESPONDED_AT_EARLIEST as a, SCORE_SCALE as o, errorCode as s, errorField as c, formatUtcDay as l, localDay as u, scaleValues as d } from "./satisfaction.js";
6
+ import { CREATE_CUSTOMER_SATISFACTION_RESPONSE as f, SATISFACTION_QUERIES as p, UPDATE_CUSTOMER_SATISFACTION_RESPONSE as m } from "./satisfactionOperations.js";
7
+ import { refusalMessage as h } from "../product/refusal.js";
8
+ import { SatisfactionLinkFields as te } from "./SatisfactionLinkFields.js";
9
+ import { createInput as ne, formProblems as re, formValuesOf as ie, newFormValues as ae, updateInput as oe, withMetricType as se } from "./satisfactionForm.js";
10
+ import { useState as g } from "react";
11
+ import { AlertTriangle as _, Pencil as ce, Plus as v } from "lucide-react";
12
+ import { useMutation as y } from "@apollo/client/react";
13
+ import { jsx as b, jsxs as x } from "react/jsx-runtime";
14
+ import { Button as S, Checkbox as C, Input as w, ResponsiveDialog as T, ResponsiveDialogContent as le, ResponsiveDialogDescription as ue, ResponsiveDialogFooter as de, ResponsiveDialogHeader as E, ResponsiveDialogTitle as D, ResponsiveDialogTrigger as O, Textarea as k } from "@burdenoff/fe-libs/ui";
15
+ //#region src/planmagnet/satisfaction/SatisfactionResponseDialog.tsx
16
+ var A = {
17
+ refetchQueries: p,
18
+ awaitRefetchQueries: !0
19
+ }, j = {
20
+ score: "score",
21
+ metricType: "score",
22
+ respondedAt: "respondedAt",
23
+ productId: "links",
24
+ releaseId: "links",
25
+ projectId: "links",
26
+ respondentName: "respondentName",
27
+ respondentEmail: "respondentEmail",
28
+ isAnonymous: "respondentName",
29
+ comment: "comment",
30
+ externalId: "externalId"
31
+ };
32
+ function M(e, t, n) {
33
+ switch (t) {
34
+ case "scoreRequired": return e("planmagnet.satisfaction.form.errors.scoreRequired", "Pick the score the customer gave.");
35
+ case "scoreOutOfRange": return e("planmagnet.satisfaction.form.errors.scoreOutOfRange", "A {{metric}} score is a whole number from {{min}} to {{max}}.", {
36
+ metric: n.metricType,
37
+ ...o[n.metricType]
38
+ });
39
+ case "respondedAtRequired": return e("planmagnet.satisfaction.form.errors.respondedAtRequired", "Enter the day the customer answered.");
40
+ case "respondedAtFormat": return e("planmagnet.satisfaction.form.errors.respondedAtFormat", "Enter a valid date.");
41
+ case "respondedAtInFuture": return e("planmagnet.satisfaction.form.errors.respondedAtInFuture", "The response date cannot be in the future.");
42
+ case "respondedAtTooOld": return e("planmagnet.satisfaction.form.errors.respondedAtTooOld", "The response date cannot be earlier than {{min}}.", { min: a });
43
+ case "linkRequired": return e("planmagnet.satisfaction.form.errors.linkRequired", "Link the response to a product, a release or a project.");
44
+ case "respondentNameTooLong": return e("planmagnet.satisfaction.form.errors.respondentNameTooLong", "A respondent name can be at most {{max}} characters.", { max: 200 });
45
+ case "respondentEmailInvalid": return e("planmagnet.satisfaction.form.errors.respondentEmailInvalid", "Enter a valid email address of at most {{max}} characters.", { max: 254 });
46
+ case "commentTooLong": return e("planmagnet.satisfaction.form.errors.commentTooLong", "A comment can be at most {{max}} characters.", { max: i });
47
+ default: return e("planmagnet.satisfaction.form.errors.externalIdTooLong", "An external id can be at most {{max}} characters.", { max: 200 });
48
+ }
49
+ }
50
+ var N = (e) => r(e && "rounded-md ring-1 ring-destructive ring-offset-2 ring-offset-background");
51
+ function P({ response: o, links: p, onSaved: P, onStale: F }) {
52
+ let I = e(), L = !o, R = () => o ? ie(o) : ae(u(), p ?? {
53
+ productId: "",
54
+ releaseId: "",
55
+ projectId: ""
56
+ }), [z, B] = g(!1), [V, H] = g(R), [U, fe] = g(R), [pe, W] = g(!1), [G, K] = g(null), [me, q] = g(null), [he, ge] = y(f, A), [_e, ve] = y(m, A), J = ge.loading || ve.loading, Y = o ? `satisfaction-edit-${o.id}` : "satisfaction-new", X = re(V, /* @__PURE__ */ new Date(), L), Z = pe ? X : {}, Q = (e) => !!Z[e] || me === e, $ = (e) => {
57
+ H((t) => ({
58
+ ...t,
59
+ ...e
60
+ })), q(null);
61
+ }, ye = I("planmagnet.satisfaction.errors.forbidden", "Your role cannot change customer satisfaction responses in this workspace."), be = async () => {
62
+ if (W(!0), K(null), q(null), !(Object.keys(X).length > 0 || V.score === null)) try {
63
+ if (o) {
64
+ let e = oe(U, V);
65
+ Object.keys(e).length > 0 && (await t(_e({ variables: {
66
+ id: o.id,
67
+ input: e
68
+ } })), P?.("updated"));
69
+ } else await t(he({ variables: { input: ne({
70
+ ...V,
71
+ score: V.score
72
+ }) } })), P?.("created");
73
+ B(!1);
74
+ } catch (e) {
75
+ let t = c(e);
76
+ q(t ? j[t] ?? null : null), K(h(e, L ? I("planmagnet.satisfaction.form.errors.create", "The response could not be added.") : I("planmagnet.satisfaction.form.errors.save", "The response could not be saved."), ye)), s(e) === "NOT_FOUND" && !L && F?.();
77
+ }
78
+ }, xe = u(), Se = V.metricType === "CSAT" ? I("planmagnet.satisfaction.form.csatHint", "“How satisfied were you?” — 1 very dissatisfied … 5 very satisfied. 4 and 5 count as satisfied.") : I("planmagnet.satisfaction.form.npsHint", "“How likely are you to recommend us?” — 0 not at all … 10 extremely. 9–10 promote, 7–8 are passive, 0–6 detract.");
79
+ return /* @__PURE__ */ x(T, {
80
+ open: z,
81
+ onOpenChange: (e) => {
82
+ if (B(e), e) {
83
+ let e = R();
84
+ H(e), fe(e), W(!1);
85
+ }
86
+ K(null), q(null);
87
+ },
88
+ children: [/* @__PURE__ */ b(O, {
89
+ asChild: !0,
90
+ children: o ? /* @__PURE__ */ b(S, {
91
+ size: "sm",
92
+ variant: "outline",
93
+ "data-testid": "satisfaction-response-edit",
94
+ "aria-label": I("planmagnet.satisfaction.actions.editNamed", "Edit the response of {{date}}", { date: l(o.respondedAt) }),
95
+ children: /* @__PURE__ */ b(ce, { className: "h-4 w-4" })
96
+ }) : /* @__PURE__ */ x(S, {
97
+ size: "sm",
98
+ "data-testid": "satisfaction-response-new",
99
+ children: [/* @__PURE__ */ b(v, { className: "mr-1 h-4 w-4" }), I("planmagnet.satisfaction.actions.add", "Add response")]
100
+ })
101
+ }), /* @__PURE__ */ x(le, {
102
+ className: "max-h-[90vh] overflow-y-auto sm:max-w-xl",
103
+ children: [
104
+ /* @__PURE__ */ x(E, { children: [/* @__PURE__ */ b(D, { children: L ? I("planmagnet.satisfaction.form.newTitle", "Add a customer response") : I("planmagnet.satisfaction.form.editTitle", "Edit customer response") }), /* @__PURE__ */ b(ue, { children: I("planmagnet.satisfaction.form.description", "One customer’s answer, as they gave it. It counts in every CSAT or NPS figure for its product, release and project.") })] }),
105
+ /* @__PURE__ */ x("div", {
106
+ className: "min-w-0 space-y-4",
107
+ children: [
108
+ /* @__PURE__ */ x("fieldset", { children: [/* @__PURE__ */ x("legend", {
109
+ className: "text-sm font-medium",
110
+ children: [I("planmagnet.satisfaction.form.metric", "Metric"), " *"]
111
+ }), /* @__PURE__ */ b("div", {
112
+ className: "mt-1 flex gap-2",
113
+ role: "group",
114
+ children: ee.map((e) => /* @__PURE__ */ b(S, {
115
+ type: "button",
116
+ size: "sm",
117
+ variant: V.metricType === e ? "default" : "outline",
118
+ "aria-pressed": V.metricType === e,
119
+ onClick: () => {
120
+ H((t) => se(t, e)), q(null);
121
+ },
122
+ "data-testid": `satisfaction-form-metric-${e}`,
123
+ children: e === "CSAT" ? I("planmagnet.satisfaction.form.metricCsat", "CSAT (1–5)") : I("planmagnet.satisfaction.form.metricNps", "NPS (0–10)")
124
+ }, e))
125
+ })] }),
126
+ /* @__PURE__ */ x("fieldset", {
127
+ className: N(Q("score")),
128
+ children: [
129
+ /* @__PURE__ */ x("legend", {
130
+ className: "text-sm font-medium",
131
+ children: [I("planmagnet.satisfaction.form.score", "Score"), " *"]
132
+ }),
133
+ /* @__PURE__ */ b("p", {
134
+ className: "text-xs text-muted-foreground",
135
+ children: Se
136
+ }),
137
+ /* @__PURE__ */ b("div", {
138
+ className: "mt-1 flex flex-wrap gap-1",
139
+ role: "group",
140
+ children: d(V.metricType).map((e) => /* @__PURE__ */ b(S, {
141
+ type: "button",
142
+ size: "sm",
143
+ variant: V.score === e ? "default" : "outline",
144
+ "aria-pressed": V.score === e,
145
+ className: "w-9 px-0 tabular-nums",
146
+ onClick: () => $({ score: e }),
147
+ "data-testid": `satisfaction-form-score-${e}`,
148
+ children: e
149
+ }, e))
150
+ }),
151
+ Z.score && /* @__PURE__ */ b(n, { message: M(I, Z.score, V) })
152
+ ]
153
+ }),
154
+ /* @__PURE__ */ x("div", { children: [
155
+ /* @__PURE__ */ x("label", {
156
+ htmlFor: `${Y}-responded`,
157
+ className: "text-sm font-medium",
158
+ children: [I("planmagnet.satisfaction.form.respondedAt", "Answered on"), " *"]
159
+ }),
160
+ /* @__PURE__ */ b(w, {
161
+ id: `${Y}-responded`,
162
+ type: "date",
163
+ className: "mt-1 w-48",
164
+ value: V.respondedAt,
165
+ min: a,
166
+ max: xe,
167
+ "aria-invalid": Q("respondedAt") || void 0,
168
+ onChange: (e) => $({ respondedAt: e.target.value }),
169
+ "data-testid": "satisfaction-form-responded-at"
170
+ }),
171
+ Z.respondedAt && /* @__PURE__ */ b(n, { message: M(I, Z.respondedAt, V) })
172
+ ] }),
173
+ /* @__PURE__ */ x("fieldset", {
174
+ className: r("space-y-2", N(Q("links"))),
175
+ children: [
176
+ /* @__PURE__ */ x("legend", {
177
+ className: "text-sm font-medium",
178
+ children: [I("planmagnet.satisfaction.form.about", "About"), " *"]
179
+ }),
180
+ /* @__PURE__ */ b("p", {
181
+ className: "text-xs text-muted-foreground",
182
+ children: I("planmagnet.satisfaction.form.aboutHint", "At least one. A release must belong to the product chosen with it, or to no product.")
183
+ }),
184
+ /* @__PURE__ */ b(te, {
185
+ idPrefix: Y,
186
+ value: {
187
+ productId: V.productId,
188
+ releaseId: V.releaseId,
189
+ projectId: V.projectId
190
+ },
191
+ onChange: (e) => $(e),
192
+ notes: {
193
+ productId: o?.productId && !o.product && V.productId === o.productId ? I("planmagnet.satisfaction.form.productDeleted", "The linked product was deleted.") : null,
194
+ releaseId: o?.releaseId && !o.release && V.releaseId === o.releaseId ? I("planmagnet.satisfaction.form.releaseDeleted", "The linked release was deleted.") : null,
195
+ projectId: o?.projectId && !o.project && V.projectId === o.projectId ? I("planmagnet.satisfaction.form.projectDeleted", "The linked project was deleted.") : null
196
+ }
197
+ }),
198
+ Z.links && /* @__PURE__ */ b(n, { message: M(I, Z.links, V) })
199
+ ]
200
+ }),
201
+ /* @__PURE__ */ x("fieldset", {
202
+ className: "space-y-2",
203
+ children: [
204
+ /* @__PURE__ */ b("legend", {
205
+ className: "text-sm font-medium",
206
+ children: I("planmagnet.satisfaction.form.respondent", "Respondent")
207
+ }),
208
+ /* @__PURE__ */ x("div", {
209
+ className: "flex items-start gap-2",
210
+ children: [/* @__PURE__ */ b(C, {
211
+ id: `${Y}-anonymous`,
212
+ checked: V.isAnonymous,
213
+ onCheckedChange: (e) => $({ isAnonymous: e === !0 }),
214
+ "data-testid": "satisfaction-form-anonymous"
215
+ }), /* @__PURE__ */ b("label", {
216
+ htmlFor: `${Y}-anonymous`,
217
+ className: "text-sm",
218
+ children: I("planmagnet.satisfaction.form.anonymous", "Anonymous — no name or email is stored for this response.")
219
+ })]
220
+ }),
221
+ V.isAnonymous && !U.isAnonymous && (U.respondentName || U.respondentEmail) && /* @__PURE__ */ x("p", {
222
+ className: "flex items-center gap-1.5 rounded-md bg-status-warning-bg-subtle px-2 py-1 text-xs text-foreground",
223
+ children: [/* @__PURE__ */ b(_, {
224
+ className: "h-3.5 w-3.5 shrink-0",
225
+ "aria-hidden": !0
226
+ }), I("planmagnet.satisfaction.form.anonymousClears", "Saving removes the name and email stored with this response.")]
227
+ }),
228
+ !V.isAnonymous && /* @__PURE__ */ x("div", {
229
+ className: "grid gap-2 sm:grid-cols-2",
230
+ children: [/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("label", {
231
+ htmlFor: `${Y}-name`,
232
+ className: "text-xs text-muted-foreground",
233
+ children: I("planmagnet.satisfaction.form.name", "Name")
234
+ }), /* @__PURE__ */ b(w, {
235
+ id: `${Y}-name`,
236
+ value: V.respondentName,
237
+ maxLength: 200,
238
+ "aria-invalid": Q("respondentName") || void 0,
239
+ onChange: (e) => $({ respondentName: e.target.value }),
240
+ "data-testid": "satisfaction-form-name"
241
+ })] }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("label", {
242
+ htmlFor: `${Y}-email`,
243
+ className: "text-xs text-muted-foreground",
244
+ children: I("planmagnet.satisfaction.form.email", "Email")
245
+ }), /* @__PURE__ */ b(w, {
246
+ id: `${Y}-email`,
247
+ type: "email",
248
+ value: V.respondentEmail,
249
+ maxLength: 254,
250
+ "aria-invalid": Q("respondentEmail") || void 0,
251
+ onChange: (e) => $({ respondentEmail: e.target.value }),
252
+ "data-testid": "satisfaction-form-email"
253
+ })] })]
254
+ }),
255
+ Z.respondentName && /* @__PURE__ */ b(n, { message: M(I, Z.respondentName, V) }),
256
+ Z.respondentEmail && /* @__PURE__ */ b(n, { message: M(I, Z.respondentEmail, V) })
257
+ ]
258
+ }),
259
+ /* @__PURE__ */ x("div", { children: [
260
+ /* @__PURE__ */ b("label", {
261
+ htmlFor: `${Y}-comment`,
262
+ className: "text-sm font-medium",
263
+ children: I("planmagnet.satisfaction.form.comment", "Comment")
264
+ }),
265
+ /* @__PURE__ */ b(k, {
266
+ id: `${Y}-comment`,
267
+ className: "mt-1 min-h-[80px]",
268
+ value: V.comment,
269
+ maxLength: i,
270
+ "aria-invalid": Q("comment") || void 0,
271
+ onChange: (e) => $({ comment: e.target.value }),
272
+ "data-testid": "satisfaction-form-comment"
273
+ }),
274
+ /* @__PURE__ */ x("p", {
275
+ className: "text-right text-xs text-muted-foreground tabular-nums",
276
+ children: [
277
+ V.comment.length,
278
+ " / ",
279
+ i
280
+ ]
281
+ }),
282
+ Z.comment && /* @__PURE__ */ b(n, { message: M(I, Z.comment, V) })
283
+ ] }),
284
+ L ? /* @__PURE__ */ x("div", { children: [
285
+ /* @__PURE__ */ b("label", {
286
+ htmlFor: `${Y}-external`,
287
+ className: "text-sm font-medium",
288
+ children: I("planmagnet.satisfaction.form.externalId", "External id")
289
+ }),
290
+ /* @__PURE__ */ b(w, {
291
+ id: `${Y}-external`,
292
+ className: "mt-1",
293
+ value: V.externalId,
294
+ maxLength: 200,
295
+ "aria-invalid": Q("externalId") || void 0,
296
+ placeholder: I("planmagnet.satisfaction.form.externalIdPlaceholder", "Optional"),
297
+ onChange: (e) => $({ externalId: e.target.value }),
298
+ "data-testid": "satisfaction-form-external-id"
299
+ }),
300
+ /* @__PURE__ */ b("p", {
301
+ className: "mt-1 text-xs text-muted-foreground",
302
+ children: I("planmagnet.satisfaction.form.externalIdHint", "The answer’s id in the survey tool it came from, if any. It cannot be changed later.")
303
+ }),
304
+ Z.externalId && /* @__PURE__ */ b(n, { message: M(I, Z.externalId, V) })
305
+ ] }) : /* @__PURE__ */ x("p", {
306
+ className: "text-xs text-muted-foreground",
307
+ "data-testid": "satisfaction-form-source",
308
+ children: [o?.source === "IMPORT" ? I("planmagnet.satisfaction.form.sourceImport", "Imported from a file") : I("planmagnet.satisfaction.form.sourceManual", "Entered by a team member"), o?.externalId ? ` · ${I("planmagnet.satisfaction.form.externalIdShown", "external id {{id}}", { id: o.externalId })}` : ""]
309
+ })
310
+ ]
311
+ }),
312
+ G && /* @__PURE__ */ b(n, { message: G }),
313
+ /* @__PURE__ */ b(de, { children: /* @__PURE__ */ b(S, {
314
+ onClick: () => void be(),
315
+ disabled: J,
316
+ "data-testid": "satisfaction-form-save",
317
+ children: J ? I("planmagnet.satisfaction.actions.saving", "Saving…") : L ? I("planmagnet.satisfaction.actions.addConfirm", "Add response") : I("planmagnet.satisfaction.actions.save", "Save")
318
+ }) })
319
+ ]
320
+ })]
321
+ });
322
+ }
323
+ //#endregion
324
+ export { P as SatisfactionResponseDialog };
325
+
326
+ //# sourceMappingURL=SatisfactionResponseDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SatisfactionResponseDialog.js","names":[],"sources":["../../../src/planmagnet/satisfaction/SatisfactionResponseDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useMutation } from '@apollo/client/react';\nimport {\n Button,\n Checkbox,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { AlertTriangle, Pencil, Plus } from 'lucide-react';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { ErrorLine } from '../controls/ConfirmButton';\nimport { refusalMessage } from '../product/refusal';\nimport { throwOnError } from '../utils/mutationResult';\nimport { cn } from '../utils/cn';\nimport { SatisfactionLinkFields } from './SatisfactionLinkFields';\nimport {\n COMMENT_MAX,\n EXTERNAL_ID_MAX,\n METRIC_TYPES,\n RESPONDED_AT_EARLIEST,\n RESPONDENT_EMAIL_MAX,\n RESPONDENT_NAME_MAX,\n SCORE_SCALE,\n errorCode,\n errorField,\n formatUtcDay,\n localDay,\n scaleValues,\n} from './satisfaction';\nimport {\n createInput,\n formProblems,\n formValuesOf,\n newFormValues,\n updateInput,\n withMetricType,\n type FormField,\n type FormLinks,\n type FormProblem,\n type ResponseFormValues,\n} from './satisfactionForm';\nimport {\n CREATE_CUSTOMER_SATISFACTION_RESPONSE,\n SATISFACTION_QUERIES,\n UPDATE_CUSTOMER_SATISFACTION_RESPONSE,\n type SatisfactionResponse,\n} from './satisfactionOperations';\n\n/**\n * Add one customer's answer (source MANUAL), or edit one. Entered by a team member on the customer's behalf: the score\n * on its metric's own scale, the day they answered, what it is about (a product, a release and/or a project — at least\n * one), and who answered — or nobody, for an anonymous response, which stores no name or email at all.\n *\n * An edit sends only what changed. Every refusal is the service's own sentence, shown in the dialog; a field the service\n * names is marked. A response deleted meanwhile (NOT_FOUND) makes the page re-read its list.\n */\n\nconst writeOptions = { refetchQueries: SATISFACTION_QUERIES, awaitRefetchQueries: true };\n\n/** Service `extensions.field` → the form field it marks. */\nconst FIELD_OF: Record<string, FormField> = {\n score: 'score',\n metricType: 'score',\n respondedAt: 'respondedAt',\n productId: 'links',\n releaseId: 'links',\n projectId: 'links',\n respondentName: 'respondentName',\n respondentEmail: 'respondentEmail',\n isAnonymous: 'respondentName',\n comment: 'comment',\n externalId: 'externalId',\n};\n\nfunction problemText(\n tr: ReturnType<typeof useTr>,\n problem: FormProblem,\n values: ResponseFormValues\n) {\n switch (problem) {\n case 'scoreRequired':\n return tr(\n 'planmagnet.satisfaction.form.errors.scoreRequired',\n 'Pick the score the customer gave.'\n );\n case 'scoreOutOfRange':\n return tr(\n 'planmagnet.satisfaction.form.errors.scoreOutOfRange',\n 'A {{metric}} score is a whole number from {{min}} to {{max}}.',\n { metric: values.metricType, ...SCORE_SCALE[values.metricType] }\n );\n case 'respondedAtRequired':\n return tr(\n 'planmagnet.satisfaction.form.errors.respondedAtRequired',\n 'Enter the day the customer answered.'\n );\n case 'respondedAtFormat':\n return tr('planmagnet.satisfaction.form.errors.respondedAtFormat', 'Enter a valid date.');\n case 'respondedAtInFuture':\n return tr(\n 'planmagnet.satisfaction.form.errors.respondedAtInFuture',\n 'The response date cannot be in the future.'\n );\n case 'respondedAtTooOld':\n return tr(\n 'planmagnet.satisfaction.form.errors.respondedAtTooOld',\n 'The response date cannot be earlier than {{min}}.',\n { min: RESPONDED_AT_EARLIEST }\n );\n case 'linkRequired':\n return tr(\n 'planmagnet.satisfaction.form.errors.linkRequired',\n 'Link the response to a product, a release or a project.'\n );\n case 'respondentNameTooLong':\n return tr(\n 'planmagnet.satisfaction.form.errors.respondentNameTooLong',\n 'A respondent name can be at most {{max}} characters.',\n { max: RESPONDENT_NAME_MAX }\n );\n case 'respondentEmailInvalid':\n return tr(\n 'planmagnet.satisfaction.form.errors.respondentEmailInvalid',\n 'Enter a valid email address of at most {{max}} characters.',\n { max: RESPONDENT_EMAIL_MAX }\n );\n case 'commentTooLong':\n return tr(\n 'planmagnet.satisfaction.form.errors.commentTooLong',\n 'A comment can be at most {{max}} characters.',\n { max: COMMENT_MAX }\n );\n default:\n return tr(\n 'planmagnet.satisfaction.form.errors.externalIdTooLong',\n 'An external id can be at most {{max}} characters.',\n { max: EXTERNAL_ID_MAX }\n );\n }\n}\n\nconst fieldClass = (invalid: boolean) =>\n cn(invalid && 'rounded-md ring-1 ring-destructive ring-offset-2 ring-offset-background');\n\nexport function SatisfactionResponseDialog({\n response,\n links,\n onSaved,\n onStale,\n}: {\n /** Edit this response; omitted, the dialog adds a new one. */\n response?: SatisfactionResponse;\n /** A new response's starting links — the page's product / release / project filters. */\n links?: FormLinks;\n onSaved?: (kind: 'created' | 'updated') => void;\n /** The response no longer exists (deleted meanwhile): re-read what is on the page. */\n onStale?: () => void;\n}) {\n const tr = useTr();\n const isNew = !response;\n const initialValues = (): ResponseFormValues =>\n response\n ? formValuesOf(response)\n : newFormValues(localDay(), links ?? { productId: '', releaseId: '', projectId: '' });\n const [open, setOpen] = useState(false);\n const [values, setValues] = useState<ResponseFormValues>(initialValues);\n const [initial, setInitial] = useState<ResponseFormValues>(initialValues);\n const [attempted, setAttempted] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [serverField, setServerField] = useState<FormField | null>(null);\n const [create, createState] = useMutation(CREATE_CUSTOMER_SATISFACTION_RESPONSE, writeOptions);\n const [update, updateState] = useMutation(UPDATE_CUSTOMER_SATISFACTION_RESPONSE, writeOptions);\n const saving = createState.loading || updateState.loading;\n const idPrefix = response ? `satisfaction-edit-${response.id}` : 'satisfaction-new';\n\n const problems = formProblems(values, new Date(), isNew);\n const shown = attempted ? problems : {};\n const invalid = (field: FormField) => !!shown[field] || serverField === field;\n const set = (patch: Partial<ResponseFormValues>) => {\n setValues((v) => ({ ...v, ...patch }));\n setServerField(null);\n };\n const forbidden = tr(\n 'planmagnet.satisfaction.errors.forbidden',\n 'Your role cannot change customer satisfaction responses in this workspace.'\n );\n\n const save = async () => {\n setAttempted(true);\n setError(null);\n setServerField(null);\n if (Object.keys(problems).length > 0 || values.score === null) return;\n try {\n if (response) {\n const input = updateInput(initial, values);\n if (Object.keys(input).length > 0) {\n await throwOnError(update({ variables: { id: response.id, input } }));\n onSaved?.('updated');\n }\n } else {\n await throwOnError(\n create({ variables: { input: createInput({ ...values, score: values.score }) } })\n );\n onSaved?.('created');\n }\n setOpen(false);\n } catch (e) {\n const field = errorField(e);\n setServerField(field ? (FIELD_OF[field] ?? null) : null);\n setError(\n refusalMessage(\n e,\n isNew\n ? tr('planmagnet.satisfaction.form.errors.create', 'The response could not be added.')\n : tr('planmagnet.satisfaction.form.errors.save', 'The response could not be saved.'),\n forbidden\n )\n );\n if (errorCode(e) === 'NOT_FOUND' && !isNew) onStale?.();\n }\n };\n\n const today = localDay();\n const metricHint =\n values.metricType === 'CSAT'\n ? tr(\n 'planmagnet.satisfaction.form.csatHint',\n '“How satisfied were you?” — 1 very dissatisfied … 5 very satisfied. 4 and 5 count as satisfied.'\n )\n : tr(\n 'planmagnet.satisfaction.form.npsHint',\n '“How likely are you to recommend us?” — 0 not at all … 10 extremely. 9–10 promote, 7–8 are passive, 0–6 detract.'\n );\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) {\n const start = initialValues();\n setValues(start);\n setInitial(start);\n setAttempted(false);\n }\n setError(null);\n setServerField(null);\n }}\n >\n <ResponsiveDialogTrigger asChild>\n {response ? (\n <Button\n size=\"sm\"\n variant=\"outline\"\n data-testid=\"satisfaction-response-edit\"\n aria-label={tr(\n 'planmagnet.satisfaction.actions.editNamed',\n 'Edit the response of {{date}}',\n {\n date: formatUtcDay(response.respondedAt),\n }\n )}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n ) : (\n <Button size=\"sm\" data-testid=\"satisfaction-response-new\">\n <Plus className=\"mr-1 h-4 w-4\" />\n {tr('planmagnet.satisfaction.actions.add', 'Add response')}\n </Button>\n )}\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-xl\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {isNew\n ? tr('planmagnet.satisfaction.form.newTitle', 'Add a customer response')\n : tr('planmagnet.satisfaction.form.editTitle', 'Edit customer response')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'planmagnet.satisfaction.form.description',\n 'One customer’s answer, as they gave it. It counts in every CSAT or NPS figure for its product, release and project.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n <div className=\"min-w-0 space-y-4\">\n <fieldset>\n <legend className=\"text-sm font-medium\">\n {tr('planmagnet.satisfaction.form.metric', 'Metric')} *\n </legend>\n <div className=\"mt-1 flex gap-2\" role=\"group\">\n {METRIC_TYPES.map((metric) => (\n <Button\n key={metric}\n type=\"button\"\n size=\"sm\"\n variant={values.metricType === metric ? 'default' : 'outline'}\n aria-pressed={values.metricType === metric}\n onClick={() => {\n setValues((v) => withMetricType(v, metric));\n setServerField(null);\n }}\n data-testid={`satisfaction-form-metric-${metric}`}\n >\n {metric === 'CSAT'\n ? tr('planmagnet.satisfaction.form.metricCsat', 'CSAT (1–5)')\n : tr('planmagnet.satisfaction.form.metricNps', 'NPS (0–10)')}\n </Button>\n ))}\n </div>\n </fieldset>\n\n <fieldset className={fieldClass(invalid('score'))}>\n <legend className=\"text-sm font-medium\">\n {tr('planmagnet.satisfaction.form.score', 'Score')} *\n </legend>\n <p className=\"text-xs text-muted-foreground\">{metricHint}</p>\n <div className=\"mt-1 flex flex-wrap gap-1\" role=\"group\">\n {scaleValues(values.metricType).map((score) => (\n <Button\n key={score}\n type=\"button\"\n size=\"sm\"\n variant={values.score === score ? 'default' : 'outline'}\n aria-pressed={values.score === score}\n className=\"w-9 px-0 tabular-nums\"\n onClick={() => set({ score })}\n data-testid={`satisfaction-form-score-${score}`}\n >\n {score}\n </Button>\n ))}\n </div>\n {shown.score && <ErrorLine message={problemText(tr, shown.score, values)} />}\n </fieldset>\n\n <div>\n <label htmlFor={`${idPrefix}-responded`} className=\"text-sm font-medium\">\n {tr('planmagnet.satisfaction.form.respondedAt', 'Answered on')} *\n </label>\n <Input\n id={`${idPrefix}-responded`}\n type=\"date\"\n className=\"mt-1 w-48\"\n value={values.respondedAt}\n min={RESPONDED_AT_EARLIEST}\n max={today}\n aria-invalid={invalid('respondedAt') || undefined}\n onChange={(e) => set({ respondedAt: e.target.value })}\n data-testid=\"satisfaction-form-responded-at\"\n />\n {shown.respondedAt && (\n <ErrorLine message={problemText(tr, shown.respondedAt, values)} />\n )}\n </div>\n\n <fieldset className={cn('space-y-2', fieldClass(invalid('links')))}>\n <legend className=\"text-sm font-medium\">\n {tr('planmagnet.satisfaction.form.about', 'About')} *\n </legend>\n <p className=\"text-xs text-muted-foreground\">\n {tr(\n 'planmagnet.satisfaction.form.aboutHint',\n 'At least one. A release must belong to the product chosen with it, or to no product.'\n )}\n </p>\n <SatisfactionLinkFields\n idPrefix={idPrefix}\n value={{\n productId: values.productId,\n releaseId: values.releaseId,\n projectId: values.projectId,\n }}\n onChange={(next) => set(next)}\n notes={{\n productId:\n response?.productId &&\n !response.product &&\n values.productId === response.productId\n ? tr(\n 'planmagnet.satisfaction.form.productDeleted',\n 'The linked product was deleted.'\n )\n : null,\n releaseId:\n response?.releaseId &&\n !response.release &&\n values.releaseId === response.releaseId\n ? tr(\n 'planmagnet.satisfaction.form.releaseDeleted',\n 'The linked release was deleted.'\n )\n : null,\n projectId:\n response?.projectId &&\n !response.project &&\n values.projectId === response.projectId\n ? tr(\n 'planmagnet.satisfaction.form.projectDeleted',\n 'The linked project was deleted.'\n )\n : null,\n }}\n />\n {shown.links && <ErrorLine message={problemText(tr, shown.links, values)} />}\n </fieldset>\n\n <fieldset className=\"space-y-2\">\n <legend className=\"text-sm font-medium\">\n {tr('planmagnet.satisfaction.form.respondent', 'Respondent')}\n </legend>\n <div className=\"flex items-start gap-2\">\n <Checkbox\n id={`${idPrefix}-anonymous`}\n checked={values.isAnonymous}\n onCheckedChange={(checked) => set({ isAnonymous: checked === true })}\n data-testid=\"satisfaction-form-anonymous\"\n />\n <label htmlFor={`${idPrefix}-anonymous`} className=\"text-sm\">\n {tr(\n 'planmagnet.satisfaction.form.anonymous',\n 'Anonymous — no name or email is stored for this response.'\n )}\n </label>\n </div>\n {values.isAnonymous &&\n !initial.isAnonymous &&\n (initial.respondentName || initial.respondentEmail) && (\n <p className=\"flex items-center gap-1.5 rounded-md bg-status-warning-bg-subtle px-2 py-1 text-xs text-foreground\">\n <AlertTriangle className=\"h-3.5 w-3.5 shrink-0\" aria-hidden={true} />\n {tr(\n 'planmagnet.satisfaction.form.anonymousClears',\n 'Saving removes the name and email stored with this response.'\n )}\n </p>\n )}\n {!values.isAnonymous && (\n <div className=\"grid gap-2 sm:grid-cols-2\">\n <div>\n <label htmlFor={`${idPrefix}-name`} className=\"text-xs text-muted-foreground\">\n {tr('planmagnet.satisfaction.form.name', 'Name')}\n </label>\n <Input\n id={`${idPrefix}-name`}\n value={values.respondentName}\n maxLength={RESPONDENT_NAME_MAX}\n aria-invalid={invalid('respondentName') || undefined}\n onChange={(e) => set({ respondentName: e.target.value })}\n data-testid=\"satisfaction-form-name\"\n />\n </div>\n <div>\n <label htmlFor={`${idPrefix}-email`} className=\"text-xs text-muted-foreground\">\n {tr('planmagnet.satisfaction.form.email', 'Email')}\n </label>\n <Input\n id={`${idPrefix}-email`}\n type=\"email\"\n value={values.respondentEmail}\n maxLength={RESPONDENT_EMAIL_MAX}\n aria-invalid={invalid('respondentEmail') || undefined}\n onChange={(e) => set({ respondentEmail: e.target.value })}\n data-testid=\"satisfaction-form-email\"\n />\n </div>\n </div>\n )}\n {shown.respondentName && (\n <ErrorLine message={problemText(tr, shown.respondentName, values)} />\n )}\n {shown.respondentEmail && (\n <ErrorLine message={problemText(tr, shown.respondentEmail, values)} />\n )}\n </fieldset>\n\n <div>\n <label htmlFor={`${idPrefix}-comment`} className=\"text-sm font-medium\">\n {tr('planmagnet.satisfaction.form.comment', 'Comment')}\n </label>\n <Textarea\n id={`${idPrefix}-comment`}\n className=\"mt-1 min-h-[80px]\"\n value={values.comment}\n maxLength={COMMENT_MAX}\n aria-invalid={invalid('comment') || undefined}\n onChange={(e) => set({ comment: e.target.value })}\n data-testid=\"satisfaction-form-comment\"\n />\n <p className=\"text-right text-xs text-muted-foreground tabular-nums\">\n {values.comment.length} / {COMMENT_MAX}\n </p>\n {shown.comment && <ErrorLine message={problemText(tr, shown.comment, values)} />}\n </div>\n\n {isNew ? (\n <div>\n <label htmlFor={`${idPrefix}-external`} className=\"text-sm font-medium\">\n {tr('planmagnet.satisfaction.form.externalId', 'External id')}\n </label>\n <Input\n id={`${idPrefix}-external`}\n className=\"mt-1\"\n value={values.externalId}\n maxLength={EXTERNAL_ID_MAX}\n aria-invalid={invalid('externalId') || undefined}\n placeholder={tr('planmagnet.satisfaction.form.externalIdPlaceholder', 'Optional')}\n onChange={(e) => set({ externalId: e.target.value })}\n data-testid=\"satisfaction-form-external-id\"\n />\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {tr(\n 'planmagnet.satisfaction.form.externalIdHint',\n 'The answer’s id in the survey tool it came from, if any. It cannot be changed later.'\n )}\n </p>\n {shown.externalId && (\n <ErrorLine message={problemText(tr, shown.externalId, values)} />\n )}\n </div>\n ) : (\n <p className=\"text-xs text-muted-foreground\" data-testid=\"satisfaction-form-source\">\n {response?.source === 'IMPORT'\n ? tr('planmagnet.satisfaction.form.sourceImport', 'Imported from a file')\n : tr('planmagnet.satisfaction.form.sourceManual', 'Entered by a team member')}\n {response?.externalId\n ? ` · ${tr('planmagnet.satisfaction.form.externalIdShown', 'external id {{id}}', {\n id: response.externalId,\n })}`\n : ''}\n </p>\n )}\n </div>\n\n {error && <ErrorLine message={error} />}\n <ResponsiveDialogFooter>\n <Button\n onClick={() => void save()}\n disabled={saving}\n data-testid=\"satisfaction-form-save\"\n >\n {saving\n ? tr('planmagnet.satisfaction.actions.saving', 'Saving…')\n : isNew\n ? tr('planmagnet.satisfaction.actions.addConfirm', 'Add response')\n : tr('planmagnet.satisfaction.actions.save', 'Save')}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { SatisfactionMetricType, SatisfactionWindowInput } from './satisfaction';
2
+ import { FormLinks } from './satisfactionForm';
3
+ import { SatisfactionAccess } from './useSatisfactionAccess';
4
+
5
+ export interface ResponsesFilter {
6
+ productId?: string;
7
+ releaseId?: string;
8
+ projectId?: string;
9
+ metricType?: SatisfactionMetricType;
10
+ window: SatisfactionWindowInput;
11
+ }
12
+ export declare function SatisfactionResponsesTable({ workspaceId, filter, access, links, filtered, onNotice, onStale, }: {
13
+ workspaceId: string;
14
+ filter: ResponsesFilter;
15
+ access: SatisfactionAccess;
16
+ /** A new response's starting links (the page's filters). */
17
+ links: FormLinks;
18
+ /** True when any product / release / project / metric filter is set — for the empty-state wording. */
19
+ filtered: boolean;
20
+ onNotice: (message: string, tone: 'status' | 'alert') => void;
21
+ onStale: () => void;
22
+ }): import("react/jsx-runtime").JSX.Element;
23
+ //# sourceMappingURL=SatisfactionResponsesTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SatisfactionResponsesTable.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/satisfaction/SatisfactionResponsesTable.tsx"],"names":[],"mappings":"AAsBA,OAAO,EAML,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAQpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAWlE,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,sBAAsB,CAAC;IACpC,MAAM,EAAE,uBAAuB,CAAC;CACjC;AA4KD,wBAAgB,0BAA0B,CAAC,EACzC,WAAW,EACX,MAAM,EACN,MAAM,EACN,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,OAAO,GACR,EAAE;IACD,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,4DAA4D;IAC5D,KAAK,EAAE,SAAS,CAAC;IACjB,sGAAsG;IACtG,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,KAAK,IAAI,CAAC;IAC9D,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,2CA4OA"}