@burojs/ui-shadcn 0.5.0 → 0.7.0

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 (172) hide show
  1. package/README.md +9 -0
  2. package/dist/bulk-summary-BWSAS7k2.js +143 -0
  3. package/dist/bulk-summary-BWSAS7k2.js.map +1 -0
  4. package/dist/bulk-summary-CrEij5vq.cjs +143 -0
  5. package/dist/bulk-summary-CrEij5vq.cjs.map +1 -0
  6. package/dist/candidates-relation-picker-CS_JZ4Fd.js +31 -0
  7. package/dist/candidates-relation-picker-CS_JZ4Fd.js.map +1 -0
  8. package/dist/candidates-relation-picker-y5M6_oe4.cjs +31 -0
  9. package/dist/candidates-relation-picker-y5M6_oe4.cjs.map +1 -0
  10. package/dist/{check-confirm-dialog-CkaVTPT9.js → check-confirm-dialog-2j0gOd7x.js} +392 -103
  11. package/dist/check-confirm-dialog-2j0gOd7x.js.map +1 -0
  12. package/dist/{check-confirm-dialog-BRByTFpg.cjs → check-confirm-dialog-C-NHgMST.cjs} +476 -102
  13. package/dist/check-confirm-dialog-C-NHgMST.cjs.map +1 -0
  14. package/dist/{dialog-B_rt9S1c.js → dialog-C123sVQ8.js} +4 -4
  15. package/dist/dialog-C123sVQ8.js.map +1 -0
  16. package/dist/{dialog-DRNUq9EA.cjs → dialog-vmMdfUWs.cjs} +3 -3
  17. package/dist/dialog-vmMdfUWs.cjs.map +1 -0
  18. package/dist/{filter-builder-B3mZFL-K.cjs → filter-builder-DFu0JP-1.cjs} +959 -128
  19. package/dist/filter-builder-DFu0JP-1.cjs.map +1 -0
  20. package/dist/{filter-builder-DOnaXNfP.js → filter-builder-DaWHUZuV.js} +863 -116
  21. package/dist/filter-builder-DaWHUZuV.js.map +1 -0
  22. package/dist/{flow-runtime-B6zOHYqI.cjs → flow-runtime-BpmrIUb6.cjs} +61 -29
  23. package/dist/flow-runtime-BpmrIUb6.cjs.map +1 -0
  24. package/dist/{flow-runtime-D8h99FSg.js → flow-runtime-DyqTmK3H.js} +62 -30
  25. package/dist/flow-runtime-DyqTmK3H.js.map +1 -0
  26. package/dist/{flow-screen-CX05b3G9.cjs → flow-screen-Bcz9NF2V.cjs} +132 -88
  27. package/dist/flow-screen-Bcz9NF2V.cjs.map +1 -0
  28. package/dist/{flow-screen-CmbARSqg.js → flow-screen-DCdli6Ha.js} +132 -88
  29. package/dist/flow-screen-DCdli6Ha.js.map +1 -0
  30. package/dist/index.cjs +951 -605
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +618 -332
  33. package/dist/index.d.ts +618 -332
  34. package/dist/index.js +863 -522
  35. package/dist/index.js.map +1 -1
  36. package/dist/{inline-flow-host-B7U1OJUe.cjs → inline-flow-host-3vw7LzzD.cjs} +5 -5
  37. package/dist/inline-flow-host-3vw7LzzD.cjs.map +1 -0
  38. package/dist/{inline-flow-host-C3H2UpSA.js → inline-flow-host-C0rI9B7f.js} +5 -5
  39. package/dist/inline-flow-host-C0rI9B7f.js.map +1 -0
  40. package/dist/label-66W1qbke.js.map +1 -1
  41. package/dist/label-w6JYf2cF.cjs.map +1 -1
  42. package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs +153 -0
  43. package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs.map +1 -0
  44. package/dist/lazy-relation-picker-dialog-CAScLPb2.js +135 -0
  45. package/dist/lazy-relation-picker-dialog-CAScLPb2.js.map +1 -0
  46. package/dist/markdown/index.d.cts +2 -3
  47. package/dist/markdown/index.d.ts +2 -3
  48. package/dist/{page-flow-route-CyXSvXSE.js → page-flow-route-BHUEpn0I.js} +36 -23
  49. package/dist/page-flow-route-BHUEpn0I.js.map +1 -0
  50. package/dist/{page-flow-route-CQ7HsbBm.cjs → page-flow-route-DS8IVTVH.cjs} +41 -28
  51. package/dist/page-flow-route-DS8IVTVH.cjs.map +1 -0
  52. package/dist/{place-node-SIp4XYQH.cjs → place-node-CNb3w-c7.cjs} +2227 -999
  53. package/dist/place-node-CNb3w-c7.cjs.map +1 -0
  54. package/dist/{place-node--GdsP688.js → place-node-CfzQvh4Y.js} +2164 -984
  55. package/dist/place-node-CfzQvh4Y.js.map +1 -0
  56. package/dist/{related-list-cards-B6x9HQQp.js → related-list-cards-BIqqY69Y.js} +9 -4
  57. package/dist/{related-list-cards-B6x9HQQp.js.map → related-list-cards-BIqqY69Y.js.map} +1 -1
  58. package/dist/{related-list-cards-nxuODlIA.cjs → related-list-cards-CelMxl4q.cjs} +9 -4
  59. package/dist/{related-list-cards-nxuODlIA.cjs.map → related-list-cards-CelMxl4q.cjs.map} +1 -1
  60. package/dist/{related-list-timeline-DEdfd-rr.js → related-list-timeline-CRh7xwLD.js} +6 -7
  61. package/dist/related-list-timeline-CRh7xwLD.js.map +1 -0
  62. package/dist/{related-list-timeline-BYJKEbmA.cjs → related-list-timeline-DsLdi8sv.cjs} +5 -6
  63. package/dist/related-list-timeline-DsLdi8sv.cjs.map +1 -0
  64. package/dist/{related-rows-B4levHcR.js → related-rows--uP2iKQC.js} +27 -17
  65. package/dist/related-rows--uP2iKQC.js.map +1 -0
  66. package/dist/{related-rows-zh5f_4QR.cjs → related-rows-MPnLCmPF.cjs} +32 -22
  67. package/dist/related-rows-MPnLCmPF.cjs.map +1 -0
  68. package/dist/{search-access-CURsWn-U.js → relation-create-C43cdPQt.js} +596 -29
  69. package/dist/relation-create-C43cdPQt.js.map +1 -0
  70. package/dist/{search-access-B7G5oR8n.cjs → relation-create-DDEYcKfw.cjs} +630 -32
  71. package/dist/relation-create-DDEYcKfw.cjs.map +1 -0
  72. package/dist/{relation-many-picker-gdAOD-qK.js → relation-many-picker-BJhaEqxz.js} +210 -107
  73. package/dist/relation-many-picker-BJhaEqxz.js.map +1 -0
  74. package/dist/{relation-many-picker-B-NCEUgX.cjs → relation-many-picker-DzuGJpEl.cjs} +218 -115
  75. package/dist/relation-many-picker-DzuGJpEl.cjs.map +1 -0
  76. package/dist/{relation-picker-BAY77ivO.js → relation-picker-D-tBBZW_.js} +40 -45
  77. package/dist/relation-picker-D-tBBZW_.js.map +1 -0
  78. package/dist/{relation-picker-Da_uhFvA.cjs → relation-picker-DaIMzf2H.cjs} +45 -50
  79. package/dist/relation-picker-DaIMzf2H.cjs.map +1 -0
  80. package/dist/{relation-picker-dialog-M0pQaWlp.cjs → relation-picker-dialog-9Vg5vvR1.cjs} +142 -40
  81. package/dist/relation-picker-dialog-9Vg5vvR1.cjs.map +1 -0
  82. package/dist/{relation-picker-dialog-hA9rYrZB.js → relation-picker-dialog-DoEY8EkH.js} +143 -41
  83. package/dist/relation-picker-dialog-DoEY8EkH.js.map +1 -0
  84. package/dist/relation-select-B8Fq4o-t.js +48 -0
  85. package/dist/relation-select-B8Fq4o-t.js.map +1 -0
  86. package/dist/relation-select-Cr3CfNLy.cjs +55 -0
  87. package/dist/relation-select-Cr3CfNLy.cjs.map +1 -0
  88. package/dist/{resource-page-router-OSQyRA5b.cjs → resource-page-router-C_kGW2o_.cjs} +552 -303
  89. package/dist/resource-page-router-C_kGW2o_.cjs.map +1 -0
  90. package/dist/{resource-page-router-T4vS5gbJ.js → resource-page-router-Diw6S6TK.js} +507 -258
  91. package/dist/resource-page-router-Diw6S6TK.js.map +1 -0
  92. package/dist/{rich-text-vTyVjDoa.cjs → rich-text-C_7JCvRo.cjs} +2 -2
  93. package/dist/{rich-text-vTyVjDoa.cjs.map → rich-text-C_7JCvRo.cjs.map} +1 -1
  94. package/dist/{rich-text-DFerywgz.js → rich-text-RcPBf6hr.js} +2 -2
  95. package/dist/{rich-text-DFerywgz.js.map → rich-text-RcPBf6hr.js.map} +1 -1
  96. package/dist/success-notices-Dl3M1fZP.cjs +46 -0
  97. package/dist/success-notices-Dl3M1fZP.cjs.map +1 -0
  98. package/dist/success-notices-LDEgEkc4.js +41 -0
  99. package/dist/success-notices-LDEgEkc4.js.map +1 -0
  100. package/dist/{toaster-C7CiYA4Z.cjs → toaster-BbByOytP.cjs} +72 -80
  101. package/dist/toaster-BbByOytP.cjs.map +1 -0
  102. package/dist/{toaster-CfCRLgR-.js → toaster-Ct_YHGs6.js} +66 -68
  103. package/dist/toaster-Ct_YHGs6.js.map +1 -0
  104. package/dist/{use-dialog-filter-state-BDcgS5Oz.js → use-dialog-filter-state-B_MqzHaT.js} +5 -4
  105. package/dist/use-dialog-filter-state-B_MqzHaT.js.map +1 -0
  106. package/dist/{use-dialog-filter-state-BBCC0OEK.cjs → use-dialog-filter-state-tNvqAAjh.cjs} +6 -5
  107. package/dist/use-dialog-filter-state-tNvqAAjh.cjs.map +1 -0
  108. package/dist/use-theme-DFlpXils.cjs.map +1 -1
  109. package/dist/use-theme-dnE3Od3I.js.map +1 -1
  110. package/package.json +11 -11
  111. package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
  112. package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
  113. package/dist/bulk-summary-GxMDP4bw.js +0 -108
  114. package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
  115. package/dist/candidates-gate-CX2PBu6l.js +0 -63
  116. package/dist/candidates-gate-CX2PBu6l.js.map +0 -1
  117. package/dist/candidates-gate-DiFI52r7.cjs +0 -70
  118. package/dist/candidates-gate-DiFI52r7.cjs.map +0 -1
  119. package/dist/candidates-relation-picker-CBsVcnPR.js +0 -82
  120. package/dist/candidates-relation-picker-CBsVcnPR.js.map +0 -1
  121. package/dist/candidates-relation-picker-Dd_GVLlu.cjs +0 -84
  122. package/dist/candidates-relation-picker-Dd_GVLlu.cjs.map +0 -1
  123. package/dist/check-confirm-dialog-BRByTFpg.cjs.map +0 -1
  124. package/dist/check-confirm-dialog-CkaVTPT9.js.map +0 -1
  125. package/dist/dialog-B_rt9S1c.js.map +0 -1
  126. package/dist/dialog-DRNUq9EA.cjs.map +0 -1
  127. package/dist/filter-builder-B3mZFL-K.cjs.map +0 -1
  128. package/dist/filter-builder-DOnaXNfP.js.map +0 -1
  129. package/dist/flow-runtime-B6zOHYqI.cjs.map +0 -1
  130. package/dist/flow-runtime-D8h99FSg.js.map +0 -1
  131. package/dist/flow-screen-CX05b3G9.cjs.map +0 -1
  132. package/dist/flow-screen-CmbARSqg.js.map +0 -1
  133. package/dist/inline-flow-host-B7U1OJUe.cjs.map +0 -1
  134. package/dist/inline-flow-host-C3H2UpSA.js.map +0 -1
  135. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js +0 -14
  136. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js.map +0 -1
  137. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs +0 -19
  138. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs.map +0 -1
  139. package/dist/page-flow-route-CQ7HsbBm.cjs.map +0 -1
  140. package/dist/page-flow-route-CyXSvXSE.js.map +0 -1
  141. package/dist/peek-filter-CnqHwTMU.cjs +0 -51
  142. package/dist/peek-filter-CnqHwTMU.cjs.map +0 -1
  143. package/dist/peek-filter-ErLfO153.js +0 -28
  144. package/dist/peek-filter-ErLfO153.js.map +0 -1
  145. package/dist/place-node--GdsP688.js.map +0 -1
  146. package/dist/place-node-SIp4XYQH.cjs.map +0 -1
  147. package/dist/related-list-timeline-BYJKEbmA.cjs.map +0 -1
  148. package/dist/related-list-timeline-DEdfd-rr.js.map +0 -1
  149. package/dist/related-rows-B4levHcR.js.map +0 -1
  150. package/dist/related-rows-zh5f_4QR.cjs.map +0 -1
  151. package/dist/relation-create-BOaUe12G.cjs +0 -362
  152. package/dist/relation-create-BOaUe12G.cjs.map +0 -1
  153. package/dist/relation-create-Bg-eEJLs.js +0 -337
  154. package/dist/relation-create-Bg-eEJLs.js.map +0 -1
  155. package/dist/relation-many-picker-B-NCEUgX.cjs.map +0 -1
  156. package/dist/relation-many-picker-gdAOD-qK.js.map +0 -1
  157. package/dist/relation-picker-BAY77ivO.js.map +0 -1
  158. package/dist/relation-picker-Da_uhFvA.cjs.map +0 -1
  159. package/dist/relation-picker-dialog-M0pQaWlp.cjs.map +0 -1
  160. package/dist/relation-picker-dialog-hA9rYrZB.js.map +0 -1
  161. package/dist/resource-page-router-OSQyRA5b.cjs.map +0 -1
  162. package/dist/resource-page-router-T4vS5gbJ.js.map +0 -1
  163. package/dist/search-access-B7G5oR8n.cjs.map +0 -1
  164. package/dist/search-access-CURsWn-U.js.map +0 -1
  165. package/dist/shell-BPlk8hEa.js +0 -205
  166. package/dist/shell-BPlk8hEa.js.map +0 -1
  167. package/dist/shell-DxhlsPwm.cjs +0 -271
  168. package/dist/shell-DxhlsPwm.cjs.map +0 -1
  169. package/dist/toaster-C7CiYA4Z.cjs.map +0 -1
  170. package/dist/toaster-CfCRLgR-.js.map +0 -1
  171. package/dist/use-dialog-filter-state-BBCC0OEK.cjs.map +0 -1
  172. package/dist/use-dialog-filter-state-BDcgS5Oz.js.map +0 -1
@@ -1,11 +1,12 @@
1
- import { d as useLinkComponent } from "./toaster-CfCRLgR-.js";
2
- import { h as useFlowChrome, m as text } from "./check-confirm-dialog-CkaVTPT9.js";
1
+ import { l as useLinkComponent } from "./toaster-Ct_YHGs6.js";
2
+ import { _ as reasonText, m as useFlowChrome } from "./check-confirm-dialog-2j0gOd7x.js";
3
3
  import { t as Button } from "./button-MWVeZaqj.js";
4
- import { ht as recordRouteAvailable } from "./place-node--GdsP688.js";
5
- import { M as canonicalNavKey, a as recordErrorState, b as RecordState, m as usePublishRecordTrail, p as recordTrailKey, t as NavAreaNotFound } from "./resource-page-router-T4vS5gbJ.js";
6
- import { FlowHostBindingContext, FlowScreen } from "./flow-screen-CmbARSqg.js";
4
+ import { b as RecordState, v as InvalidRouteId, y as LoadError } from "./relation-create-C43cdPQt.js";
5
+ import { wt as recordRouteAvailable } from "./place-node-CfzQvh4Y.js";
6
+ import { M as canonicalNavKey, f as recordTrailKey, p as usePublishRecordTrail, t as NavAreaNotFound } from "./resource-page-router-Diw6S6TK.js";
7
+ import { FlowHostBindingContext, FlowScreen } from "./flow-screen-DCdli6Ha.js";
7
8
  import { compileFlow, dtoFields, effectiveFlow, hrefFor } from "@burojs/core";
8
- import { retryRecordRead, useApp, useApplicationSnapshot, useDispatcherEffects, useFlowRowResolver, useOne, useOptionalT, usePermissions, useProjectedTitle, useRouteNavigate, useTestIds } from "@burojs/react";
9
+ import { retryRecordRead, useApp, useApplicationSnapshot, useDispatcherEffects, useFlowRowResolver, useI18nCaps, useOne, useOptionalT, usePermissions, useProjectedTitle, useRouteNavigate, useRouteRecordId, useTestIds } from "@burojs/react";
9
10
  import { IconLoader2 } from "@tabler/icons-react";
10
11
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
11
12
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -31,10 +32,19 @@ function PageFlowRoute({ match }) {
31
32
  }
32
33
  function PageFlowRecord(props) {
33
34
  const { resource, id } = props;
35
+ const recordId = useRouteRecordId(resource, id);
36
+ if (recordId === void 0) return jsx(InvalidRouteId, { resource });
37
+ return jsx(PageFlowRead, {
38
+ ...props,
39
+ recordId
40
+ });
41
+ }
42
+ function PageFlowRead(props) {
43
+ const { resource, recordId } = props;
34
44
  const def = useApp().registry.get(resource);
35
45
  const projection = useMemo(() => def === void 0 ? void 0 : dtoFields(def, "record"), [def]);
36
46
  const query = useOne(resource, {
37
- id,
47
+ id: recordId,
38
48
  retry: retryRecordRead,
39
49
  ...projection === void 0 ? {} : { projection }
40
50
  });
@@ -43,17 +53,18 @@ function PageFlowRecord(props) {
43
53
  const record = result.data;
44
54
  if (record === null || typeof record !== "object") return jsx(RecordState, { state: "not-found" });
45
55
  return jsx(PageFlowGate, {
46
- ...props,
56
+ resource,
57
+ id: props.id,
58
+ name: props.name,
47
59
  record
48
60
  });
49
61
  }
50
- if (query.error != null) {
51
- const state = recordErrorState(query.error);
52
- return jsx(RecordState, {
53
- state,
54
- onRetry: state === "error" ? () => void query.refetch() : void 0
55
- });
56
- }
62
+ if (query.error != null) return jsx(LoadError, {
63
+ surface: "record",
64
+ resource,
65
+ error: query.error,
66
+ retry: () => void query.refetch()
67
+ });
57
68
  return jsx(RecordState, { state: "loading" });
58
69
  }
59
70
  function PageFlowGate(props) {
@@ -110,6 +121,7 @@ function usePageFlowFrame(props) {
110
121
  }
111
122
  function PageFlowUnavailable(props) {
112
123
  const t = useOptionalT();
124
+ const caps = useI18nCaps(props.resource);
113
125
  const tid = useTestIds();
114
126
  const { recordHref } = usePageFlowFrame(props);
115
127
  const Link = useLinkComponent();
@@ -121,16 +133,16 @@ function PageFlowUnavailable(props) {
121
133
  children: [
122
134
  jsx("p", {
123
135
  className: "font-medium",
124
- children: text(t, "flow.notAvailable", "This action is not available")
136
+ children: t("flow.notAvailable", { defaultValue: "This action is not available" })
125
137
  }),
126
138
  props.reason === void 0 ? null : jsx("p", {
127
139
  className: "text-muted-foreground",
128
- children: props.reason
140
+ children: reasonText(caps, props.reason, props.resource)
129
141
  }),
130
142
  jsx(Link, {
131
143
  href: recordHref,
132
144
  className: "underline",
133
- children: text(t, "flow.back", "Back")
145
+ children: t("flow.back", { defaultValue: "Back" })
134
146
  })
135
147
  ]
136
148
  }) });
@@ -153,6 +165,7 @@ function PageFlowRun(props) {
153
165
  const { resource, name, stale = false, onDirty, onMounted } = props;
154
166
  const [record] = useState(props.record);
155
167
  const t = useOptionalT();
168
+ const caps = useI18nCaps(resource);
156
169
  const tid = useTestIds();
157
170
  const navigate = useRouteNavigate();
158
171
  const Link = useLinkComponent();
@@ -228,14 +241,14 @@ function PageFlowRun(props) {
228
241
  href: recordHref,
229
242
  className: "ml-auto text-sm underline",
230
243
  "data-testid": `${tid.pageFlow(compiled?.flowId ?? name)}:back`,
231
- children: text(t, "flow.back", "Back")
244
+ children: t("flow.back", { defaultValue: "Back" })
232
245
  })
233
246
  }),
234
247
  stale ? jsxs("div", {
235
248
  role: "status",
236
249
  "data-buro-flow-state": "stale",
237
250
  className: "flex flex-wrap items-center gap-2 text-sm text-muted-foreground",
238
- children: [jsx("span", { children: text(t, "flow.noLongerApplies", "This action no longer applies") }), jsx(Button, {
251
+ children: [jsx("span", { children: t("flow.noLongerApplies", { defaultValue: "This action no longer applies" }) }), jsx(Button, {
239
252
  type: "button",
240
253
  size: "sm",
241
254
  variant: "outline",
@@ -243,7 +256,7 @@ function PageFlowRun(props) {
243
256
  flow.reset();
244
257
  setDiscarded(true);
245
258
  },
246
- children: text(t, "flow.discard", "Discard")
259
+ children: t("flow.discard", { defaultValue: "Discard" })
247
260
  })]
248
261
  }) : null,
249
262
  opened ? jsx(FlowHostBindingContext.Provider, {
@@ -263,7 +276,7 @@ function PageFlowRun(props) {
263
276
  }) : refused ? jsx("p", {
264
277
  role: "alert",
265
278
  className: "text-sm",
266
- children: snapshot.reason ?? text(t, "flow.unavailable", "Not allowed.")
279
+ children: snapshot.reason === void 0 ? t("flow.unavailable", { defaultValue: "Not allowed." }) : reasonText(caps, snapshot.reason, resource)
267
280
  }) : jsx(IconLoader2, {
268
281
  className: "h-5 w-5 animate-spin self-center",
269
282
  "aria-hidden": "true"
@@ -274,4 +287,4 @@ function PageFlowRun(props) {
274
287
  //#endregion
275
288
  export { PageFlowRoute };
276
289
 
277
- //# sourceMappingURL=page-flow-route-CyXSvXSE.js.map
290
+ //# sourceMappingURL=page-flow-route-BHUEpn0I.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-flow-route-BHUEpn0I.js","names":[],"sources":["../src/flows/page-flow-route.tsx"],"sourcesContent":["'use client';\n/**\n * A page-hosted flow's route (P6 3.11): `<record route>/<flow name>`. It owns the whole gate in\n * order, so a user who may not run the flow never sees a trace of it:\n *\n * 1. the permission map is decided, else the record route's loading state and nothing is read;\n * 2. static: the resource has a record page, the flow is an item flow and, in the published\n * snapshot, its `host` is `page`; else the record route's not-found;\n * 3. the record is read by id with the record DTO;\n * 4. the flow's gate over that row (`requires`, the mutation's `requires`, `action/run` rules):\n * hidden, absent, or a missing or forbidden row is the same not-found as `/articles/missing`;\n * a false `when` or a blocking pre-click check is \"not available\" and the way back;\n * 5. only then the run (`useFlowRun`) and its sources.\n */\nimport {\n type RecordId,\n type RouteMatch,\n compileFlow,\n dtoFields,\n effectiveFlow,\n hrefFor,\n} from '@burojs/core';\nimport {\n retryRecordRead,\n useApp,\n useApplicationSnapshot,\n useDispatcherEffects,\n useFlowRowResolver,\n useOne,\n useI18nCaps,\n useOptionalT,\n usePermissions,\n useProjectedTitle,\n useRouteNavigate,\n useRouteRecordId,\n useTestIds,\n} from '@burojs/react';\nimport { useFlowRun } from '@burojs/react/flows';\nimport { IconLoader2 } from '@tabler/icons-react';\nimport { type ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useLinkComponent } from '../layout/link-component.js';\nimport { reasonText } from '../lib/reason-text.js';\nimport { InvalidRouteId, LoadError } from '../load-error/load-error-view.js';\nimport { recordRouteAvailable } from '../layout/record-targets.js';\nimport { recordTrailKey, usePublishRecordTrail } from '../layout/record-trail-store.js';\nimport { RecordState } from '../record/record-state.js';\nimport { NavAreaNotFound } from '../layout/resource-page-router.js';\nimport { canonicalNavKey } from '../resource-key.js';\nimport { Button } from '../primitives/button.js';\nimport { useFlowChrome } from './chrome.js';\nimport { type FlowHostBinding, FlowHostBindingContext, FlowScreen } from './flow-screen.js';\n\ntype Row = { id: string } & Record<string, unknown>;\n\ninterface PageFlowProps {\n readonly resource: string;\n readonly id: string;\n readonly name: string;\n}\n\nexport interface PageFlowRouteProps {\n readonly match: RouteMatch;\n}\n\n/** The route: the static gate, then the record, then the flow's own gate. */\nexport function PageFlowRoute({ match }: PageFlowRouteProps): ReactElement {\n const app = useApp();\n const snapshot = useApplicationSnapshot();\n const permissions = usePermissions();\n const resource = canonicalNavKey(match as never);\n const id = match.id ?? '';\n const name = match.flow ?? '';\n // (1) Decided before anything is read.\n if (permissions?.status === 'loading') return <RecordState state=\"loading\" />;\n // (2) Static: a record page to hang the route on, and an item flow whose effective host is page.\n const flow = effectiveFlow(snapshot, match.ref, name);\n const hasRecordPage =\n recordRouteAvailable(app.registry, resource, 'show') ||\n recordRouteAvailable(app.registry, resource, 'edit');\n // A name no item flow answers to is no route at all (`/orders/x/segments`): the framework 404,\n // as for any unmatched URL. A flow that exists but is not page-hosted, or a record page that is\n // missing, keeps the record route's not-found, so a denied flow reads like a missing record.\n if (flow === undefined) return <NavAreaNotFound />;\n if (!hasRecordPage || flow.host !== 'page' || id === '') return <RecordState state=\"not-found\" />;\n return <PageFlowRecord key={`${resource}:${id}:${name}`} {...{ resource, id, name }} />;\n}\n\n/** (3) The record by id, with the record DTO: the same read the record route makes. */\nfunction PageFlowRecord(props: PageFlowProps): ReactElement {\n const { resource, id } = props;\n // The URL's id as the id field types it (spec 4.19); an invalid one names no record.\n const recordId = useRouteRecordId(resource, id);\n if (recordId === undefined) return <InvalidRouteId resource={resource} />;\n return <PageFlowRead {...props} recordId={recordId} />;\n}\n\n/** The record by its typed id. */\nfunction PageFlowRead(props: PageFlowProps & { readonly recordId: RecordId }): ReactElement {\n const { resource, recordId } = props;\n const app = useApp();\n const def = app.registry.get(resource);\n const projection = useMemo(\n () => (def === undefined ? undefined : dtoFields(def, 'record')),\n [def],\n );\n const query = useOne(resource, {\n id: recordId,\n retry: retryRecordRead,\n ...(projection === undefined ? {} : { projection }),\n });\n const result = query.data as { readonly data?: unknown } | undefined;\n if (result !== undefined) {\n const record = result.data;\n if (record === null || typeof record !== 'object') return <RecordState state=\"not-found\" />;\n return (\n <PageFlowGate resource={resource} id={props.id} name={props.name} record={record as Row} />\n );\n }\n if (query.error != null) {\n return (\n <LoadError\n surface=\"record\"\n resource={resource}\n error={query.error}\n retry={() => void query.refetch()}\n />\n );\n }\n return <RecordState state=\"loading\" />;\n}\n\n/** (4) The flow's gate over the row, as its button decides it: no second rule here. */\nfunction PageFlowGate(props: PageFlowProps & { readonly record: Row }): ReactElement {\n const { resource, name, record } = props;\n const resolve = useFlowRowResolver(resource);\n const control = resolve(name, record);\n // Someone else's change can take the flow away under a form that holds input: the page keeps\n // the form, read-only, with the \"no longer applies\" notice and Discard (3.12), instead of\n // unmounting it silently. `useRef` here, `PageFlowRun` mounted from the first available render.\n const dirty = useRef(false);\n const running = useRef(false);\n const goneNow =\n (control.state === 'absent' && control.cause === 'when') || control.state === 'disabled';\n const keep = goneNow && running.current && dirty.current;\n const track = (value: boolean) => {\n dirty.current = value;\n };\n if (keep)\n return (\n <PageFlowRun {...props} stale onDirty={track} onMounted={(on) => (running.current = on)} />\n );\n switch (control.state) {\n case 'undecided':\n case 'pending':\n return <RecordState state=\"loading\" />;\n case 'absent':\n return control.cause === 'when' ? (\n <PageFlowUnavailable {...props} />\n ) : (\n <RecordState state=\"not-found\" />\n );\n case 'disabled':\n return (\n <PageFlowUnavailable\n {...props}\n {...(control.reason === undefined ? {} : { reason: control.reason })}\n />\n );\n default:\n return (\n <PageFlowRun\n {...props}\n record={(control.record ?? record) as Row}\n onDirty={track}\n onMounted={(on) => (running.current = on)}\n />\n );\n }\n}\n\n/**\n * The flow's name, the record's title and the record's way back, shared by every state of the\n * page that shows the record. It also tells the header's trail (`<HeaderBreadcrumbs>`) the\n * record's title and the flow's label, which the trail ends with: only these states do, so a\n * loading or not-found flow route never names its flow.\n */\nfunction usePageFlowFrame(props: PageFlowProps & { readonly record: Row }) {\n const { resource, id, name, record } = props;\n const app = useApp();\n const chrome = useFlowChrome(resource, name);\n const title = useProjectedTitle(resource, id, record);\n const showable = recordRouteAvailable(app.registry, resource, 'show');\n const recordHref = hrefFor(app.registry, resource, {\n action: showable ? 'show' : 'edit',\n id,\n });\n usePublishRecordTrail({\n key: recordTrailKey(resource, id, `flow:${name}`),\n title,\n flowLabel: chrome.labelText,\n });\n return { chrome, title, recordHref };\n}\n\n/** `when` false, or a blocking check: the reason and the way back, no form. */\nfunction PageFlowUnavailable(\n props: PageFlowProps & { readonly record: Row; readonly reason?: string },\n): ReactElement {\n const t = useOptionalT();\n const caps = useI18nCaps(props.resource);\n const tid = useTestIds();\n const { recordHref } = usePageFlowFrame(props);\n const Link = useLinkComponent();\n const flowId = usePageFlowId(props.resource, props.name);\n return (\n <div>\n <div\n role=\"status\"\n data-testid={`${tid.pageFlow(flowId)}:unavailable`}\n className=\"flex flex-col gap-2 px-1 py-6 text-sm\"\n >\n <p className=\"font-medium\">\n {t('flow.notAvailable', { defaultValue: 'This action is not available' })}\n </p>\n {props.reason === undefined ? null : (\n <p className=\"text-muted-foreground\">{reasonText(caps, props.reason, props.resource)}</p>\n )}\n <Link href={recordHref} className=\"underline\">\n {t('flow.back', { defaultValue: 'Back' })}\n </Link>\n </div>\n </div>\n );\n}\n\n/** The flow's identity (`default:articles/actions/rename`), for test ids; stable when compiled. */\nfunction usePageFlowId(resource: string, name: string): string {\n const app = useApp();\n const def = app.registry.get(resource);\n return useMemo(() => {\n try {\n return def === undefined\n ? `${resource}/actions/${name}`\n : compileFlow(def as never, name).flowId;\n } catch {\n return `${resource}/actions/${name}`;\n }\n }, [def, resource, name]);\n}\n\n/** (5) The run and its host. */\nfunction PageFlowRun(\n props: PageFlowProps & {\n readonly record: Row;\n /** The flow no longer applies: the form stays, read-only, with a notice and Discard. */\n readonly stale?: boolean;\n readonly onDirty?: (dirty: boolean) => void;\n readonly onMounted?: (mounted: boolean) => void;\n },\n): ReactElement {\n const { resource, name, stale = false, onDirty, onMounted } = props;\n // The run belongs to the record as it was opened: the refetch after the write must not build\n // a new run (and lose the outcome `then` is about to run on). Display reads the fresh one.\n const [record] = useState(props.record);\n const t = useOptionalT();\n const caps = useI18nCaps(resource);\n const tid = useTestIds();\n const navigate = useRouteNavigate();\n const Link = useLinkComponent();\n const scopeEffects = useDispatcherEffects();\n const { chrome, title, recordHref } = usePageFlowFrame(props);\n const navigateRef = useRef(navigate);\n navigateRef.current = navigate;\n // `redirect` replaces the page: Back cannot reach a form that has been submitted.\n const thenEffects = useCallback(\n () => ({\n ...scopeEffects(),\n redirect: (to: Parameters<typeof navigate>[0]) => navigateRef.current(to, { replace: true }),\n }),\n [scopeEffects],\n );\n const target = useMemo(() => ({ resource, action: name }), [resource, name]);\n const flow = useFlowRun(target, { record, surface: 'page', thenEffects });\n // `then` is the page's to run, once it left the form (4.13).\n flow.holdThen(true);\n const { run, snapshot, compiled } = flow;\n const flowRef = useRef(flow);\n flowRef.current = flow;\n\n // The page leaves the form behind: after the form below it reset (a clean form asks the guard\n // nothing), replace the page by the destination, then run `then` over it.\n const [finished, setFinished] = useState(false);\n useEffect(() => {\n if (!finished) return;\n const current = flowRef.current;\n if (current.compiled?.flow.then?.redirect === undefined)\n navigateRef.current(recordHref, { replace: true });\n // After the route committed: an overlay opened by `then` belongs to the destination, and a\n // route change in flight would close it again.\n setTimeout(() => current.runThen(), 0);\n }, [finished, recordHref]);\n const binding = useMemo<FlowHostBinding>(\n () => ({\n kind: 'page',\n close: () => navigateRef.current(recordHref),\n finish: () => setFinished(true),\n }),\n [recordHref],\n );\n\n useEffect(() => {\n onMounted?.(true);\n return () => onMounted?.(false);\n }, [onMounted]);\n // Unsaved input or an unresolved attempt: what the gate must not unmount silently.\n useEffect(() => {\n if (run === null) return;\n let initial: string | undefined;\n const sync = () => {\n const now = run.getSnapshot();\n if (initial === undefined && now.history.includes('open'))\n initial = JSON.stringify(now.values);\n onDirty?.(\n now.outcome?.outcome !== 'success' &&\n (now.outcome?.outcome === 'unknown' ||\n (initial !== undefined && JSON.stringify(now.values) !== initial)),\n );\n };\n sync();\n return run.subscribe(sync);\n }, [run, onDirty]);\n // Discard: the input is dropped first, so the guard has nothing to ask, then the page leaves.\n const [discarded, setDiscarded] = useState(false);\n useEffect(() => {\n if (discarded) navigateRef.current(recordHref, { replace: true });\n }, [discarded, recordHref]);\n\n const documentTitle = `${chrome.labelText}${title === '' ? '' : ` · ${title}`}`;\n useEffect(() => {\n const previous = document.title;\n document.title = documentTitle;\n return () => {\n document.title = previous;\n };\n }, [documentTitle]);\n\n const opened =\n run !== null &&\n compiled !== null &&\n (snapshot.history.includes('open') || snapshot.step === 'outcome');\n const refused = snapshot.control === 'disabled' || snapshot.control === 'absent';\n return (\n <div\n data-testid={compiled === null ? undefined : tid.pageFlow(compiled.flowId)}\n className=\"flex min-h-0 flex-col gap-4\"\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <Link\n href={recordHref}\n className=\"ml-auto text-sm underline\"\n data-testid={`${tid.pageFlow(compiled?.flowId ?? name)}:back`}\n >\n {t('flow.back', { defaultValue: 'Back' })}\n </Link>\n </div>\n {stale ? (\n <div\n role=\"status\"\n data-buro-flow-state=\"stale\"\n className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\"\n >\n <span>\n {t('flow.noLongerApplies', { defaultValue: 'This action no longer applies' })}\n </span>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n flow.reset();\n setDiscarded(true);\n }}\n >\n {t('flow.discard', { defaultValue: 'Discard' })}\n </Button>\n </div>\n ) : null}\n {opened ? (\n <FlowHostBindingContext.Provider value={binding}>\n <fieldset disabled={stale} className=\"m-0 min-w-0 border-0 p-0\">\n <FlowScreen\n host=\"page\"\n run={run}\n compiled={compiled}\n label={chrome.labelText}\n {...(title === '' ? {} : { title })}\n record={record}\n />\n </fieldset>\n </FlowHostBindingContext.Provider>\n ) : refused ? (\n <p role=\"alert\" className=\"text-sm\">\n {snapshot.reason === undefined\n ? t('flow.unavailable', { defaultValue: 'Not allowed.' })\n : reasonText(caps, snapshot.reason, resource)}\n </p>\n ) : (\n <IconLoader2 className=\"h-5 w-5 animate-spin self-center\" aria-hidden=\"true\" />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,9 +1,10 @@
1
- const require_toaster = require("./toaster-C7CiYA4Z.cjs");
2
- const require_check_confirm_dialog = require("./check-confirm-dialog-BRByTFpg.cjs");
1
+ const require_toaster = require("./toaster-BbByOytP.cjs");
2
+ const require_check_confirm_dialog = require("./check-confirm-dialog-C-NHgMST.cjs");
3
3
  const require_button = require("./button-BxnnE28H.cjs");
4
- const require_place_node = require("./place-node-SIp4XYQH.cjs");
5
- const require_resource_page_router = require("./resource-page-router-OSQyRA5b.cjs");
6
- const require_flow_screen = require("./flow-screen-CX05b3G9.cjs");
4
+ const require_relation_create = require("./relation-create-DDEYcKfw.cjs");
5
+ const require_place_node = require("./place-node-CNb3w-c7.cjs");
6
+ const require_resource_page_router = require("./resource-page-router-C_kGW2o_.cjs");
7
+ const require_flow_screen = require("./flow-screen-Bcz9NF2V.cjs");
7
8
  let _burojs_core = require("@burojs/core");
8
9
  let _burojs_react = require("@burojs/react");
9
10
  let _tabler_icons_react = require("@tabler/icons-react");
@@ -18,11 +19,11 @@ function PageFlowRoute({ match }) {
18
19
  const resource = require_resource_page_router.canonicalNavKey(match);
19
20
  const id = match.id ?? "";
20
21
  const name = match.flow ?? "";
21
- if (permissions?.status === "loading") return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "loading" });
22
+ if (permissions?.status === "loading") return (0, react_jsx_runtime.jsx)(require_relation_create.RecordState, { state: "loading" });
22
23
  const flow = (0, _burojs_core.effectiveFlow)(snapshot, match.ref, name);
23
24
  const hasRecordPage = require_place_node.recordRouteAvailable(app.registry, resource, "show") || require_place_node.recordRouteAvailable(app.registry, resource, "edit");
24
25
  if (flow === void 0) return (0, react_jsx_runtime.jsx)(require_resource_page_router.NavAreaNotFound, {});
25
- if (!hasRecordPage || flow.host !== "page" || id === "") return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "not-found" });
26
+ if (!hasRecordPage || flow.host !== "page" || id === "") return (0, react_jsx_runtime.jsx)(require_relation_create.RecordState, { state: "not-found" });
26
27
  return (0, react_jsx_runtime.jsx)(PageFlowRecord, {
27
28
  resource,
28
29
  id,
@@ -31,30 +32,40 @@ function PageFlowRoute({ match }) {
31
32
  }
32
33
  function PageFlowRecord(props) {
33
34
  const { resource, id } = props;
35
+ const recordId = (0, _burojs_react.useRouteRecordId)(resource, id);
36
+ if (recordId === void 0) return (0, react_jsx_runtime.jsx)(require_relation_create.InvalidRouteId, { resource });
37
+ return (0, react_jsx_runtime.jsx)(PageFlowRead, {
38
+ ...props,
39
+ recordId
40
+ });
41
+ }
42
+ function PageFlowRead(props) {
43
+ const { resource, recordId } = props;
34
44
  const def = (0, _burojs_react.useApp)().registry.get(resource);
35
45
  const projection = (0, react.useMemo)(() => def === void 0 ? void 0 : (0, _burojs_core.dtoFields)(def, "record"), [def]);
36
46
  const query = (0, _burojs_react.useOne)(resource, {
37
- id,
47
+ id: recordId,
38
48
  retry: _burojs_react.retryRecordRead,
39
49
  ...projection === void 0 ? {} : { projection }
40
50
  });
41
51
  const result = query.data;
42
52
  if (result !== void 0) {
43
53
  const record = result.data;
44
- if (record === null || typeof record !== "object") return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "not-found" });
54
+ if (record === null || typeof record !== "object") return (0, react_jsx_runtime.jsx)(require_relation_create.RecordState, { state: "not-found" });
45
55
  return (0, react_jsx_runtime.jsx)(PageFlowGate, {
46
- ...props,
56
+ resource,
57
+ id: props.id,
58
+ name: props.name,
47
59
  record
48
60
  });
49
61
  }
50
- if (query.error != null) {
51
- const state = require_resource_page_router.recordErrorState(query.error);
52
- return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, {
53
- state,
54
- onRetry: state === "error" ? () => void query.refetch() : void 0
55
- });
56
- }
57
- return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "loading" });
62
+ if (query.error != null) return (0, react_jsx_runtime.jsx)(require_relation_create.LoadError, {
63
+ surface: "record",
64
+ resource,
65
+ error: query.error,
66
+ retry: () => void query.refetch()
67
+ });
68
+ return (0, react_jsx_runtime.jsx)(require_relation_create.RecordState, { state: "loading" });
58
69
  }
59
70
  function PageFlowGate(props) {
60
71
  const { resource, name, record } = props;
@@ -73,8 +84,8 @@ function PageFlowGate(props) {
73
84
  });
74
85
  switch (control.state) {
75
86
  case "undecided":
76
- case "pending": return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "loading" });
77
- case "absent": return control.cause === "when" ? (0, react_jsx_runtime.jsx)(PageFlowUnavailable, { ...props }) : (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "not-found" });
87
+ case "pending": return (0, react_jsx_runtime.jsx)(require_relation_create.RecordState, { state: "loading" });
88
+ case "absent": return control.cause === "when" ? (0, react_jsx_runtime.jsx)(PageFlowUnavailable, { ...props }) : (0, react_jsx_runtime.jsx)(require_relation_create.RecordState, { state: "not-found" });
78
89
  case "disabled": return (0, react_jsx_runtime.jsx)(PageFlowUnavailable, {
79
90
  ...props,
80
91
  ...control.reason === void 0 ? {} : { reason: control.reason }
@@ -110,6 +121,7 @@ function usePageFlowFrame(props) {
110
121
  }
111
122
  function PageFlowUnavailable(props) {
112
123
  const t = (0, _burojs_react.useOptionalT)();
124
+ const caps = (0, _burojs_react.useI18nCaps)(props.resource);
113
125
  const tid = (0, _burojs_react.useTestIds)();
114
126
  const { recordHref } = usePageFlowFrame(props);
115
127
  const Link = require_toaster.useLinkComponent();
@@ -121,16 +133,16 @@ function PageFlowUnavailable(props) {
121
133
  children: [
122
134
  (0, react_jsx_runtime.jsx)("p", {
123
135
  className: "font-medium",
124
- children: require_check_confirm_dialog.text(t, "flow.notAvailable", "This action is not available")
136
+ children: t("flow.notAvailable", { defaultValue: "This action is not available" })
125
137
  }),
126
138
  props.reason === void 0 ? null : (0, react_jsx_runtime.jsx)("p", {
127
139
  className: "text-muted-foreground",
128
- children: props.reason
140
+ children: require_check_confirm_dialog.reasonText(caps, props.reason, props.resource)
129
141
  }),
130
142
  (0, react_jsx_runtime.jsx)(Link, {
131
143
  href: recordHref,
132
144
  className: "underline",
133
- children: require_check_confirm_dialog.text(t, "flow.back", "Back")
145
+ children: t("flow.back", { defaultValue: "Back" })
134
146
  })
135
147
  ]
136
148
  }) });
@@ -153,6 +165,7 @@ function PageFlowRun(props) {
153
165
  const { resource, name, stale = false, onDirty, onMounted } = props;
154
166
  const [record] = (0, react.useState)(props.record);
155
167
  const t = (0, _burojs_react.useOptionalT)();
168
+ const caps = (0, _burojs_react.useI18nCaps)(resource);
156
169
  const tid = (0, _burojs_react.useTestIds)();
157
170
  const navigate = (0, _burojs_react.useRouteNavigate)();
158
171
  const Link = require_toaster.useLinkComponent();
@@ -228,14 +241,14 @@ function PageFlowRun(props) {
228
241
  href: recordHref,
229
242
  className: "ml-auto text-sm underline",
230
243
  "data-testid": `${tid.pageFlow(compiled?.flowId ?? name)}:back`,
231
- children: require_check_confirm_dialog.text(t, "flow.back", "Back")
244
+ children: t("flow.back", { defaultValue: "Back" })
232
245
  })
233
246
  }),
234
247
  stale ? (0, react_jsx_runtime.jsxs)("div", {
235
248
  role: "status",
236
249
  "data-buro-flow-state": "stale",
237
250
  className: "flex flex-wrap items-center gap-2 text-sm text-muted-foreground",
238
- children: [(0, react_jsx_runtime.jsx)("span", { children: require_check_confirm_dialog.text(t, "flow.noLongerApplies", "This action no longer applies") }), (0, react_jsx_runtime.jsx)(require_button.Button, {
251
+ children: [(0, react_jsx_runtime.jsx)("span", { children: t("flow.noLongerApplies", { defaultValue: "This action no longer applies" }) }), (0, react_jsx_runtime.jsx)(require_button.Button, {
239
252
  type: "button",
240
253
  size: "sm",
241
254
  variant: "outline",
@@ -243,7 +256,7 @@ function PageFlowRun(props) {
243
256
  flow.reset();
244
257
  setDiscarded(true);
245
258
  },
246
- children: require_check_confirm_dialog.text(t, "flow.discard", "Discard")
259
+ children: t("flow.discard", { defaultValue: "Discard" })
247
260
  })]
248
261
  }) : null,
249
262
  opened ? (0, react_jsx_runtime.jsx)(require_flow_screen.FlowHostBindingContext.Provider, {
@@ -263,7 +276,7 @@ function PageFlowRun(props) {
263
276
  }) : refused ? (0, react_jsx_runtime.jsx)("p", {
264
277
  role: "alert",
265
278
  className: "text-sm",
266
- children: snapshot.reason ?? require_check_confirm_dialog.text(t, "flow.unavailable", "Not allowed.")
279
+ children: snapshot.reason === void 0 ? t("flow.unavailable", { defaultValue: "Not allowed." }) : require_check_confirm_dialog.reasonText(caps, snapshot.reason, resource)
267
280
  }) : (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconLoader2, {
268
281
  className: "h-5 w-5 animate-spin self-center",
269
282
  "aria-hidden": "true"
@@ -274,4 +287,4 @@ function PageFlowRun(props) {
274
287
  //#endregion
275
288
  exports.PageFlowRoute = PageFlowRoute;
276
289
 
277
- //# sourceMappingURL=page-flow-route-CQ7HsbBm.cjs.map
290
+ //# sourceMappingURL=page-flow-route-DS8IVTVH.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-flow-route-DS8IVTVH.cjs","names":["useApp","useApplicationSnapshot","usePermissions","canonicalNavKey","RecordState","effectiveFlow","recordRouteAvailable","NavAreaNotFound","useRouteRecordId","InvalidRouteId","useMemo","dtoFields","useOne","retryRecordRead","LoadError","useFlowRowResolver","useRef","useFlowChrome","useProjectedTitle","hrefFor","recordTrailKey","useOptionalT","useI18nCaps","useTestIds","useLinkComponent","reasonText","compileFlow","useState","useRouteNavigate","useDispatcherEffects","useCallback","useFlowRun","Button","FlowHostBindingContext","FlowScreen","IconLoader2"],"sources":["../src/flows/page-flow-route.tsx"],"sourcesContent":["'use client';\n/**\n * A page-hosted flow's route (P6 3.11): `<record route>/<flow name>`. It owns the whole gate in\n * order, so a user who may not run the flow never sees a trace of it:\n *\n * 1. the permission map is decided, else the record route's loading state and nothing is read;\n * 2. static: the resource has a record page, the flow is an item flow and, in the published\n * snapshot, its `host` is `page`; else the record route's not-found;\n * 3. the record is read by id with the record DTO;\n * 4. the flow's gate over that row (`requires`, the mutation's `requires`, `action/run` rules):\n * hidden, absent, or a missing or forbidden row is the same not-found as `/articles/missing`;\n * a false `when` or a blocking pre-click check is \"not available\" and the way back;\n * 5. only then the run (`useFlowRun`) and its sources.\n */\nimport {\n type RecordId,\n type RouteMatch,\n compileFlow,\n dtoFields,\n effectiveFlow,\n hrefFor,\n} from '@burojs/core';\nimport {\n retryRecordRead,\n useApp,\n useApplicationSnapshot,\n useDispatcherEffects,\n useFlowRowResolver,\n useOne,\n useI18nCaps,\n useOptionalT,\n usePermissions,\n useProjectedTitle,\n useRouteNavigate,\n useRouteRecordId,\n useTestIds,\n} from '@burojs/react';\nimport { useFlowRun } from '@burojs/react/flows';\nimport { IconLoader2 } from '@tabler/icons-react';\nimport { type ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useLinkComponent } from '../layout/link-component.js';\nimport { reasonText } from '../lib/reason-text.js';\nimport { InvalidRouteId, LoadError } from '../load-error/load-error-view.js';\nimport { recordRouteAvailable } from '../layout/record-targets.js';\nimport { recordTrailKey, usePublishRecordTrail } from '../layout/record-trail-store.js';\nimport { RecordState } from '../record/record-state.js';\nimport { NavAreaNotFound } from '../layout/resource-page-router.js';\nimport { canonicalNavKey } from '../resource-key.js';\nimport { Button } from '../primitives/button.js';\nimport { useFlowChrome } from './chrome.js';\nimport { type FlowHostBinding, FlowHostBindingContext, FlowScreen } from './flow-screen.js';\n\ntype Row = { id: string } & Record<string, unknown>;\n\ninterface PageFlowProps {\n readonly resource: string;\n readonly id: string;\n readonly name: string;\n}\n\nexport interface PageFlowRouteProps {\n readonly match: RouteMatch;\n}\n\n/** The route: the static gate, then the record, then the flow's own gate. */\nexport function PageFlowRoute({ match }: PageFlowRouteProps): ReactElement {\n const app = useApp();\n const snapshot = useApplicationSnapshot();\n const permissions = usePermissions();\n const resource = canonicalNavKey(match as never);\n const id = match.id ?? '';\n const name = match.flow ?? '';\n // (1) Decided before anything is read.\n if (permissions?.status === 'loading') return <RecordState state=\"loading\" />;\n // (2) Static: a record page to hang the route on, and an item flow whose effective host is page.\n const flow = effectiveFlow(snapshot, match.ref, name);\n const hasRecordPage =\n recordRouteAvailable(app.registry, resource, 'show') ||\n recordRouteAvailable(app.registry, resource, 'edit');\n // A name no item flow answers to is no route at all (`/orders/x/segments`): the framework 404,\n // as for any unmatched URL. A flow that exists but is not page-hosted, or a record page that is\n // missing, keeps the record route's not-found, so a denied flow reads like a missing record.\n if (flow === undefined) return <NavAreaNotFound />;\n if (!hasRecordPage || flow.host !== 'page' || id === '') return <RecordState state=\"not-found\" />;\n return <PageFlowRecord key={`${resource}:${id}:${name}`} {...{ resource, id, name }} />;\n}\n\n/** (3) The record by id, with the record DTO: the same read the record route makes. */\nfunction PageFlowRecord(props: PageFlowProps): ReactElement {\n const { resource, id } = props;\n // The URL's id as the id field types it (spec 4.19); an invalid one names no record.\n const recordId = useRouteRecordId(resource, id);\n if (recordId === undefined) return <InvalidRouteId resource={resource} />;\n return <PageFlowRead {...props} recordId={recordId} />;\n}\n\n/** The record by its typed id. */\nfunction PageFlowRead(props: PageFlowProps & { readonly recordId: RecordId }): ReactElement {\n const { resource, recordId } = props;\n const app = useApp();\n const def = app.registry.get(resource);\n const projection = useMemo(\n () => (def === undefined ? undefined : dtoFields(def, 'record')),\n [def],\n );\n const query = useOne(resource, {\n id: recordId,\n retry: retryRecordRead,\n ...(projection === undefined ? {} : { projection }),\n });\n const result = query.data as { readonly data?: unknown } | undefined;\n if (result !== undefined) {\n const record = result.data;\n if (record === null || typeof record !== 'object') return <RecordState state=\"not-found\" />;\n return (\n <PageFlowGate resource={resource} id={props.id} name={props.name} record={record as Row} />\n );\n }\n if (query.error != null) {\n return (\n <LoadError\n surface=\"record\"\n resource={resource}\n error={query.error}\n retry={() => void query.refetch()}\n />\n );\n }\n return <RecordState state=\"loading\" />;\n}\n\n/** (4) The flow's gate over the row, as its button decides it: no second rule here. */\nfunction PageFlowGate(props: PageFlowProps & { readonly record: Row }): ReactElement {\n const { resource, name, record } = props;\n const resolve = useFlowRowResolver(resource);\n const control = resolve(name, record);\n // Someone else's change can take the flow away under a form that holds input: the page keeps\n // the form, read-only, with the \"no longer applies\" notice and Discard (3.12), instead of\n // unmounting it silently. `useRef` here, `PageFlowRun` mounted from the first available render.\n const dirty = useRef(false);\n const running = useRef(false);\n const goneNow =\n (control.state === 'absent' && control.cause === 'when') || control.state === 'disabled';\n const keep = goneNow && running.current && dirty.current;\n const track = (value: boolean) => {\n dirty.current = value;\n };\n if (keep)\n return (\n <PageFlowRun {...props} stale onDirty={track} onMounted={(on) => (running.current = on)} />\n );\n switch (control.state) {\n case 'undecided':\n case 'pending':\n return <RecordState state=\"loading\" />;\n case 'absent':\n return control.cause === 'when' ? (\n <PageFlowUnavailable {...props} />\n ) : (\n <RecordState state=\"not-found\" />\n );\n case 'disabled':\n return (\n <PageFlowUnavailable\n {...props}\n {...(control.reason === undefined ? {} : { reason: control.reason })}\n />\n );\n default:\n return (\n <PageFlowRun\n {...props}\n record={(control.record ?? record) as Row}\n onDirty={track}\n onMounted={(on) => (running.current = on)}\n />\n );\n }\n}\n\n/**\n * The flow's name, the record's title and the record's way back, shared by every state of the\n * page that shows the record. It also tells the header's trail (`<HeaderBreadcrumbs>`) the\n * record's title and the flow's label, which the trail ends with: only these states do, so a\n * loading or not-found flow route never names its flow.\n */\nfunction usePageFlowFrame(props: PageFlowProps & { readonly record: Row }) {\n const { resource, id, name, record } = props;\n const app = useApp();\n const chrome = useFlowChrome(resource, name);\n const title = useProjectedTitle(resource, id, record);\n const showable = recordRouteAvailable(app.registry, resource, 'show');\n const recordHref = hrefFor(app.registry, resource, {\n action: showable ? 'show' : 'edit',\n id,\n });\n usePublishRecordTrail({\n key: recordTrailKey(resource, id, `flow:${name}`),\n title,\n flowLabel: chrome.labelText,\n });\n return { chrome, title, recordHref };\n}\n\n/** `when` false, or a blocking check: the reason and the way back, no form. */\nfunction PageFlowUnavailable(\n props: PageFlowProps & { readonly record: Row; readonly reason?: string },\n): ReactElement {\n const t = useOptionalT();\n const caps = useI18nCaps(props.resource);\n const tid = useTestIds();\n const { recordHref } = usePageFlowFrame(props);\n const Link = useLinkComponent();\n const flowId = usePageFlowId(props.resource, props.name);\n return (\n <div>\n <div\n role=\"status\"\n data-testid={`${tid.pageFlow(flowId)}:unavailable`}\n className=\"flex flex-col gap-2 px-1 py-6 text-sm\"\n >\n <p className=\"font-medium\">\n {t('flow.notAvailable', { defaultValue: 'This action is not available' })}\n </p>\n {props.reason === undefined ? null : (\n <p className=\"text-muted-foreground\">{reasonText(caps, props.reason, props.resource)}</p>\n )}\n <Link href={recordHref} className=\"underline\">\n {t('flow.back', { defaultValue: 'Back' })}\n </Link>\n </div>\n </div>\n );\n}\n\n/** The flow's identity (`default:articles/actions/rename`), for test ids; stable when compiled. */\nfunction usePageFlowId(resource: string, name: string): string {\n const app = useApp();\n const def = app.registry.get(resource);\n return useMemo(() => {\n try {\n return def === undefined\n ? `${resource}/actions/${name}`\n : compileFlow(def as never, name).flowId;\n } catch {\n return `${resource}/actions/${name}`;\n }\n }, [def, resource, name]);\n}\n\n/** (5) The run and its host. */\nfunction PageFlowRun(\n props: PageFlowProps & {\n readonly record: Row;\n /** The flow no longer applies: the form stays, read-only, with a notice and Discard. */\n readonly stale?: boolean;\n readonly onDirty?: (dirty: boolean) => void;\n readonly onMounted?: (mounted: boolean) => void;\n },\n): ReactElement {\n const { resource, name, stale = false, onDirty, onMounted } = props;\n // The run belongs to the record as it was opened: the refetch after the write must not build\n // a new run (and lose the outcome `then` is about to run on). Display reads the fresh one.\n const [record] = useState(props.record);\n const t = useOptionalT();\n const caps = useI18nCaps(resource);\n const tid = useTestIds();\n const navigate = useRouteNavigate();\n const Link = useLinkComponent();\n const scopeEffects = useDispatcherEffects();\n const { chrome, title, recordHref } = usePageFlowFrame(props);\n const navigateRef = useRef(navigate);\n navigateRef.current = navigate;\n // `redirect` replaces the page: Back cannot reach a form that has been submitted.\n const thenEffects = useCallback(\n () => ({\n ...scopeEffects(),\n redirect: (to: Parameters<typeof navigate>[0]) => navigateRef.current(to, { replace: true }),\n }),\n [scopeEffects],\n );\n const target = useMemo(() => ({ resource, action: name }), [resource, name]);\n const flow = useFlowRun(target, { record, surface: 'page', thenEffects });\n // `then` is the page's to run, once it left the form (4.13).\n flow.holdThen(true);\n const { run, snapshot, compiled } = flow;\n const flowRef = useRef(flow);\n flowRef.current = flow;\n\n // The page leaves the form behind: after the form below it reset (a clean form asks the guard\n // nothing), replace the page by the destination, then run `then` over it.\n const [finished, setFinished] = useState(false);\n useEffect(() => {\n if (!finished) return;\n const current = flowRef.current;\n if (current.compiled?.flow.then?.redirect === undefined)\n navigateRef.current(recordHref, { replace: true });\n // After the route committed: an overlay opened by `then` belongs to the destination, and a\n // route change in flight would close it again.\n setTimeout(() => current.runThen(), 0);\n }, [finished, recordHref]);\n const binding = useMemo<FlowHostBinding>(\n () => ({\n kind: 'page',\n close: () => navigateRef.current(recordHref),\n finish: () => setFinished(true),\n }),\n [recordHref],\n );\n\n useEffect(() => {\n onMounted?.(true);\n return () => onMounted?.(false);\n }, [onMounted]);\n // Unsaved input or an unresolved attempt: what the gate must not unmount silently.\n useEffect(() => {\n if (run === null) return;\n let initial: string | undefined;\n const sync = () => {\n const now = run.getSnapshot();\n if (initial === undefined && now.history.includes('open'))\n initial = JSON.stringify(now.values);\n onDirty?.(\n now.outcome?.outcome !== 'success' &&\n (now.outcome?.outcome === 'unknown' ||\n (initial !== undefined && JSON.stringify(now.values) !== initial)),\n );\n };\n sync();\n return run.subscribe(sync);\n }, [run, onDirty]);\n // Discard: the input is dropped first, so the guard has nothing to ask, then the page leaves.\n const [discarded, setDiscarded] = useState(false);\n useEffect(() => {\n if (discarded) navigateRef.current(recordHref, { replace: true });\n }, [discarded, recordHref]);\n\n const documentTitle = `${chrome.labelText}${title === '' ? '' : ` · ${title}`}`;\n useEffect(() => {\n const previous = document.title;\n document.title = documentTitle;\n return () => {\n document.title = previous;\n };\n }, [documentTitle]);\n\n const opened =\n run !== null &&\n compiled !== null &&\n (snapshot.history.includes('open') || snapshot.step === 'outcome');\n const refused = snapshot.control === 'disabled' || snapshot.control === 'absent';\n return (\n <div\n data-testid={compiled === null ? undefined : tid.pageFlow(compiled.flowId)}\n className=\"flex min-h-0 flex-col gap-4\"\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <Link\n href={recordHref}\n className=\"ml-auto text-sm underline\"\n data-testid={`${tid.pageFlow(compiled?.flowId ?? name)}:back`}\n >\n {t('flow.back', { defaultValue: 'Back' })}\n </Link>\n </div>\n {stale ? (\n <div\n role=\"status\"\n data-buro-flow-state=\"stale\"\n className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\"\n >\n <span>\n {t('flow.noLongerApplies', { defaultValue: 'This action no longer applies' })}\n </span>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n flow.reset();\n setDiscarded(true);\n }}\n >\n {t('flow.discard', { defaultValue: 'Discard' })}\n </Button>\n </div>\n ) : null}\n {opened ? (\n <FlowHostBindingContext.Provider value={binding}>\n <fieldset disabled={stale} className=\"m-0 min-w-0 border-0 p-0\">\n <FlowScreen\n host=\"page\"\n run={run}\n compiled={compiled}\n label={chrome.labelText}\n {...(title === '' ? {} : { title })}\n record={record}\n />\n </fieldset>\n </FlowHostBindingContext.Provider>\n ) : refused ? (\n <p role=\"alert\" className=\"text-sm\">\n {snapshot.reason === undefined\n ? t('flow.unavailable', { defaultValue: 'Not allowed.' })\n : reasonText(caps, snapshot.reason, resource)}\n </p>\n ) : (\n <IconLoader2 className=\"h-5 w-5 animate-spin self-center\" aria-hidden=\"true\" />\n )}\n </div>\n );\n}\n"],"mappings":"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"}