@burojs/ui-shadcn 0.4.0 → 0.5.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.
- package/README.md +22 -5
- package/dist/bulk-summary-CwSd6Yjb.cjs +108 -0
- package/dist/bulk-summary-CwSd6Yjb.cjs.map +1 -0
- package/dist/bulk-summary-GxMDP4bw.js +108 -0
- package/dist/bulk-summary-GxMDP4bw.js.map +1 -0
- package/dist/button-BxnnE28H.cjs +55 -0
- package/dist/button-BxnnE28H.cjs.map +1 -0
- package/dist/button-MWVeZaqj.js +44 -0
- package/dist/button-MWVeZaqj.js.map +1 -0
- package/dist/candidates-gate-CX2PBu6l.js +63 -0
- package/dist/candidates-gate-CX2PBu6l.js.map +1 -0
- package/dist/candidates-gate-DiFI52r7.cjs +70 -0
- package/dist/candidates-gate-DiFI52r7.cjs.map +1 -0
- package/dist/candidates-relation-picker-CBsVcnPR.js +82 -0
- package/dist/candidates-relation-picker-CBsVcnPR.js.map +1 -0
- package/dist/candidates-relation-picker-Dd_GVLlu.cjs +84 -0
- package/dist/candidates-relation-picker-Dd_GVLlu.cjs.map +1 -0
- package/dist/check-confirm-dialog-BRByTFpg.cjs +1534 -0
- package/dist/check-confirm-dialog-BRByTFpg.cjs.map +1 -0
- package/dist/check-confirm-dialog-CkaVTPT9.js +1232 -0
- package/dist/check-confirm-dialog-CkaVTPT9.js.map +1 -0
- package/dist/dialog-B_rt9S1c.js +63 -0
- package/dist/dialog-B_rt9S1c.js.map +1 -0
- package/dist/dialog-DRNUq9EA.cjs +112 -0
- package/dist/dialog-DRNUq9EA.cjs.map +1 -0
- package/dist/filter-builder-B3mZFL-K.cjs +1761 -0
- package/dist/filter-builder-B3mZFL-K.cjs.map +1 -0
- package/dist/filter-builder-DOnaXNfP.js +1605 -0
- package/dist/filter-builder-DOnaXNfP.js.map +1 -0
- package/dist/flow-runtime-B6zOHYqI.cjs +255 -0
- package/dist/flow-runtime-B6zOHYqI.cjs.map +1 -0
- package/dist/flow-runtime-D8h99FSg.js +252 -0
- package/dist/flow-runtime-D8h99FSg.js.map +1 -0
- package/dist/flow-screen-CX05b3G9.cjs +705 -0
- package/dist/flow-screen-CX05b3G9.cjs.map +1 -0
- package/dist/flow-screen-CmbARSqg.js +701 -0
- package/dist/flow-screen-CmbARSqg.js.map +1 -0
- package/dist/index.cjs +2825 -11912
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1612 -1862
- package/dist/index.d.ts +1700 -1950
- package/dist/index.js +2498 -11605
- package/dist/index.js.map +1 -1
- package/dist/inline-flow-host-B7U1OJUe.cjs +112 -0
- package/dist/inline-flow-host-B7U1OJUe.cjs.map +1 -0
- package/dist/inline-flow-host-C3H2UpSA.js +112 -0
- package/dist/inline-flow-host-C3H2UpSA.js.map +1 -0
- package/dist/input-BI0czGyI.cjs +23 -0
- package/dist/input-BI0czGyI.cjs.map +1 -0
- package/dist/input-CUPStzZO.js +17 -0
- package/dist/input-CUPStzZO.js.map +1 -0
- package/dist/label-66W1qbke.js +29 -0
- package/dist/label-66W1qbke.js.map +1 -0
- package/dist/label-w6JYf2cF.cjs +36 -0
- package/dist/label-w6JYf2cF.cjs.map +1 -0
- package/dist/lazy-relation-picker-dialog-2oxnA6IR.js +14 -0
- package/dist/lazy-relation-picker-dialog-2oxnA6IR.js.map +1 -0
- package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs +19 -0
- package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs.map +1 -0
- package/dist/markdown/index.cjs +1 -1
- package/dist/markdown/index.js +1 -1
- package/dist/page-flow-route-CQ7HsbBm.cjs +277 -0
- package/dist/page-flow-route-CQ7HsbBm.cjs.map +1 -0
- package/dist/page-flow-route-CyXSvXSE.js +277 -0
- package/dist/page-flow-route-CyXSvXSE.js.map +1 -0
- package/dist/peek-filter-CnqHwTMU.cjs +51 -0
- package/dist/peek-filter-CnqHwTMU.cjs.map +1 -0
- package/dist/peek-filter-ErLfO153.js +28 -0
- package/dist/peek-filter-ErLfO153.js.map +1 -0
- package/dist/place-node--GdsP688.js +7315 -0
- package/dist/place-node--GdsP688.js.map +1 -0
- package/dist/place-node-SIp4XYQH.cjs +7833 -0
- package/dist/place-node-SIp4XYQH.cjs.map +1 -0
- package/dist/related-list-cards-B6x9HQQp.js +42 -0
- package/dist/related-list-cards-B6x9HQQp.js.map +1 -0
- package/dist/related-list-cards-nxuODlIA.cjs +42 -0
- package/dist/related-list-cards-nxuODlIA.cjs.map +1 -0
- package/dist/related-list-timeline-BYJKEbmA.cjs +142 -0
- package/dist/related-list-timeline-BYJKEbmA.cjs.map +1 -0
- package/dist/related-list-timeline-DEdfd-rr.js +142 -0
- package/dist/related-list-timeline-DEdfd-rr.js.map +1 -0
- package/dist/related-rows-B4levHcR.js +186 -0
- package/dist/related-rows-B4levHcR.js.map +1 -0
- package/dist/related-rows-zh5f_4QR.cjs +209 -0
- package/dist/related-rows-zh5f_4QR.cjs.map +1 -0
- package/dist/relation-create-BOaUe12G.cjs +362 -0
- package/dist/relation-create-BOaUe12G.cjs.map +1 -0
- package/dist/relation-create-Bg-eEJLs.js +337 -0
- package/dist/relation-create-Bg-eEJLs.js.map +1 -0
- package/dist/relation-many-picker-B-NCEUgX.cjs +465 -0
- package/dist/relation-many-picker-B-NCEUgX.cjs.map +1 -0
- package/dist/relation-many-picker-gdAOD-qK.js +464 -0
- package/dist/relation-many-picker-gdAOD-qK.js.map +1 -0
- package/dist/relation-picker-BAY77ivO.js +462 -0
- package/dist/relation-picker-BAY77ivO.js.map +1 -0
- package/dist/relation-picker-Da_uhFvA.cjs +479 -0
- package/dist/relation-picker-Da_uhFvA.cjs.map +1 -0
- package/dist/relation-picker-dialog-M0pQaWlp.cjs +300 -0
- package/dist/relation-picker-dialog-M0pQaWlp.cjs.map +1 -0
- package/dist/relation-picker-dialog-hA9rYrZB.js +299 -0
- package/dist/relation-picker-dialog-hA9rYrZB.js.map +1 -0
- package/dist/resource-page-router-OSQyRA5b.cjs +1779 -0
- package/dist/resource-page-router-OSQyRA5b.cjs.map +1 -0
- package/dist/resource-page-router-T4vS5gbJ.js +1582 -0
- package/dist/resource-page-router-T4vS5gbJ.js.map +1 -0
- package/dist/{rich-text-IDFvyvRE.js → rich-text-DFerywgz.js} +7 -4
- package/dist/{rich-text-IDFvyvRE.js.map → rich-text-DFerywgz.js.map} +1 -1
- package/dist/{rich-text-lndbYqzR.cjs → rich-text-vTyVjDoa.cjs} +25 -22
- package/dist/{rich-text-lndbYqzR.cjs.map → rich-text-vTyVjDoa.cjs.map} +1 -1
- package/dist/search-access-B7G5oR8n.cjs +812 -0
- package/dist/search-access-B7G5oR8n.cjs.map +1 -0
- package/dist/search-access-CURsWn-U.js +669 -0
- package/dist/search-access-CURsWn-U.js.map +1 -0
- package/dist/shell-BPlk8hEa.js +205 -0
- package/dist/shell-BPlk8hEa.js.map +1 -0
- package/dist/shell-DxhlsPwm.cjs +271 -0
- package/dist/shell-DxhlsPwm.cjs.map +1 -0
- package/dist/skeleton-U2noefj5.js +13 -0
- package/dist/skeleton-U2noefj5.js.map +1 -0
- package/dist/skeleton-_1gP7wan.cjs +18 -0
- package/dist/skeleton-_1gP7wan.cjs.map +1 -0
- package/dist/styles.css +241 -9
- package/dist/toaster-C7CiYA4Z.cjs +248 -0
- package/dist/toaster-C7CiYA4Z.cjs.map +1 -0
- package/dist/toaster-CfCRLgR-.js +164 -0
- package/dist/toaster-CfCRLgR-.js.map +1 -0
- package/dist/use-dialog-filter-state-BBCC0OEK.cjs +92 -0
- package/dist/use-dialog-filter-state-BBCC0OEK.cjs.map +1 -0
- package/dist/use-dialog-filter-state-BDcgS5Oz.js +87 -0
- package/dist/use-dialog-filter-state-BDcgS5Oz.js.map +1 -0
- package/dist/{use-theme-BwPWinB2.cjs → use-theme-DFlpXils.cjs} +12 -1
- package/dist/use-theme-DFlpXils.cjs.map +1 -0
- package/dist/{use-theme-IHmtrhEt.js → use-theme-dnE3Od3I.js} +7 -2
- package/dist/use-theme-dnE3Od3I.js.map +1 -0
- package/package.json +16 -14
- package/dist/use-theme-BwPWinB2.cjs.map +0 -1
- package/dist/use-theme-IHmtrhEt.js.map +0 -1
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
const require_toaster = require("./toaster-C7CiYA4Z.cjs");
|
|
2
|
+
const require_check_confirm_dialog = require("./check-confirm-dialog-BRByTFpg.cjs");
|
|
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");
|
|
7
|
+
let _burojs_core = require("@burojs/core");
|
|
8
|
+
let _burojs_react = require("@burojs/react");
|
|
9
|
+
let _tabler_icons_react = require("@tabler/icons-react");
|
|
10
|
+
let react = require("react");
|
|
11
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
12
|
+
let _burojs_react_flows = require("@burojs/react/flows");
|
|
13
|
+
//#region src/flows/page-flow-route.tsx
|
|
14
|
+
function PageFlowRoute({ match }) {
|
|
15
|
+
const app = (0, _burojs_react.useApp)();
|
|
16
|
+
const snapshot = (0, _burojs_react.useApplicationSnapshot)();
|
|
17
|
+
const permissions = (0, _burojs_react.usePermissions)();
|
|
18
|
+
const resource = require_resource_page_router.canonicalNavKey(match);
|
|
19
|
+
const id = match.id ?? "";
|
|
20
|
+
const name = match.flow ?? "";
|
|
21
|
+
if (permissions?.status === "loading") return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "loading" });
|
|
22
|
+
const flow = (0, _burojs_core.effectiveFlow)(snapshot, match.ref, name);
|
|
23
|
+
const hasRecordPage = require_place_node.recordRouteAvailable(app.registry, resource, "show") || require_place_node.recordRouteAvailable(app.registry, resource, "edit");
|
|
24
|
+
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
|
+
return (0, react_jsx_runtime.jsx)(PageFlowRecord, {
|
|
27
|
+
resource,
|
|
28
|
+
id,
|
|
29
|
+
name
|
|
30
|
+
}, `${resource}:${id}:${name}`);
|
|
31
|
+
}
|
|
32
|
+
function PageFlowRecord(props) {
|
|
33
|
+
const { resource, id } = props;
|
|
34
|
+
const def = (0, _burojs_react.useApp)().registry.get(resource);
|
|
35
|
+
const projection = (0, react.useMemo)(() => def === void 0 ? void 0 : (0, _burojs_core.dtoFields)(def, "record"), [def]);
|
|
36
|
+
const query = (0, _burojs_react.useOne)(resource, {
|
|
37
|
+
id,
|
|
38
|
+
retry: _burojs_react.retryRecordRead,
|
|
39
|
+
...projection === void 0 ? {} : { projection }
|
|
40
|
+
});
|
|
41
|
+
const result = query.data;
|
|
42
|
+
if (result !== void 0) {
|
|
43
|
+
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" });
|
|
45
|
+
return (0, react_jsx_runtime.jsx)(PageFlowGate, {
|
|
46
|
+
...props,
|
|
47
|
+
record
|
|
48
|
+
});
|
|
49
|
+
}
|
|
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" });
|
|
58
|
+
}
|
|
59
|
+
function PageFlowGate(props) {
|
|
60
|
+
const { resource, name, record } = props;
|
|
61
|
+
const control = (0, _burojs_react.useFlowRowResolver)(resource)(name, record);
|
|
62
|
+
const dirty = (0, react.useRef)(false);
|
|
63
|
+
const running = (0, react.useRef)(false);
|
|
64
|
+
const keep = (control.state === "absent" && control.cause === "when" || control.state === "disabled") && running.current && dirty.current;
|
|
65
|
+
const track = (value) => {
|
|
66
|
+
dirty.current = value;
|
|
67
|
+
};
|
|
68
|
+
if (keep) return (0, react_jsx_runtime.jsx)(PageFlowRun, {
|
|
69
|
+
...props,
|
|
70
|
+
stale: true,
|
|
71
|
+
onDirty: track,
|
|
72
|
+
onMounted: (on) => running.current = on
|
|
73
|
+
});
|
|
74
|
+
switch (control.state) {
|
|
75
|
+
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" });
|
|
78
|
+
case "disabled": return (0, react_jsx_runtime.jsx)(PageFlowUnavailable, {
|
|
79
|
+
...props,
|
|
80
|
+
...control.reason === void 0 ? {} : { reason: control.reason }
|
|
81
|
+
});
|
|
82
|
+
default: return (0, react_jsx_runtime.jsx)(PageFlowRun, {
|
|
83
|
+
...props,
|
|
84
|
+
record: control.record ?? record,
|
|
85
|
+
onDirty: track,
|
|
86
|
+
onMounted: (on) => running.current = on
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function usePageFlowFrame(props) {
|
|
91
|
+
const { resource, id, name, record } = props;
|
|
92
|
+
const app = (0, _burojs_react.useApp)();
|
|
93
|
+
const chrome = require_check_confirm_dialog.useFlowChrome(resource, name);
|
|
94
|
+
const title = (0, _burojs_react.useProjectedTitle)(resource, id, record);
|
|
95
|
+
const showable = require_place_node.recordRouteAvailable(app.registry, resource, "show");
|
|
96
|
+
const recordHref = (0, _burojs_core.hrefFor)(app.registry, resource, {
|
|
97
|
+
action: showable ? "show" : "edit",
|
|
98
|
+
id
|
|
99
|
+
});
|
|
100
|
+
require_resource_page_router.usePublishRecordTrail({
|
|
101
|
+
key: require_resource_page_router.recordTrailKey(resource, id, `flow:${name}`),
|
|
102
|
+
title,
|
|
103
|
+
flowLabel: chrome.labelText
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
chrome,
|
|
107
|
+
title,
|
|
108
|
+
recordHref
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function PageFlowUnavailable(props) {
|
|
112
|
+
const t = (0, _burojs_react.useOptionalT)();
|
|
113
|
+
const tid = (0, _burojs_react.useTestIds)();
|
|
114
|
+
const { recordHref } = usePageFlowFrame(props);
|
|
115
|
+
const Link = require_toaster.useLinkComponent();
|
|
116
|
+
const flowId = usePageFlowId(props.resource, props.name);
|
|
117
|
+
return (0, react_jsx_runtime.jsx)("div", { children: (0, react_jsx_runtime.jsxs)("div", {
|
|
118
|
+
role: "status",
|
|
119
|
+
"data-testid": `${tid.pageFlow(flowId)}:unavailable`,
|
|
120
|
+
className: "flex flex-col gap-2 px-1 py-6 text-sm",
|
|
121
|
+
children: [
|
|
122
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
123
|
+
className: "font-medium",
|
|
124
|
+
children: require_check_confirm_dialog.text(t, "flow.notAvailable", "This action is not available")
|
|
125
|
+
}),
|
|
126
|
+
props.reason === void 0 ? null : (0, react_jsx_runtime.jsx)("p", {
|
|
127
|
+
className: "text-muted-foreground",
|
|
128
|
+
children: props.reason
|
|
129
|
+
}),
|
|
130
|
+
(0, react_jsx_runtime.jsx)(Link, {
|
|
131
|
+
href: recordHref,
|
|
132
|
+
className: "underline",
|
|
133
|
+
children: require_check_confirm_dialog.text(t, "flow.back", "Back")
|
|
134
|
+
})
|
|
135
|
+
]
|
|
136
|
+
}) });
|
|
137
|
+
}
|
|
138
|
+
function usePageFlowId(resource, name) {
|
|
139
|
+
const def = (0, _burojs_react.useApp)().registry.get(resource);
|
|
140
|
+
return (0, react.useMemo)(() => {
|
|
141
|
+
try {
|
|
142
|
+
return def === void 0 ? `${resource}/actions/${name}` : (0, _burojs_core.compileFlow)(def, name).flowId;
|
|
143
|
+
} catch {
|
|
144
|
+
return `${resource}/actions/${name}`;
|
|
145
|
+
}
|
|
146
|
+
}, [
|
|
147
|
+
def,
|
|
148
|
+
resource,
|
|
149
|
+
name
|
|
150
|
+
]);
|
|
151
|
+
}
|
|
152
|
+
function PageFlowRun(props) {
|
|
153
|
+
const { resource, name, stale = false, onDirty, onMounted } = props;
|
|
154
|
+
const [record] = (0, react.useState)(props.record);
|
|
155
|
+
const t = (0, _burojs_react.useOptionalT)();
|
|
156
|
+
const tid = (0, _burojs_react.useTestIds)();
|
|
157
|
+
const navigate = (0, _burojs_react.useRouteNavigate)();
|
|
158
|
+
const Link = require_toaster.useLinkComponent();
|
|
159
|
+
const scopeEffects = (0, _burojs_react.useDispatcherEffects)();
|
|
160
|
+
const { chrome, title, recordHref } = usePageFlowFrame(props);
|
|
161
|
+
const navigateRef = (0, react.useRef)(navigate);
|
|
162
|
+
navigateRef.current = navigate;
|
|
163
|
+
const thenEffects = (0, react.useCallback)(() => ({
|
|
164
|
+
...scopeEffects(),
|
|
165
|
+
redirect: (to) => navigateRef.current(to, { replace: true })
|
|
166
|
+
}), [scopeEffects]);
|
|
167
|
+
const target = (0, react.useMemo)(() => ({
|
|
168
|
+
resource,
|
|
169
|
+
action: name
|
|
170
|
+
}), [resource, name]);
|
|
171
|
+
const flow = (0, _burojs_react_flows.useFlowRun)(target, {
|
|
172
|
+
record,
|
|
173
|
+
surface: "page",
|
|
174
|
+
thenEffects
|
|
175
|
+
});
|
|
176
|
+
flow.holdThen(true);
|
|
177
|
+
const { run, snapshot, compiled } = flow;
|
|
178
|
+
const flowRef = (0, react.useRef)(flow);
|
|
179
|
+
flowRef.current = flow;
|
|
180
|
+
const [finished, setFinished] = (0, react.useState)(false);
|
|
181
|
+
(0, react.useEffect)(() => {
|
|
182
|
+
if (!finished) return;
|
|
183
|
+
const current = flowRef.current;
|
|
184
|
+
if (current.compiled?.flow.then?.redirect === void 0) navigateRef.current(recordHref, { replace: true });
|
|
185
|
+
setTimeout(() => current.runThen(), 0);
|
|
186
|
+
}, [finished, recordHref]);
|
|
187
|
+
const binding = (0, react.useMemo)(() => ({
|
|
188
|
+
kind: "page",
|
|
189
|
+
close: () => navigateRef.current(recordHref),
|
|
190
|
+
finish: () => setFinished(true)
|
|
191
|
+
}), [recordHref]);
|
|
192
|
+
(0, react.useEffect)(() => {
|
|
193
|
+
onMounted?.(true);
|
|
194
|
+
return () => onMounted?.(false);
|
|
195
|
+
}, [onMounted]);
|
|
196
|
+
(0, react.useEffect)(() => {
|
|
197
|
+
if (run === null) return;
|
|
198
|
+
let initial;
|
|
199
|
+
const sync = () => {
|
|
200
|
+
const now = run.getSnapshot();
|
|
201
|
+
if (initial === void 0 && now.history.includes("open")) initial = JSON.stringify(now.values);
|
|
202
|
+
onDirty?.(now.outcome?.outcome !== "success" && (now.outcome?.outcome === "unknown" || initial !== void 0 && JSON.stringify(now.values) !== initial));
|
|
203
|
+
};
|
|
204
|
+
sync();
|
|
205
|
+
return run.subscribe(sync);
|
|
206
|
+
}, [run, onDirty]);
|
|
207
|
+
const [discarded, setDiscarded] = (0, react.useState)(false);
|
|
208
|
+
(0, react.useEffect)(() => {
|
|
209
|
+
if (discarded) navigateRef.current(recordHref, { replace: true });
|
|
210
|
+
}, [discarded, recordHref]);
|
|
211
|
+
const documentTitle = `${chrome.labelText}${title === "" ? "" : ` · ${title}`}`;
|
|
212
|
+
(0, react.useEffect)(() => {
|
|
213
|
+
const previous = document.title;
|
|
214
|
+
document.title = documentTitle;
|
|
215
|
+
return () => {
|
|
216
|
+
document.title = previous;
|
|
217
|
+
};
|
|
218
|
+
}, [documentTitle]);
|
|
219
|
+
const opened = run !== null && compiled !== null && (snapshot.history.includes("open") || snapshot.step === "outcome");
|
|
220
|
+
const refused = snapshot.control === "disabled" || snapshot.control === "absent";
|
|
221
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
222
|
+
"data-testid": compiled === null ? void 0 : tid.pageFlow(compiled.flowId),
|
|
223
|
+
className: "flex min-h-0 flex-col gap-4",
|
|
224
|
+
children: [
|
|
225
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
226
|
+
className: "flex flex-wrap items-center gap-2",
|
|
227
|
+
children: (0, react_jsx_runtime.jsx)(Link, {
|
|
228
|
+
href: recordHref,
|
|
229
|
+
className: "ml-auto text-sm underline",
|
|
230
|
+
"data-testid": `${tid.pageFlow(compiled?.flowId ?? name)}:back`,
|
|
231
|
+
children: require_check_confirm_dialog.text(t, "flow.back", "Back")
|
|
232
|
+
})
|
|
233
|
+
}),
|
|
234
|
+
stale ? (0, react_jsx_runtime.jsxs)("div", {
|
|
235
|
+
role: "status",
|
|
236
|
+
"data-buro-flow-state": "stale",
|
|
237
|
+
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, {
|
|
239
|
+
type: "button",
|
|
240
|
+
size: "sm",
|
|
241
|
+
variant: "outline",
|
|
242
|
+
onClick: () => {
|
|
243
|
+
flow.reset();
|
|
244
|
+
setDiscarded(true);
|
|
245
|
+
},
|
|
246
|
+
children: require_check_confirm_dialog.text(t, "flow.discard", "Discard")
|
|
247
|
+
})]
|
|
248
|
+
}) : null,
|
|
249
|
+
opened ? (0, react_jsx_runtime.jsx)(require_flow_screen.FlowHostBindingContext.Provider, {
|
|
250
|
+
value: binding,
|
|
251
|
+
children: (0, react_jsx_runtime.jsx)("fieldset", {
|
|
252
|
+
disabled: stale,
|
|
253
|
+
className: "m-0 min-w-0 border-0 p-0",
|
|
254
|
+
children: (0, react_jsx_runtime.jsx)(require_flow_screen.FlowScreen, {
|
|
255
|
+
host: "page",
|
|
256
|
+
run,
|
|
257
|
+
compiled,
|
|
258
|
+
label: chrome.labelText,
|
|
259
|
+
...title === "" ? {} : { title },
|
|
260
|
+
record
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
}) : refused ? (0, react_jsx_runtime.jsx)("p", {
|
|
264
|
+
role: "alert",
|
|
265
|
+
className: "text-sm",
|
|
266
|
+
children: snapshot.reason ?? require_check_confirm_dialog.text(t, "flow.unavailable", "Not allowed.")
|
|
267
|
+
}) : (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconLoader2, {
|
|
268
|
+
className: "h-5 w-5 animate-spin self-center",
|
|
269
|
+
"aria-hidden": "true"
|
|
270
|
+
})
|
|
271
|
+
]
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
//#endregion
|
|
275
|
+
exports.PageFlowRoute = PageFlowRoute;
|
|
276
|
+
|
|
277
|
+
//# sourceMappingURL=page-flow-route-CQ7HsbBm.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-flow-route-CQ7HsbBm.cjs","names":["useApp","useApplicationSnapshot","usePermissions","canonicalNavKey","RecordState","effectiveFlow","recordRouteAvailable","NavAreaNotFound","useMemo","dtoFields","useOne","retryRecordRead","recordErrorState","useFlowRowResolver","useRef","useFlowChrome","useProjectedTitle","hrefFor","recordTrailKey","useOptionalT","useTestIds","useLinkComponent","text","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 { type RouteMatch, compileFlow, dtoFields, effectiveFlow, hrefFor } from '@burojs/core';\nimport {\n retryRecordRead,\n useApp,\n useApplicationSnapshot,\n useDispatcherEffects,\n useFlowRowResolver,\n useOne,\n useOptionalT,\n usePermissions,\n useProjectedTitle,\n useRouteNavigate,\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 { recordErrorState } from '../layout/record-page.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 { text, 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 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,\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 <PageFlowGate {...props} record={record as Row} />;\n }\n if (query.error != null) {\n const state = recordErrorState(query.error);\n return (\n <RecordState\n state={state}\n onRetry={state === 'error' ? () => void query.refetch() : undefined}\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 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 {text(t, 'flow.notAvailable', 'This action is not available')}\n </p>\n {props.reason === undefined ? null : (\n <p className=\"text-muted-foreground\">{props.reason}</p>\n )}\n <Link href={recordHref} className=\"underline\">\n {text(t, 'flow.back', '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 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 {text(t, 'flow.back', '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>{text(t, 'flow.noLongerApplies', 'This action no longer applies')}</span>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n flow.reset();\n setDiscarded(true);\n }}\n >\n {text(t, 'flow.discard', '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 ?? text(t, 'flow.unavailable', 'Not allowed.')}\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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"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { d as useLinkComponent } from "./toaster-CfCRLgR-.js";
|
|
2
|
+
import { h as useFlowChrome, m as text } from "./check-confirm-dialog-CkaVTPT9.js";
|
|
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";
|
|
7
|
+
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 { IconLoader2 } from "@tabler/icons-react";
|
|
10
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { useFlowRun } from "@burojs/react/flows";
|
|
13
|
+
//#region src/flows/page-flow-route.tsx
|
|
14
|
+
function PageFlowRoute({ match }) {
|
|
15
|
+
const app = useApp();
|
|
16
|
+
const snapshot = useApplicationSnapshot();
|
|
17
|
+
const permissions = usePermissions();
|
|
18
|
+
const resource = canonicalNavKey(match);
|
|
19
|
+
const id = match.id ?? "";
|
|
20
|
+
const name = match.flow ?? "";
|
|
21
|
+
if (permissions?.status === "loading") return jsx(RecordState, { state: "loading" });
|
|
22
|
+
const flow = effectiveFlow(snapshot, match.ref, name);
|
|
23
|
+
const hasRecordPage = recordRouteAvailable(app.registry, resource, "show") || recordRouteAvailable(app.registry, resource, "edit");
|
|
24
|
+
if (flow === void 0) return jsx(NavAreaNotFound, {});
|
|
25
|
+
if (!hasRecordPage || flow.host !== "page" || id === "") return jsx(RecordState, { state: "not-found" });
|
|
26
|
+
return jsx(PageFlowRecord, {
|
|
27
|
+
resource,
|
|
28
|
+
id,
|
|
29
|
+
name
|
|
30
|
+
}, `${resource}:${id}:${name}`);
|
|
31
|
+
}
|
|
32
|
+
function PageFlowRecord(props) {
|
|
33
|
+
const { resource, id } = props;
|
|
34
|
+
const def = useApp().registry.get(resource);
|
|
35
|
+
const projection = useMemo(() => def === void 0 ? void 0 : dtoFields(def, "record"), [def]);
|
|
36
|
+
const query = useOne(resource, {
|
|
37
|
+
id,
|
|
38
|
+
retry: retryRecordRead,
|
|
39
|
+
...projection === void 0 ? {} : { projection }
|
|
40
|
+
});
|
|
41
|
+
const result = query.data;
|
|
42
|
+
if (result !== void 0) {
|
|
43
|
+
const record = result.data;
|
|
44
|
+
if (record === null || typeof record !== "object") return jsx(RecordState, { state: "not-found" });
|
|
45
|
+
return jsx(PageFlowGate, {
|
|
46
|
+
...props,
|
|
47
|
+
record
|
|
48
|
+
});
|
|
49
|
+
}
|
|
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
|
+
}
|
|
57
|
+
return jsx(RecordState, { state: "loading" });
|
|
58
|
+
}
|
|
59
|
+
function PageFlowGate(props) {
|
|
60
|
+
const { resource, name, record } = props;
|
|
61
|
+
const control = useFlowRowResolver(resource)(name, record);
|
|
62
|
+
const dirty = useRef(false);
|
|
63
|
+
const running = useRef(false);
|
|
64
|
+
const keep = (control.state === "absent" && control.cause === "when" || control.state === "disabled") && running.current && dirty.current;
|
|
65
|
+
const track = (value) => {
|
|
66
|
+
dirty.current = value;
|
|
67
|
+
};
|
|
68
|
+
if (keep) return jsx(PageFlowRun, {
|
|
69
|
+
...props,
|
|
70
|
+
stale: true,
|
|
71
|
+
onDirty: track,
|
|
72
|
+
onMounted: (on) => running.current = on
|
|
73
|
+
});
|
|
74
|
+
switch (control.state) {
|
|
75
|
+
case "undecided":
|
|
76
|
+
case "pending": return jsx(RecordState, { state: "loading" });
|
|
77
|
+
case "absent": return control.cause === "when" ? jsx(PageFlowUnavailable, { ...props }) : jsx(RecordState, { state: "not-found" });
|
|
78
|
+
case "disabled": return jsx(PageFlowUnavailable, {
|
|
79
|
+
...props,
|
|
80
|
+
...control.reason === void 0 ? {} : { reason: control.reason }
|
|
81
|
+
});
|
|
82
|
+
default: return jsx(PageFlowRun, {
|
|
83
|
+
...props,
|
|
84
|
+
record: control.record ?? record,
|
|
85
|
+
onDirty: track,
|
|
86
|
+
onMounted: (on) => running.current = on
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function usePageFlowFrame(props) {
|
|
91
|
+
const { resource, id, name, record } = props;
|
|
92
|
+
const app = useApp();
|
|
93
|
+
const chrome = useFlowChrome(resource, name);
|
|
94
|
+
const title = useProjectedTitle(resource, id, record);
|
|
95
|
+
const showable = recordRouteAvailable(app.registry, resource, "show");
|
|
96
|
+
const recordHref = hrefFor(app.registry, resource, {
|
|
97
|
+
action: showable ? "show" : "edit",
|
|
98
|
+
id
|
|
99
|
+
});
|
|
100
|
+
usePublishRecordTrail({
|
|
101
|
+
key: recordTrailKey(resource, id, `flow:${name}`),
|
|
102
|
+
title,
|
|
103
|
+
flowLabel: chrome.labelText
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
chrome,
|
|
107
|
+
title,
|
|
108
|
+
recordHref
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function PageFlowUnavailable(props) {
|
|
112
|
+
const t = useOptionalT();
|
|
113
|
+
const tid = useTestIds();
|
|
114
|
+
const { recordHref } = usePageFlowFrame(props);
|
|
115
|
+
const Link = useLinkComponent();
|
|
116
|
+
const flowId = usePageFlowId(props.resource, props.name);
|
|
117
|
+
return jsx("div", { children: jsxs("div", {
|
|
118
|
+
role: "status",
|
|
119
|
+
"data-testid": `${tid.pageFlow(flowId)}:unavailable`,
|
|
120
|
+
className: "flex flex-col gap-2 px-1 py-6 text-sm",
|
|
121
|
+
children: [
|
|
122
|
+
jsx("p", {
|
|
123
|
+
className: "font-medium",
|
|
124
|
+
children: text(t, "flow.notAvailable", "This action is not available")
|
|
125
|
+
}),
|
|
126
|
+
props.reason === void 0 ? null : jsx("p", {
|
|
127
|
+
className: "text-muted-foreground",
|
|
128
|
+
children: props.reason
|
|
129
|
+
}),
|
|
130
|
+
jsx(Link, {
|
|
131
|
+
href: recordHref,
|
|
132
|
+
className: "underline",
|
|
133
|
+
children: text(t, "flow.back", "Back")
|
|
134
|
+
})
|
|
135
|
+
]
|
|
136
|
+
}) });
|
|
137
|
+
}
|
|
138
|
+
function usePageFlowId(resource, name) {
|
|
139
|
+
const def = useApp().registry.get(resource);
|
|
140
|
+
return useMemo(() => {
|
|
141
|
+
try {
|
|
142
|
+
return def === void 0 ? `${resource}/actions/${name}` : compileFlow(def, name).flowId;
|
|
143
|
+
} catch {
|
|
144
|
+
return `${resource}/actions/${name}`;
|
|
145
|
+
}
|
|
146
|
+
}, [
|
|
147
|
+
def,
|
|
148
|
+
resource,
|
|
149
|
+
name
|
|
150
|
+
]);
|
|
151
|
+
}
|
|
152
|
+
function PageFlowRun(props) {
|
|
153
|
+
const { resource, name, stale = false, onDirty, onMounted } = props;
|
|
154
|
+
const [record] = useState(props.record);
|
|
155
|
+
const t = useOptionalT();
|
|
156
|
+
const tid = useTestIds();
|
|
157
|
+
const navigate = useRouteNavigate();
|
|
158
|
+
const Link = useLinkComponent();
|
|
159
|
+
const scopeEffects = useDispatcherEffects();
|
|
160
|
+
const { chrome, title, recordHref } = usePageFlowFrame(props);
|
|
161
|
+
const navigateRef = useRef(navigate);
|
|
162
|
+
navigateRef.current = navigate;
|
|
163
|
+
const thenEffects = useCallback(() => ({
|
|
164
|
+
...scopeEffects(),
|
|
165
|
+
redirect: (to) => navigateRef.current(to, { replace: true })
|
|
166
|
+
}), [scopeEffects]);
|
|
167
|
+
const target = useMemo(() => ({
|
|
168
|
+
resource,
|
|
169
|
+
action: name
|
|
170
|
+
}), [resource, name]);
|
|
171
|
+
const flow = useFlowRun(target, {
|
|
172
|
+
record,
|
|
173
|
+
surface: "page",
|
|
174
|
+
thenEffects
|
|
175
|
+
});
|
|
176
|
+
flow.holdThen(true);
|
|
177
|
+
const { run, snapshot, compiled } = flow;
|
|
178
|
+
const flowRef = useRef(flow);
|
|
179
|
+
flowRef.current = flow;
|
|
180
|
+
const [finished, setFinished] = useState(false);
|
|
181
|
+
useEffect(() => {
|
|
182
|
+
if (!finished) return;
|
|
183
|
+
const current = flowRef.current;
|
|
184
|
+
if (current.compiled?.flow.then?.redirect === void 0) navigateRef.current(recordHref, { replace: true });
|
|
185
|
+
setTimeout(() => current.runThen(), 0);
|
|
186
|
+
}, [finished, recordHref]);
|
|
187
|
+
const binding = useMemo(() => ({
|
|
188
|
+
kind: "page",
|
|
189
|
+
close: () => navigateRef.current(recordHref),
|
|
190
|
+
finish: () => setFinished(true)
|
|
191
|
+
}), [recordHref]);
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
onMounted?.(true);
|
|
194
|
+
return () => onMounted?.(false);
|
|
195
|
+
}, [onMounted]);
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
if (run === null) return;
|
|
198
|
+
let initial;
|
|
199
|
+
const sync = () => {
|
|
200
|
+
const now = run.getSnapshot();
|
|
201
|
+
if (initial === void 0 && now.history.includes("open")) initial = JSON.stringify(now.values);
|
|
202
|
+
onDirty?.(now.outcome?.outcome !== "success" && (now.outcome?.outcome === "unknown" || initial !== void 0 && JSON.stringify(now.values) !== initial));
|
|
203
|
+
};
|
|
204
|
+
sync();
|
|
205
|
+
return run.subscribe(sync);
|
|
206
|
+
}, [run, onDirty]);
|
|
207
|
+
const [discarded, setDiscarded] = useState(false);
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
if (discarded) navigateRef.current(recordHref, { replace: true });
|
|
210
|
+
}, [discarded, recordHref]);
|
|
211
|
+
const documentTitle = `${chrome.labelText}${title === "" ? "" : ` · ${title}`}`;
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
const previous = document.title;
|
|
214
|
+
document.title = documentTitle;
|
|
215
|
+
return () => {
|
|
216
|
+
document.title = previous;
|
|
217
|
+
};
|
|
218
|
+
}, [documentTitle]);
|
|
219
|
+
const opened = run !== null && compiled !== null && (snapshot.history.includes("open") || snapshot.step === "outcome");
|
|
220
|
+
const refused = snapshot.control === "disabled" || snapshot.control === "absent";
|
|
221
|
+
return jsxs("div", {
|
|
222
|
+
"data-testid": compiled === null ? void 0 : tid.pageFlow(compiled.flowId),
|
|
223
|
+
className: "flex min-h-0 flex-col gap-4",
|
|
224
|
+
children: [
|
|
225
|
+
jsx("div", {
|
|
226
|
+
className: "flex flex-wrap items-center gap-2",
|
|
227
|
+
children: jsx(Link, {
|
|
228
|
+
href: recordHref,
|
|
229
|
+
className: "ml-auto text-sm underline",
|
|
230
|
+
"data-testid": `${tid.pageFlow(compiled?.flowId ?? name)}:back`,
|
|
231
|
+
children: text(t, "flow.back", "Back")
|
|
232
|
+
})
|
|
233
|
+
}),
|
|
234
|
+
stale ? jsxs("div", {
|
|
235
|
+
role: "status",
|
|
236
|
+
"data-buro-flow-state": "stale",
|
|
237
|
+
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, {
|
|
239
|
+
type: "button",
|
|
240
|
+
size: "sm",
|
|
241
|
+
variant: "outline",
|
|
242
|
+
onClick: () => {
|
|
243
|
+
flow.reset();
|
|
244
|
+
setDiscarded(true);
|
|
245
|
+
},
|
|
246
|
+
children: text(t, "flow.discard", "Discard")
|
|
247
|
+
})]
|
|
248
|
+
}) : null,
|
|
249
|
+
opened ? jsx(FlowHostBindingContext.Provider, {
|
|
250
|
+
value: binding,
|
|
251
|
+
children: jsx("fieldset", {
|
|
252
|
+
disabled: stale,
|
|
253
|
+
className: "m-0 min-w-0 border-0 p-0",
|
|
254
|
+
children: jsx(FlowScreen, {
|
|
255
|
+
host: "page",
|
|
256
|
+
run,
|
|
257
|
+
compiled,
|
|
258
|
+
label: chrome.labelText,
|
|
259
|
+
...title === "" ? {} : { title },
|
|
260
|
+
record
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
}) : refused ? jsx("p", {
|
|
264
|
+
role: "alert",
|
|
265
|
+
className: "text-sm",
|
|
266
|
+
children: snapshot.reason ?? text(t, "flow.unavailable", "Not allowed.")
|
|
267
|
+
}) : jsx(IconLoader2, {
|
|
268
|
+
className: "h-5 w-5 animate-spin self-center",
|
|
269
|
+
"aria-hidden": "true"
|
|
270
|
+
})
|
|
271
|
+
]
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
//#endregion
|
|
275
|
+
export { PageFlowRoute };
|
|
276
|
+
|
|
277
|
+
//# sourceMappingURL=page-flow-route-CyXSvXSE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-flow-route-CyXSvXSE.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 { type RouteMatch, compileFlow, dtoFields, effectiveFlow, hrefFor } from '@burojs/core';\nimport {\n retryRecordRead,\n useApp,\n useApplicationSnapshot,\n useDispatcherEffects,\n useFlowRowResolver,\n useOne,\n useOptionalT,\n usePermissions,\n useProjectedTitle,\n useRouteNavigate,\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 { recordErrorState } from '../layout/record-page.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 { text, 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 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,\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 <PageFlowGate {...props} record={record as Row} />;\n }\n if (query.error != null) {\n const state = recordErrorState(query.error);\n return (\n <RecordState\n state={state}\n onRetry={state === 'error' ? () => void query.refetch() : undefined}\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 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 {text(t, 'flow.notAvailable', 'This action is not available')}\n </p>\n {props.reason === undefined ? null : (\n <p className=\"text-muted-foreground\">{props.reason}</p>\n )}\n <Link href={recordHref} className=\"underline\">\n {text(t, 'flow.back', '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 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 {text(t, 'flow.back', '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>{text(t, 'flow.noLongerApplies', 'This action no longer applies')}</span>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n flow.reset();\n setDiscarded(true);\n }}\n >\n {text(t, 'flow.discard', '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 ?? text(t, 'flow.unavailable', 'Not allowed.')}\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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"}
|