@byline/admin 3.12.1 → 3.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/forms/form-context.js +30 -60
- package/dist/forms/form-modals.d.ts +30 -0
- package/dist/forms/form-modals.js +189 -0
- package/dist/forms/form-renderer.js +22 -363
- package/dist/forms/form-status-display.d.ts +21 -0
- package/dist/forms/form-status-display.js +94 -0
- package/dist/forms/status-transitions.d.ts +23 -0
- package/dist/forms/status-transitions.js +37 -0
- package/dist/forms/status-transitions.test.node.d.ts +8 -0
- package/dist/forms/use-form-layout.d.ts +35 -0
- package/dist/forms/use-form-layout.js +77 -0
- package/dist/forms/use-form-layout.test.node.d.ts +8 -0
- package/dist/forms/use-tracked-slot.d.ts +45 -0
- package/dist/forms/use-tracked-slot.js +46 -0
- package/package.json +5 -5
- package/src/forms/form-context.tsx +36 -92
- package/src/forms/form-modals.tsx +186 -0
- package/src/forms/form-renderer.tsx +24 -387
- package/src/forms/form-status-display.tsx +108 -0
- package/src/forms/status-transitions.test.node.ts +87 -0
- package/src/forms/status-transitions.ts +86 -0
- package/src/forms/use-form-layout.test.node.ts +82 -0
- package/src/forms/use-form-layout.ts +134 -0
- package/src/forms/use-tracked-slot.ts +101 -0
|
@@ -1,145 +1,25 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { useTranslation } from "@byline/i18n/react";
|
|
5
|
-
import { Alert, Button,
|
|
5
|
+
import { Alert, Button, ComboButton } from "@byline/ui/react";
|
|
6
6
|
import classnames from "classnames";
|
|
7
7
|
import { FieldRenderer } from "../fields/field-renderer.js";
|
|
8
8
|
import { useBylineFieldServices } from "../fields/field-services-context.js";
|
|
9
|
-
import { LocalDateTime } from "../fields/local-date-time.js";
|
|
10
9
|
import { AdminGroup } from "../presentation/group.js";
|
|
11
10
|
import { AdminRow } from "../presentation/row.js";
|
|
12
11
|
import { AdminTabs } from "../presentation/tabs.js";
|
|
13
12
|
import { AvailableLocalesWidget } from "./available-locales-widget.js";
|
|
14
13
|
import { DocumentActions } from "./document-actions.js";
|
|
15
14
|
import { FormProvider, useFieldValue, useFormContext } from "./form-context.js";
|
|
15
|
+
import { NavigationGuardModal, SystemFieldsConfirmModal, UnsavedChangesModal } from "./form-modals.js";
|
|
16
16
|
import form_renderer_module from "./form-renderer.module.js";
|
|
17
|
+
import { FormStatusDisplay } from "./form-status-display.js";
|
|
17
18
|
import { useNavigationGuardAdapter } from "./navigation-guard.js";
|
|
18
19
|
import { PathWidget } from "./path-widget.js";
|
|
20
|
+
import { computeStatusTransitions } from "./status-transitions.js";
|
|
19
21
|
import { executeUploadsWithProgress } from "./upload-executor.js";
|
|
20
|
-
|
|
21
|
-
const { t } = useTranslation('byline-admin');
|
|
22
|
-
const statusCode = initialData?.status;
|
|
23
|
-
const statusLabel = workflowStatuses?.find((s)=>s.name === statusCode)?.label ?? statusCode;
|
|
24
|
-
const showStatusCell = (workflowStatuses?.length ?? 0) > 1;
|
|
25
|
-
return /*#__PURE__*/ jsxs("div", {
|
|
26
|
-
className: classnames('byline-form-status', form_renderer_module.status),
|
|
27
|
-
children: [
|
|
28
|
-
/*#__PURE__*/ jsxs("div", {
|
|
29
|
-
className: classnames('byline-form-status-meta', form_renderer_module["status-meta"]),
|
|
30
|
-
children: [
|
|
31
|
-
showStatusCell && /*#__PURE__*/ jsxs("div", {
|
|
32
|
-
className: classnames('byline-form-status-cell', form_renderer_module["status-cell"]),
|
|
33
|
-
children: [
|
|
34
|
-
/*#__PURE__*/ jsx("span", {
|
|
35
|
-
className: classnames('byline-form-status-muted', form_renderer_module["status-muted"]),
|
|
36
|
-
children: t('forms.status.label')
|
|
37
|
-
}),
|
|
38
|
-
/*#__PURE__*/ jsx("span", {
|
|
39
|
-
className: classnames('byline-form-status-trunc', form_renderer_module["status-trunc"]),
|
|
40
|
-
children: statusLabel
|
|
41
|
-
})
|
|
42
|
-
]
|
|
43
|
-
}),
|
|
44
|
-
initialData?.updatedAt != null && /*#__PURE__*/ jsxs("div", {
|
|
45
|
-
className: classnames('byline-form-status-cell', form_renderer_module["status-cell"]),
|
|
46
|
-
children: [
|
|
47
|
-
/*#__PURE__*/ jsx("span", {
|
|
48
|
-
className: classnames('byline-form-status-muted', form_renderer_module["status-muted"]),
|
|
49
|
-
children: t('forms.status.lastModified')
|
|
50
|
-
}),
|
|
51
|
-
/*#__PURE__*/ jsx("span", {
|
|
52
|
-
className: classnames('byline-form-status-trunc', form_renderer_module["status-trunc"]),
|
|
53
|
-
children: /*#__PURE__*/ jsx(LocalDateTime, {
|
|
54
|
-
value: initialData.updatedAt
|
|
55
|
-
})
|
|
56
|
-
})
|
|
57
|
-
]
|
|
58
|
-
}),
|
|
59
|
-
initialData?.createdAt != null && /*#__PURE__*/ jsxs("div", {
|
|
60
|
-
className: classnames('byline-form-status-cell', form_renderer_module["status-cell"]),
|
|
61
|
-
children: [
|
|
62
|
-
/*#__PURE__*/ jsx("span", {
|
|
63
|
-
className: classnames('byline-form-status-muted', form_renderer_module["status-muted"]),
|
|
64
|
-
children: t('forms.status.created')
|
|
65
|
-
}),
|
|
66
|
-
/*#__PURE__*/ jsx("span", {
|
|
67
|
-
className: classnames('byline-form-status-trunc', form_renderer_module["status-trunc"]),
|
|
68
|
-
children: /*#__PURE__*/ jsx(LocalDateTime, {
|
|
69
|
-
value: initialData.createdAt
|
|
70
|
-
})
|
|
71
|
-
})
|
|
72
|
-
]
|
|
73
|
-
})
|
|
74
|
-
]
|
|
75
|
-
}),
|
|
76
|
-
null != publishedVersion && /*#__PURE__*/ jsxs("div", {
|
|
77
|
-
className: classnames('byline-form-status-published', form_renderer_module["status-published"]),
|
|
78
|
-
children: [
|
|
79
|
-
/*#__PURE__*/ jsxs("span", {
|
|
80
|
-
className: classnames('byline-form-status-muted', form_renderer_module["status-muted"]),
|
|
81
|
-
children: [
|
|
82
|
-
t('forms.status.publishedLive'),
|
|
83
|
-
' ',
|
|
84
|
-
publishedVersion.updatedAt ? /*#__PURE__*/ jsx("span", {
|
|
85
|
-
children: t('forms.status.publishedOn', {
|
|
86
|
-
date: new Date(publishedVersion.updatedAt)
|
|
87
|
-
})
|
|
88
|
-
}) : ''
|
|
89
|
-
]
|
|
90
|
-
}),
|
|
91
|
-
onUnpublish && /*#__PURE__*/ jsxs(Fragment, {
|
|
92
|
-
children: [
|
|
93
|
-
' ',
|
|
94
|
-
/*#__PURE__*/ jsx("button", {
|
|
95
|
-
type: "button",
|
|
96
|
-
onClick: onUnpublish,
|
|
97
|
-
className: classnames('byline-form-status-unpublish', form_renderer_module["status-unpublish"]),
|
|
98
|
-
children: t('common.actions.unpublish')
|
|
99
|
-
})
|
|
100
|
-
]
|
|
101
|
-
})
|
|
102
|
-
]
|
|
103
|
-
})
|
|
104
|
-
]
|
|
105
|
-
});
|
|
106
|
-
};
|
|
107
|
-
function computeStatusTransitions(currentStatus, workflowStatuses, nextStatus) {
|
|
108
|
-
if (!workflowStatuses || 0 === workflowStatuses.length || !currentStatus) return {
|
|
109
|
-
primaryStatus: nextStatus,
|
|
110
|
-
secondaryStatuses: [],
|
|
111
|
-
isTerminal: false
|
|
112
|
-
};
|
|
113
|
-
if (workflowStatuses.length <= 1) return {
|
|
114
|
-
primaryStatus: void 0,
|
|
115
|
-
secondaryStatuses: [],
|
|
116
|
-
isTerminal: false
|
|
117
|
-
};
|
|
118
|
-
const currentIndex = workflowStatuses.findIndex((s)=>s.name === currentStatus);
|
|
119
|
-
if (-1 === currentIndex) return {
|
|
120
|
-
primaryStatus: nextStatus,
|
|
121
|
-
secondaryStatuses: [],
|
|
122
|
-
isTerminal: false
|
|
123
|
-
};
|
|
124
|
-
const isAtEnd = currentIndex === workflowStatuses.length - 1;
|
|
125
|
-
const isAtStart = 0 === currentIndex;
|
|
126
|
-
const availableTargets = [];
|
|
127
|
-
if (!isAtStart && workflowStatuses[0]) availableTargets.push(workflowStatuses[0]);
|
|
128
|
-
const prev = workflowStatuses[currentIndex - 1];
|
|
129
|
-
if (currentIndex > 1 && prev) availableTargets.push(prev);
|
|
130
|
-
const next = workflowStatuses[currentIndex + 1];
|
|
131
|
-
if (!isAtEnd && next) availableTargets.push(next);
|
|
132
|
-
if (isAtEnd) return {
|
|
133
|
-
primaryStatus: workflowStatuses[currentIndex],
|
|
134
|
-
secondaryStatuses: availableTargets,
|
|
135
|
-
isTerminal: true
|
|
136
|
-
};
|
|
137
|
-
return {
|
|
138
|
-
primaryStatus: nextStatus,
|
|
139
|
-
secondaryStatuses: availableTargets.filter((s)=>s.name !== nextStatus?.name),
|
|
140
|
-
isTerminal: false
|
|
141
|
-
};
|
|
142
|
-
}
|
|
22
|
+
import { useFormLayout } from "./use-form-layout.js";
|
|
143
23
|
const FormContent = ({ mode, fields, onSubmit, onCancel, onStatusChange, onUnpublish, onDelete, onDuplicate, onCopyToLocale, onDeleteLocale, contentLocales, nextStatus, workflowStatuses, publishedVersion, initialData, adminConfig, useAsTitle, useAsPath, advertiseLocales, headingLabel, headerSlot, collectionPath, initialLocale, onLocaleChange, defaultLocale = 'en', useNavigationGuard: useNavigationGuardProp, restoreWarnings, _activeTabBySet, _onTabChange })=>{
|
|
144
24
|
const { getFieldValues, runFieldHooks, validateForm, errors: initialErrors, hasChanges: hasChangesFn, resetHasChanges, getPatches, getDirtyBreakdown, getSystemPath, getSystemAvailableLocales, subscribeErrors, subscribeMeta, setFieldValue, setFieldError, getPendingUploads, clearPendingUploads, setFieldUploading } = useFormContext();
|
|
145
25
|
const { t } = useTranslation('byline-admin');
|
|
@@ -156,69 +36,7 @@ const FormContent = ({ mode, fields, onSubmit, onCancel, onStatusChange, onUnpub
|
|
|
156
36
|
}, [
|
|
157
37
|
initialLocale
|
|
158
38
|
]);
|
|
159
|
-
const fieldByName =
|
|
160
|
-
const map = new Map();
|
|
161
|
-
for (const field of fields)if ('name' in field) map.set(field.name, field);
|
|
162
|
-
return map;
|
|
163
|
-
}, [
|
|
164
|
-
fields
|
|
165
|
-
]);
|
|
166
|
-
const tabSetByName = useMemo(()=>{
|
|
167
|
-
const map = new Map();
|
|
168
|
-
for (const set of adminConfig?.tabSets ?? [])map.set(set.name, set);
|
|
169
|
-
return map;
|
|
170
|
-
}, [
|
|
171
|
-
adminConfig
|
|
172
|
-
]);
|
|
173
|
-
const rowByName = useMemo(()=>{
|
|
174
|
-
const map = new Map();
|
|
175
|
-
for (const row of adminConfig?.rows ?? [])map.set(row.name, row);
|
|
176
|
-
return map;
|
|
177
|
-
}, [
|
|
178
|
-
adminConfig
|
|
179
|
-
]);
|
|
180
|
-
const groupByName = useMemo(()=>{
|
|
181
|
-
const map = new Map();
|
|
182
|
-
for (const group of adminConfig?.groups ?? [])map.set(group.name, group);
|
|
183
|
-
return map;
|
|
184
|
-
}, [
|
|
185
|
-
adminConfig
|
|
186
|
-
]);
|
|
187
|
-
const layout = useMemo(()=>{
|
|
188
|
-
if (adminConfig?.layout) return adminConfig.layout;
|
|
189
|
-
return {
|
|
190
|
-
main: fields.filter((f)=>'name' in f).map((f)=>f.name)
|
|
191
|
-
};
|
|
192
|
-
}, [
|
|
193
|
-
adminConfig,
|
|
194
|
-
fields
|
|
195
|
-
]);
|
|
196
|
-
const fieldToTabPath = useMemo(()=>{
|
|
197
|
-
const map = new Map();
|
|
198
|
-
const visit = (names, tabSetName, tabName, seen)=>{
|
|
199
|
-
for (const name of names)if (fieldByName.has(name)) map.set(name, {
|
|
200
|
-
tabSetName,
|
|
201
|
-
tabName
|
|
202
|
-
});
|
|
203
|
-
else if (seen.has(name)) ;
|
|
204
|
-
else if (rowByName.has(name)) {
|
|
205
|
-
const row = rowByName.get(name);
|
|
206
|
-
const next = new Set(seen).add(name);
|
|
207
|
-
visit(row.fields, tabSetName, tabName, next);
|
|
208
|
-
} else if (groupByName.has(name)) {
|
|
209
|
-
const group = groupByName.get(name);
|
|
210
|
-
const next = new Set(seen).add(name);
|
|
211
|
-
visit(group.fields, tabSetName, tabName, next);
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
for (const set of adminConfig?.tabSets ?? [])for (const tab of set.tabs)visit(tab.fields, set.name, tab.name, new Set());
|
|
215
|
-
return map;
|
|
216
|
-
}, [
|
|
217
|
-
adminConfig,
|
|
218
|
-
fieldByName,
|
|
219
|
-
rowByName,
|
|
220
|
-
groupByName
|
|
221
|
-
]);
|
|
39
|
+
const { fieldByName, tabSetByName, rowByName, groupByName, layout, fieldToTabPath } = useFormLayout(adminConfig, fields);
|
|
222
40
|
const tabSets = adminConfig?.tabSets ?? [];
|
|
223
41
|
const initialActiveTabBySet = useMemo(()=>{
|
|
224
42
|
const result = {};
|
|
@@ -537,182 +355,23 @@ const FormContent = ({ mode, fields, onSubmit, onCancel, onStatusChange, onUnpub
|
|
|
537
355
|
})
|
|
538
356
|
]
|
|
539
357
|
}),
|
|
540
|
-
showUnsavedModal && /*#__PURE__*/ jsx(
|
|
541
|
-
|
|
542
|
-
closeOnOverlayClick: true,
|
|
543
|
-
onDismiss: ()=>setShowUnsavedModal(false),
|
|
544
|
-
children: /*#__PURE__*/ jsxs(Modal.Container, {
|
|
545
|
-
style: {
|
|
546
|
-
maxWidth: '460px'
|
|
547
|
-
},
|
|
548
|
-
children: [
|
|
549
|
-
/*#__PURE__*/ jsx(Modal.Header, {
|
|
550
|
-
className: classnames('byline-form-guard-modal-head', form_renderer_module["guard-modal-head"]),
|
|
551
|
-
children: /*#__PURE__*/ jsx("h3", {
|
|
552
|
-
className: classnames('byline-form-guard-modal-title', form_renderer_module["guard-modal-title"]),
|
|
553
|
-
children: t('forms.unsavedChanges.title')
|
|
554
|
-
})
|
|
555
|
-
}),
|
|
556
|
-
/*#__PURE__*/ jsx(Modal.Content, {
|
|
557
|
-
children: /*#__PURE__*/ jsx("p", {
|
|
558
|
-
className: classnames('byline-form-guard-modal-text', form_renderer_module["guard-modal-text"]),
|
|
559
|
-
children: t('forms.unsavedChanges.message')
|
|
560
|
-
})
|
|
561
|
-
}),
|
|
562
|
-
/*#__PURE__*/ jsx(Modal.Actions, {
|
|
563
|
-
children: /*#__PURE__*/ jsx(Button, {
|
|
564
|
-
size: "sm",
|
|
565
|
-
style: {
|
|
566
|
-
minWidth: '60px'
|
|
567
|
-
},
|
|
568
|
-
intent: "primary",
|
|
569
|
-
type: "button",
|
|
570
|
-
onClick: ()=>setShowUnsavedModal(false),
|
|
571
|
-
children: t('forms.unsavedChanges.okButton')
|
|
572
|
-
})
|
|
573
|
-
})
|
|
574
|
-
]
|
|
575
|
-
})
|
|
358
|
+
showUnsavedModal && /*#__PURE__*/ jsx(UnsavedChangesModal, {
|
|
359
|
+
onClose: ()=>setShowUnsavedModal(false)
|
|
576
360
|
}),
|
|
577
|
-
null != pendingSystemFieldsSubmit && /*#__PURE__*/ jsx(
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
className: classnames('byline-form-guard-modal-head', form_renderer_module["guard-modal-head"]),
|
|
588
|
-
children: [
|
|
589
|
-
/*#__PURE__*/ jsx("h3", {
|
|
590
|
-
className: classnames('byline-form-guard-modal-title', form_renderer_module["guard-modal-title"]),
|
|
591
|
-
children: pendingSystemFieldsSubmit.contentDirty ? t('forms.systemFieldsConfirm.bothTitle') : t('forms.systemFieldsConfirm.title')
|
|
592
|
-
}),
|
|
593
|
-
/*#__PURE__*/ jsx(IconButton, {
|
|
594
|
-
"aria-label": t('common.actions.close'),
|
|
595
|
-
size: "xs",
|
|
596
|
-
onClick: ()=>setPendingSystemFieldsSubmit(null),
|
|
597
|
-
children: /*#__PURE__*/ jsx(CloseIcon, {
|
|
598
|
-
width: "16px",
|
|
599
|
-
height: "16px",
|
|
600
|
-
svgClassName: "white-icon"
|
|
601
|
-
})
|
|
602
|
-
})
|
|
603
|
-
]
|
|
604
|
-
}),
|
|
605
|
-
/*#__PURE__*/ jsxs(Modal.Content, {
|
|
606
|
-
className: "prose",
|
|
607
|
-
children: [
|
|
608
|
-
pendingSystemFieldsSubmit.contentDirty && /*#__PURE__*/ jsx("p", {
|
|
609
|
-
className: classnames('byline-form-system-fields-content-note', 'm-0 mt-2'),
|
|
610
|
-
children: t('forms.systemFieldsConfirm.contentNote')
|
|
611
|
-
}),
|
|
612
|
-
/*#__PURE__*/ jsx("p", {
|
|
613
|
-
className: "m-0 mt-2",
|
|
614
|
-
style: pendingSystemFieldsSubmit.contentDirty ? {
|
|
615
|
-
marginTop: 'var(--spacing-8)',
|
|
616
|
-
paddingTop: 'var(--spacing-12)',
|
|
617
|
-
borderTop: '1px solid var(--border-color)'
|
|
618
|
-
} : void 0,
|
|
619
|
-
children: t('forms.systemFieldsConfirm.intro')
|
|
620
|
-
}),
|
|
621
|
-
/*#__PURE__*/ jsxs("ul", {
|
|
622
|
-
className: classnames('byline-form-system-fields-list', form_renderer_module["guard-modal-text"], 'm-0'),
|
|
623
|
-
children: [
|
|
624
|
-
pendingSystemFieldsSubmit.pathDirty && /*#__PURE__*/ jsx("li", {
|
|
625
|
-
children: t('forms.systemFieldsConfirm.bulletPath')
|
|
626
|
-
}),
|
|
627
|
-
pendingSystemFieldsSubmit.availableLocalesDirty && /*#__PURE__*/ jsx("li", {
|
|
628
|
-
children: t('forms.systemFieldsConfirm.bulletLocales')
|
|
629
|
-
})
|
|
630
|
-
]
|
|
631
|
-
}),
|
|
632
|
-
/*#__PURE__*/ jsx("p", {
|
|
633
|
-
className: classnames('byline-form-system-fields-effect', form_renderer_module["guard-modal-text"]),
|
|
634
|
-
style: {
|
|
635
|
-
marginTop: 'var(--spacing-4)',
|
|
636
|
-
marginBottom: 0,
|
|
637
|
-
color: 'var(--text-subtle)'
|
|
638
|
-
},
|
|
639
|
-
children: t('forms.systemFieldsConfirm.effectLine')
|
|
640
|
-
})
|
|
641
|
-
]
|
|
642
|
-
}),
|
|
643
|
-
/*#__PURE__*/ jsxs(Modal.Actions, {
|
|
644
|
-
children: [
|
|
645
|
-
/*#__PURE__*/ jsx(Button, {
|
|
646
|
-
size: "sm",
|
|
647
|
-
style: {
|
|
648
|
-
minWidth: '80px'
|
|
649
|
-
},
|
|
650
|
-
intent: "noeffect",
|
|
651
|
-
type: "button",
|
|
652
|
-
onClick: ()=>setPendingSystemFieldsSubmit(null),
|
|
653
|
-
children: t('common.actions.cancel')
|
|
654
|
-
}),
|
|
655
|
-
/*#__PURE__*/ jsx(Button, {
|
|
656
|
-
size: "sm",
|
|
657
|
-
style: {
|
|
658
|
-
minWidth: '80px'
|
|
659
|
-
},
|
|
660
|
-
intent: "primary",
|
|
661
|
-
type: "button",
|
|
662
|
-
onClick: ()=>{
|
|
663
|
-
const payload = pendingSystemFieldsSubmit;
|
|
664
|
-
setPendingSystemFieldsSubmit(null);
|
|
665
|
-
submitPayload(payload);
|
|
666
|
-
},
|
|
667
|
-
children: t('forms.systemFieldsConfirm.confirmButton')
|
|
668
|
-
})
|
|
669
|
-
]
|
|
670
|
-
})
|
|
671
|
-
]
|
|
672
|
-
})
|
|
361
|
+
null != pendingSystemFieldsSubmit && /*#__PURE__*/ jsx(SystemFieldsConfirmModal, {
|
|
362
|
+
contentDirty: pendingSystemFieldsSubmit.contentDirty,
|
|
363
|
+
pathDirty: pendingSystemFieldsSubmit.pathDirty,
|
|
364
|
+
availableLocalesDirty: pendingSystemFieldsSubmit.availableLocalesDirty,
|
|
365
|
+
onCancel: ()=>setPendingSystemFieldsSubmit(null),
|
|
366
|
+
onConfirm: ()=>{
|
|
367
|
+
const payload = pendingSystemFieldsSubmit;
|
|
368
|
+
setPendingSystemFieldsSubmit(null);
|
|
369
|
+
submitPayload(payload);
|
|
370
|
+
}
|
|
673
371
|
}),
|
|
674
|
-
guard.isBlocked && /*#__PURE__*/ jsx(
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
onDismiss: guard.stay,
|
|
678
|
-
children: /*#__PURE__*/ jsxs(Modal.Container, {
|
|
679
|
-
style: {
|
|
680
|
-
maxWidth: '460px'
|
|
681
|
-
},
|
|
682
|
-
children: [
|
|
683
|
-
/*#__PURE__*/ jsx(Modal.Header, {
|
|
684
|
-
className: classnames('byline-form-guard-modal-head', form_renderer_module["guard-modal-head"]),
|
|
685
|
-
children: /*#__PURE__*/ jsx("h3", {
|
|
686
|
-
className: classnames('byline-form-guard-modal-title', form_renderer_module["guard-modal-title"]),
|
|
687
|
-
children: t('forms.navigationGuard.title')
|
|
688
|
-
})
|
|
689
|
-
}),
|
|
690
|
-
/*#__PURE__*/ jsx(Modal.Content, {
|
|
691
|
-
children: /*#__PURE__*/ jsx("p", {
|
|
692
|
-
className: classnames('byline-form-guard-modal-text', form_renderer_module["guard-modal-text"]),
|
|
693
|
-
children: t('forms.navigationGuard.message')
|
|
694
|
-
})
|
|
695
|
-
}),
|
|
696
|
-
/*#__PURE__*/ jsxs(Modal.Actions, {
|
|
697
|
-
children: [
|
|
698
|
-
/*#__PURE__*/ jsx(Button, {
|
|
699
|
-
size: "sm",
|
|
700
|
-
intent: "noeffect",
|
|
701
|
-
type: "button",
|
|
702
|
-
onClick: guard.stay,
|
|
703
|
-
children: t('forms.navigationGuard.stayButton')
|
|
704
|
-
}),
|
|
705
|
-
/*#__PURE__*/ jsx(Button, {
|
|
706
|
-
size: "sm",
|
|
707
|
-
intent: "danger",
|
|
708
|
-
type: "button",
|
|
709
|
-
onClick: guard.proceed,
|
|
710
|
-
children: t('forms.navigationGuard.leaveButton')
|
|
711
|
-
})
|
|
712
|
-
]
|
|
713
|
-
})
|
|
714
|
-
]
|
|
715
|
-
})
|
|
372
|
+
guard.isBlocked && /*#__PURE__*/ jsx(NavigationGuardModal, {
|
|
373
|
+
onStay: guard.stay,
|
|
374
|
+
onProceed: guard.proceed
|
|
716
375
|
})
|
|
717
376
|
]
|
|
718
377
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import type { WorkflowStatus } from '@byline/core';
|
|
9
|
+
import type { PublishedVersionInfo } from './form-renderer';
|
|
10
|
+
/**
|
|
11
|
+
* The status / metadata strip at the top of a document form — current
|
|
12
|
+
* workflow status (suppressed for single-status workflows), last-modified and
|
|
13
|
+
* created timestamps, and a "published live" indicator with an inline
|
|
14
|
+
* Unpublish action when a previously-published version is still live.
|
|
15
|
+
*/
|
|
16
|
+
export declare const FormStatusDisplay: ({ initialData, workflowStatuses, publishedVersion, onUnpublish, }: {
|
|
17
|
+
initialData?: Record<string, any>;
|
|
18
|
+
workflowStatuses?: WorkflowStatus[];
|
|
19
|
+
publishedVersion?: PublishedVersionInfo | null;
|
|
20
|
+
onUnpublish?: () => Promise<void>;
|
|
21
|
+
}) => import("react").JSX.Element;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useTranslation } from "@byline/i18n/react";
|
|
4
|
+
import classnames from "classnames";
|
|
5
|
+
import { LocalDateTime } from "../fields/local-date-time.js";
|
|
6
|
+
import form_renderer_module from "./form-renderer.module.js";
|
|
7
|
+
const FormStatusDisplay = ({ initialData, workflowStatuses, publishedVersion, onUnpublish })=>{
|
|
8
|
+
const { t } = useTranslation('byline-admin');
|
|
9
|
+
const statusCode = initialData?.status;
|
|
10
|
+
const statusLabel = workflowStatuses?.find((s)=>s.name === statusCode)?.label ?? statusCode;
|
|
11
|
+
const showStatusCell = (workflowStatuses?.length ?? 0) > 1;
|
|
12
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
13
|
+
className: classnames('byline-form-status', form_renderer_module.status),
|
|
14
|
+
children: [
|
|
15
|
+
/*#__PURE__*/ jsxs("div", {
|
|
16
|
+
className: classnames('byline-form-status-meta', form_renderer_module["status-meta"]),
|
|
17
|
+
children: [
|
|
18
|
+
showStatusCell && /*#__PURE__*/ jsxs("div", {
|
|
19
|
+
className: classnames('byline-form-status-cell', form_renderer_module["status-cell"]),
|
|
20
|
+
children: [
|
|
21
|
+
/*#__PURE__*/ jsx("span", {
|
|
22
|
+
className: classnames('byline-form-status-muted', form_renderer_module["status-muted"]),
|
|
23
|
+
children: t('forms.status.label')
|
|
24
|
+
}),
|
|
25
|
+
/*#__PURE__*/ jsx("span", {
|
|
26
|
+
className: classnames('byline-form-status-trunc', form_renderer_module["status-trunc"]),
|
|
27
|
+
children: statusLabel
|
|
28
|
+
})
|
|
29
|
+
]
|
|
30
|
+
}),
|
|
31
|
+
initialData?.updatedAt != null && /*#__PURE__*/ jsxs("div", {
|
|
32
|
+
className: classnames('byline-form-status-cell', form_renderer_module["status-cell"]),
|
|
33
|
+
children: [
|
|
34
|
+
/*#__PURE__*/ jsx("span", {
|
|
35
|
+
className: classnames('byline-form-status-muted', form_renderer_module["status-muted"]),
|
|
36
|
+
children: t('forms.status.lastModified')
|
|
37
|
+
}),
|
|
38
|
+
/*#__PURE__*/ jsx("span", {
|
|
39
|
+
className: classnames('byline-form-status-trunc', form_renderer_module["status-trunc"]),
|
|
40
|
+
children: /*#__PURE__*/ jsx(LocalDateTime, {
|
|
41
|
+
value: initialData.updatedAt
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
]
|
|
45
|
+
}),
|
|
46
|
+
initialData?.createdAt != null && /*#__PURE__*/ jsxs("div", {
|
|
47
|
+
className: classnames('byline-form-status-cell', form_renderer_module["status-cell"]),
|
|
48
|
+
children: [
|
|
49
|
+
/*#__PURE__*/ jsx("span", {
|
|
50
|
+
className: classnames('byline-form-status-muted', form_renderer_module["status-muted"]),
|
|
51
|
+
children: t('forms.status.created')
|
|
52
|
+
}),
|
|
53
|
+
/*#__PURE__*/ jsx("span", {
|
|
54
|
+
className: classnames('byline-form-status-trunc', form_renderer_module["status-trunc"]),
|
|
55
|
+
children: /*#__PURE__*/ jsx(LocalDateTime, {
|
|
56
|
+
value: initialData.createdAt
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
]
|
|
60
|
+
})
|
|
61
|
+
]
|
|
62
|
+
}),
|
|
63
|
+
null != publishedVersion && /*#__PURE__*/ jsxs("div", {
|
|
64
|
+
className: classnames('byline-form-status-published', form_renderer_module["status-published"]),
|
|
65
|
+
children: [
|
|
66
|
+
/*#__PURE__*/ jsxs("span", {
|
|
67
|
+
className: classnames('byline-form-status-muted', form_renderer_module["status-muted"]),
|
|
68
|
+
children: [
|
|
69
|
+
t('forms.status.publishedLive'),
|
|
70
|
+
' ',
|
|
71
|
+
publishedVersion.updatedAt ? /*#__PURE__*/ jsx("span", {
|
|
72
|
+
children: t('forms.status.publishedOn', {
|
|
73
|
+
date: new Date(publishedVersion.updatedAt)
|
|
74
|
+
})
|
|
75
|
+
}) : ''
|
|
76
|
+
]
|
|
77
|
+
}),
|
|
78
|
+
onUnpublish && /*#__PURE__*/ jsxs(Fragment, {
|
|
79
|
+
children: [
|
|
80
|
+
' ',
|
|
81
|
+
/*#__PURE__*/ jsx("button", {
|
|
82
|
+
type: "button",
|
|
83
|
+
onClick: onUnpublish,
|
|
84
|
+
className: classnames('byline-form-status-unpublish', form_renderer_module["status-unpublish"]),
|
|
85
|
+
children: t('common.actions.unpublish')
|
|
86
|
+
})
|
|
87
|
+
]
|
|
88
|
+
})
|
|
89
|
+
]
|
|
90
|
+
})
|
|
91
|
+
]
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
export { FormStatusDisplay };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
import type { WorkflowStatus } from '@byline/core';
|
|
9
|
+
export interface StatusTransitions {
|
|
10
|
+
primaryStatus: WorkflowStatus | undefined;
|
|
11
|
+
secondaryStatuses: WorkflowStatus[];
|
|
12
|
+
isTerminal: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Compute the primary and secondary status transitions for the ComboButton.
|
|
16
|
+
* - Primary: the main action (forward step), or the current status itself
|
|
17
|
+
* when the document has reached the final workflow step (terminal state).
|
|
18
|
+
* - Secondary: other available transitions to show as dropdown options.
|
|
19
|
+
* - isTerminal: true when the document is at the final workflow status —
|
|
20
|
+
* the primary button renders as a non-actionable indicator and all
|
|
21
|
+
* back-steps move into the dropdown.
|
|
22
|
+
*/
|
|
23
|
+
export declare function computeStatusTransitions(currentStatus: string | undefined, workflowStatuses: WorkflowStatus[] | undefined, nextStatus: WorkflowStatus | undefined): StatusTransitions;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
function computeStatusTransitions(currentStatus, workflowStatuses, nextStatus) {
|
|
2
|
+
if (!workflowStatuses || 0 === workflowStatuses.length || !currentStatus) return {
|
|
3
|
+
primaryStatus: nextStatus,
|
|
4
|
+
secondaryStatuses: [],
|
|
5
|
+
isTerminal: false
|
|
6
|
+
};
|
|
7
|
+
if (workflowStatuses.length <= 1) return {
|
|
8
|
+
primaryStatus: void 0,
|
|
9
|
+
secondaryStatuses: [],
|
|
10
|
+
isTerminal: false
|
|
11
|
+
};
|
|
12
|
+
const currentIndex = workflowStatuses.findIndex((s)=>s.name === currentStatus);
|
|
13
|
+
if (-1 === currentIndex) return {
|
|
14
|
+
primaryStatus: nextStatus,
|
|
15
|
+
secondaryStatuses: [],
|
|
16
|
+
isTerminal: false
|
|
17
|
+
};
|
|
18
|
+
const isAtEnd = currentIndex === workflowStatuses.length - 1;
|
|
19
|
+
const isAtStart = 0 === currentIndex;
|
|
20
|
+
const availableTargets = [];
|
|
21
|
+
if (!isAtStart && workflowStatuses[0]) availableTargets.push(workflowStatuses[0]);
|
|
22
|
+
const prev = workflowStatuses[currentIndex - 1];
|
|
23
|
+
if (currentIndex > 1 && prev) availableTargets.push(prev);
|
|
24
|
+
const next = workflowStatuses[currentIndex + 1];
|
|
25
|
+
if (!isAtEnd && next) availableTargets.push(next);
|
|
26
|
+
if (isAtEnd) return {
|
|
27
|
+
primaryStatus: workflowStatuses[currentIndex],
|
|
28
|
+
secondaryStatuses: availableTargets,
|
|
29
|
+
isTerminal: true
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
primaryStatus: nextStatus,
|
|
33
|
+
secondaryStatuses: availableTargets.filter((s)=>s.name !== nextStatus?.name),
|
|
34
|
+
isTerminal: false
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export { computeStatusTransitions };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { CollectionAdminConfig, Field, GroupDefinition, LayoutDefinition, RowDefinition, TabSetDefinition } from '@byline/core';
|
|
2
|
+
/** Which tab set + tab a schema field is placed under. */
|
|
3
|
+
export interface TabPath {
|
|
4
|
+
tabSetName: string;
|
|
5
|
+
tabName: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Derived lookup tables that drive the form's layout walk and per-tab error
|
|
9
|
+
* badges. All are pure derivations of `adminConfig` + `fields`; the startup
|
|
10
|
+
* validator guarantees every reachable name resolves and every schema field is
|
|
11
|
+
* placed at most once, so the consuming render-time lookups stay unguarded.
|
|
12
|
+
*/
|
|
13
|
+
export interface FormLayout {
|
|
14
|
+
/** Schema field name → field definition. */
|
|
15
|
+
fieldByName: Map<string, Field>;
|
|
16
|
+
tabSetByName: Map<string, TabSetDefinition>;
|
|
17
|
+
rowByName: Map<string, RowDefinition>;
|
|
18
|
+
groupByName: Map<string, GroupDefinition>;
|
|
19
|
+
/** Region placement; synthesised to `{ main: <all fields> }` when omitted. */
|
|
20
|
+
layout: LayoutDefinition;
|
|
21
|
+
/** Reverse index: schema field name → which tab set + tab it lives in. */
|
|
22
|
+
fieldToTabPath: Map<string, TabPath>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build the reverse index from schema field name to its enclosing tab set +
|
|
26
|
+
* tab. Fields not under any tab set (e.g. raw-field placement directly in
|
|
27
|
+
* `layout.main`) are absent from the map. Rows and groups are recursed into;
|
|
28
|
+
* the `seen` set guards against a config that references a row/group cycle.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildFieldToTabPath(adminConfig: CollectionAdminConfig | undefined, fieldByName: Map<string, Field>, rowByName: Map<string, RowDefinition>, groupByName: Map<string, GroupDefinition>): Map<string, TabPath>;
|
|
31
|
+
/**
|
|
32
|
+
* Memoise the layout primitives + lookup tables the form renderer walks.
|
|
33
|
+
* Rebuilt only when `adminConfig` / `fields` change.
|
|
34
|
+
*/
|
|
35
|
+
export declare function useFormLayout(adminConfig: CollectionAdminConfig | undefined, fields: Field[]): FormLayout;
|