@jini-ai/admin 0.4.3 → 0.4.5
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/CHANGELOG.md +4 -0
- package/dist/comments/comments.module.d.ts +4 -4
- package/dist/comments/messages.en.d.ts +4 -4
- package/dist/comments/messages.en.js +4 -4
- package/dist/comments/react/components/QueueSection.js +1 -1
- package/dist/comments/react/index.d.ts +16 -16
- package/dist/forms/forms.module.d.ts +3 -2
- package/dist/forms/html-rules.d.ts +14 -0
- package/dist/forms/html-rules.js +10 -0
- package/dist/forms/messages.en.d.ts +3 -2
- package/dist/forms/messages.en.js +4 -3
- package/dist/forms/react/components/FormEditorHeaderText.js +2 -2
- package/dist/forms/react/components/FormSubmissionDetail.d.ts +3 -1
- package/dist/forms/react/components/FormSubmissionDetail.js +3 -2
- package/dist/forms/react/components/FormSubmissions.js +1 -1
- package/dist/forms/react/hooks/forms-view.hooks.d.ts +1 -6
- package/dist/forms/react/hooks/forms-view.hooks.js +3 -4
- package/dist/forms/react/index.d.ts +9 -6
- package/dist/forms/react/index.js +3 -1
- package/dist/forms/react/pages/FormsList.js +2 -7
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.d.ts +3 -1
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.js +5 -1
- package/dist/react/components/ConfirmDialog/use-confirm-request.d.ts +22 -0
- package/dist/react/components/ConfirmDialog/use-confirm-request.js +53 -0
- package/dist/react/components/RowMenu/RowMenu.hooks.js +14 -3
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +1 -0
- package/dist/redirects/messages.en.d.ts +9 -7
- package/dist/redirects/messages.en.js +14 -9
- package/dist/redirects/react/hooks/RedirectsPage.hooks.js +2 -2
- package/dist/redirects/react/index.d.ts +36 -28
- package/dist/redirects/react/pages/RedirectsPage.js +1 -1
- package/dist/redirects/redirects.module.d.ts +9 -7
- package/dist/seo/messages.en.d.ts +5 -4
- package/dist/seo/messages.en.js +6 -5
- package/dist/seo/react/components/MediaRefField.d.ts +4 -0
- package/dist/seo/react/components/MediaRefField.js +2 -2
- package/dist/seo/react/components/SitemapModal.js +4 -2
- package/dist/seo/react/hooks/MediaRefField.hooks.d.ts +4 -1
- package/dist/seo/react/hooks/MediaRefField.hooks.js +3 -3
- package/dist/seo/react/index.d.ts +15 -12
- package/dist/seo/react/options.d.ts +7 -0
- package/dist/seo/react/pages/SeoPage.js +23 -23
- package/dist/seo/rules.d.ts +6 -0
- package/dist/seo/rules.js +8 -0
- package/dist/seo/seo.module.d.ts +5 -4
- package/dist/widgets/react/pages/WidgetRegionEditor.js +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -51,10 +51,10 @@ export declare const commentsModule: {
|
|
|
51
51
|
readonly "Saved.": "Saved.";
|
|
52
52
|
readonly "failed to save Comments settings": "failed to save Comments settings";
|
|
53
53
|
readonly "failed to load Comments settings": "failed to load Comments settings";
|
|
54
|
-
readonly pending: "
|
|
55
|
-
readonly approved: "
|
|
56
|
-
readonly spam: "
|
|
57
|
-
readonly trash: "
|
|
54
|
+
readonly pending: "Pending";
|
|
55
|
+
readonly approved: "Approved";
|
|
56
|
+
readonly spam: "Spam";
|
|
57
|
+
readonly trash: "Trash";
|
|
58
58
|
};
|
|
59
59
|
readonly pages: {
|
|
60
60
|
readonly queue: {
|
|
@@ -43,10 +43,10 @@ export declare const commentsMessagesEn: {
|
|
|
43
43
|
readonly "Saved.": "Saved.";
|
|
44
44
|
readonly "failed to save Comments settings": "failed to save Comments settings";
|
|
45
45
|
readonly "failed to load Comments settings": "failed to load Comments settings";
|
|
46
|
-
readonly pending: "
|
|
47
|
-
readonly approved: "
|
|
48
|
-
readonly spam: "
|
|
49
|
-
readonly trash: "
|
|
46
|
+
readonly pending: "Pending";
|
|
47
|
+
readonly approved: "Approved";
|
|
48
|
+
readonly spam: "Spam";
|
|
49
|
+
readonly trash: "Trash";
|
|
50
50
|
};
|
|
51
51
|
export type CommentsTranslator = (key: string, vars?: Record<string, string | number>) => string;
|
|
52
52
|
/** Default copy; unknown keys remain visible and variables use the native string formatter. */
|
|
@@ -43,10 +43,10 @@ export const commentsMessagesEn = {
|
|
|
43
43
|
"Saved.": "Saved.",
|
|
44
44
|
"failed to save Comments settings": "failed to save Comments settings",
|
|
45
45
|
"failed to load Comments settings": "failed to load Comments settings",
|
|
46
|
-
"pending": "
|
|
47
|
-
"approved": "
|
|
48
|
-
"spam": "
|
|
49
|
-
"trash": "
|
|
46
|
+
"pending": "Pending",
|
|
47
|
+
"approved": "Approved",
|
|
48
|
+
"spam": "Spam",
|
|
49
|
+
"trash": "Trash"
|
|
50
50
|
};
|
|
51
51
|
/** Default copy; unknown keys remain visible and variables use the native string formatter. */
|
|
52
52
|
export const englishComments = (key, vars = {}) => {
|
|
@@ -10,7 +10,7 @@ const STATUS_OPTIONS = ["pending", "approved", "spam", "trash"];
|
|
|
10
10
|
/** The status-filter `<select>` — pure presentation, no state of its own. */
|
|
11
11
|
function QueueToolbar({ status, onStatusChange, t, }) {
|
|
12
12
|
const { onChange } = useQueueToolbar({ onStatusChange });
|
|
13
|
-
return (_jsx("div", { className: "toolbar", children: _jsxs("div", { className: "field", children: [_jsx("label", { className: "field-label", htmlFor: "comments-status-filter", children: t("Status") }), _jsx("select", { id: "comments-status-filter", value: status, onChange: onChange, ...agentHandle({ handle: "comments-status-filter" }, { role: "field", label: "Filter the moderation queue by status" }), children: STATUS_OPTIONS.map((s) => (_jsx("option", { value: s, children: s }, s))) })] }) }));
|
|
13
|
+
return (_jsx("div", { className: "toolbar", children: _jsxs("div", { className: "field", children: [_jsx("label", { className: "field-label", htmlFor: "comments-status-filter", children: t("Status") }), _jsx("select", { id: "comments-status-filter", value: status, onChange: onChange, ...agentHandle({ handle: "comments-status-filter" }, { role: "field", label: "Filter the moderation queue by status" }), children: STATUS_OPTIONS.map((s) => (_jsx("option", { value: s, children: t(s) }, s))) })] }) }));
|
|
14
14
|
}
|
|
15
15
|
/** The moderation-queue table's "More" column — a `RowMenu` built from `commentRowMenuItems`, or
|
|
16
16
|
* an em dash when the operator's permissions leave no items, plus this row's own error (if the
|
|
@@ -64,10 +64,10 @@ export declare function comments(_required: Record<string, never>, { t }?: {
|
|
|
64
64
|
readonly "Saved.": "Saved.";
|
|
65
65
|
readonly "failed to save Comments settings": "failed to save Comments settings";
|
|
66
66
|
readonly "failed to load Comments settings": "failed to load Comments settings";
|
|
67
|
-
readonly pending: "
|
|
68
|
-
readonly approved: "
|
|
69
|
-
readonly spam: "
|
|
70
|
-
readonly trash: "
|
|
67
|
+
readonly pending: "Pending";
|
|
68
|
+
readonly approved: "Approved";
|
|
69
|
+
readonly spam: "Spam";
|
|
70
|
+
readonly trash: "Trash";
|
|
71
71
|
};
|
|
72
72
|
pages: {
|
|
73
73
|
readonly queue: {
|
|
@@ -140,10 +140,10 @@ export declare function comments(_required: Record<string, never>, { t }?: {
|
|
|
140
140
|
readonly "Saved.": "Saved.";
|
|
141
141
|
readonly "failed to save Comments settings": "failed to save Comments settings";
|
|
142
142
|
readonly "failed to load Comments settings": "failed to load Comments settings";
|
|
143
|
-
readonly pending: "
|
|
144
|
-
readonly approved: "
|
|
145
|
-
readonly spam: "
|
|
146
|
-
readonly trash: "
|
|
143
|
+
readonly pending: "Pending";
|
|
144
|
+
readonly approved: "Approved";
|
|
145
|
+
readonly spam: "Spam";
|
|
146
|
+
readonly trash: "Trash";
|
|
147
147
|
};
|
|
148
148
|
pages: {
|
|
149
149
|
readonly queue: {
|
|
@@ -214,10 +214,10 @@ export declare function comments(_required: Record<string, never>, { t }?: {
|
|
|
214
214
|
readonly "Saved.": "Saved.";
|
|
215
215
|
readonly "failed to save Comments settings": "failed to save Comments settings";
|
|
216
216
|
readonly "failed to load Comments settings": "failed to load Comments settings";
|
|
217
|
-
readonly pending: "
|
|
218
|
-
readonly approved: "
|
|
219
|
-
readonly spam: "
|
|
220
|
-
readonly trash: "
|
|
217
|
+
readonly pending: "Pending";
|
|
218
|
+
readonly approved: "Approved";
|
|
219
|
+
readonly spam: "Spam";
|
|
220
|
+
readonly trash: "Trash";
|
|
221
221
|
};
|
|
222
222
|
pages: {
|
|
223
223
|
readonly queue: {
|
|
@@ -292,10 +292,10 @@ export declare function comments(_required: Record<string, never>, { t }?: {
|
|
|
292
292
|
readonly "Saved.": "Saved.";
|
|
293
293
|
readonly "failed to save Comments settings": "failed to save Comments settings";
|
|
294
294
|
readonly "failed to load Comments settings": "failed to load Comments settings";
|
|
295
|
-
readonly pending: "
|
|
296
|
-
readonly approved: "
|
|
297
|
-
readonly spam: "
|
|
298
|
-
readonly trash: "
|
|
295
|
+
readonly pending: "Pending";
|
|
296
|
+
readonly approved: "Approved";
|
|
297
|
+
readonly spam: "Spam";
|
|
298
|
+
readonly trash: "Trash";
|
|
299
299
|
};
|
|
300
300
|
pages: {
|
|
301
301
|
readonly queue: {
|
|
@@ -70,8 +70,8 @@ export declare const formsModule: {
|
|
|
70
70
|
readonly "No submissions yet.": "No submissions yet.";
|
|
71
71
|
readonly "Create form": "Create form";
|
|
72
72
|
readonly Form: "Form";
|
|
73
|
-
readonly "Configure a new form's fields
|
|
74
|
-
readonly "Configure this form's fields
|
|
73
|
+
readonly "Configure a new form's fields.": "Configure a new form's fields.";
|
|
74
|
+
readonly "Configure this form's fields, or review its submissions.": "Configure this form's fields, or review its submissions.";
|
|
75
75
|
readonly Submissions: "Submissions";
|
|
76
76
|
readonly "Back to forms": "Back to forms";
|
|
77
77
|
readonly "Actions for form \"{name}\"": "Actions for form \"{name}\"";
|
|
@@ -103,6 +103,7 @@ export declare const formsModule: {
|
|
|
103
103
|
readonly "failed to load submissions": "failed to load submissions";
|
|
104
104
|
readonly "failed to load submission": "failed to load submission";
|
|
105
105
|
readonly "delete failed": "delete failed";
|
|
106
|
+
readonly "Unknown field": "Unknown field";
|
|
106
107
|
};
|
|
107
108
|
readonly pages: {
|
|
108
109
|
readonly list: {
|
|
@@ -8,6 +8,20 @@ export interface FormAnswerColumn {
|
|
|
8
8
|
export declare function formAnswerColumns({ fields }: {
|
|
9
9
|
fields: readonly AdminFormField[];
|
|
10
10
|
}, _optional?: {}): FormAnswerColumn[];
|
|
11
|
+
/** Detail rows reuse the table's persisted field labels, including HTML-derived descriptors.
|
|
12
|
+
* Removed fields retain their answers with a plain fallback instead of exposing their IDs.
|
|
13
|
+
* @complexity O(c + a) time and space for c columns and a submitted answers; no I/O.
|
|
14
|
+
*/
|
|
15
|
+
export declare function formSubmissionRows({ data, answerColumns }: {
|
|
16
|
+
data: AdminFormSubmission['data'];
|
|
17
|
+
answerColumns: readonly FormAnswerColumn[];
|
|
18
|
+
}, { unknownFieldLabel }?: {
|
|
19
|
+
unknownFieldLabel?: string;
|
|
20
|
+
}): Array<{
|
|
21
|
+
key: string;
|
|
22
|
+
label: string;
|
|
23
|
+
value: string;
|
|
24
|
+
}>;
|
|
11
25
|
/** Editable form BODY only: the server supplies the transport and spam-protection wrapper. */
|
|
12
26
|
export declare function formHtmlStarter({ fields }: {
|
|
13
27
|
fields: readonly AdminFormField[];
|
package/dist/forms/html-rules.js
CHANGED
|
@@ -6,6 +6,16 @@ export function formAnswerColumns({ fields }, _optional = {}) {
|
|
|
6
6
|
cell: (submission) => submission.data[field.id] === undefined ? "" : String(submission.data[field.id]),
|
|
7
7
|
}));
|
|
8
8
|
}
|
|
9
|
+
/** Detail rows reuse the table's persisted field labels, including HTML-derived descriptors.
|
|
10
|
+
* Removed fields retain their answers with a plain fallback instead of exposing their IDs.
|
|
11
|
+
* @complexity O(c + a) time and space for c columns and a submitted answers; no I/O.
|
|
12
|
+
*/
|
|
13
|
+
export function formSubmissionRows({ data, answerColumns }, { unknownFieldLabel = "Unknown field" } = {}) {
|
|
14
|
+
const labels = new Map(answerColumns.map(column => [column.key, column.header]));
|
|
15
|
+
return Object.entries(data).map(([key, value]) => ({
|
|
16
|
+
key, label: labels.get(`answer:${key}`)?.trim() || unknownFieldLabel, value: String(value),
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
9
19
|
function escapeHtml(value) {
|
|
10
20
|
return value.replace(/[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[char]);
|
|
11
21
|
}
|
|
@@ -61,8 +61,8 @@ export declare const formsMessagesEn: {
|
|
|
61
61
|
readonly "No submissions yet.": "No submissions yet.";
|
|
62
62
|
readonly "Create form": "Create form";
|
|
63
63
|
readonly Form: "Form";
|
|
64
|
-
readonly "Configure a new form's fields
|
|
65
|
-
readonly "Configure this form's fields
|
|
64
|
+
readonly "Configure a new form's fields.": "Configure a new form's fields.";
|
|
65
|
+
readonly "Configure this form's fields, or review its submissions.": "Configure this form's fields, or review its submissions.";
|
|
66
66
|
readonly Submissions: "Submissions";
|
|
67
67
|
readonly "Back to forms": "Back to forms";
|
|
68
68
|
readonly "Actions for form \"{name}\"": "Actions for form \"{name}\"";
|
|
@@ -94,6 +94,7 @@ export declare const formsMessagesEn: {
|
|
|
94
94
|
readonly "failed to load submissions": "failed to load submissions";
|
|
95
95
|
readonly "failed to load submission": "failed to load submission";
|
|
96
96
|
readonly "delete failed": "delete failed";
|
|
97
|
+
readonly "Unknown field": "Unknown field";
|
|
97
98
|
};
|
|
98
99
|
/** Host translators may replace any key; the default preserves the original English bytes. */
|
|
99
100
|
export declare const translateForms: FormsTranslator;
|
|
@@ -60,8 +60,8 @@ export const formsMessagesEn = {
|
|
|
60
60
|
"No submissions yet.": "No submissions yet.",
|
|
61
61
|
"Create form": "Create form",
|
|
62
62
|
"Form": "Form",
|
|
63
|
-
"Configure a new form's fields
|
|
64
|
-
"Configure this form's fields
|
|
63
|
+
"Configure a new form's fields.": "Configure a new form's fields.",
|
|
64
|
+
"Configure this form's fields, or review its submissions.": "Configure this form's fields, or review its submissions.",
|
|
65
65
|
"Submissions": "Submissions",
|
|
66
66
|
"Back to forms": "Back to forms",
|
|
67
67
|
"Actions for form \"{name}\"": "Actions for form \"{name}\"",
|
|
@@ -92,7 +92,8 @@ export const formsMessagesEn = {
|
|
|
92
92
|
"recipients": "recipients",
|
|
93
93
|
"failed to load submissions": "failed to load submissions",
|
|
94
94
|
"failed to load submission": "failed to load submission",
|
|
95
|
-
"delete failed": "delete failed"
|
|
95
|
+
"delete failed": "delete failed",
|
|
96
|
+
"Unknown field": "Unknown field",
|
|
96
97
|
};
|
|
97
98
|
/** Host translators may replace any key; the default preserves the original English bytes. */
|
|
98
99
|
export const translateForms = (key, vars = {}) => {
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
export function FormEditorHeaderText(props, _optional = {}) {
|
|
3
3
|
const { isNew, name, t } = props;
|
|
4
4
|
return (_jsxs("div", { className: "page-header-text", children: [_jsx("p", { className: "page-kicker", children: t("Content") }), _jsx("h1", { className: "page-title", children: isNew ? t("New form") : name || t("Form") }), _jsx("p", { className: "page-description", children: isNew
|
|
5
|
-
? t("Configure a new form's fields
|
|
6
|
-
: t("Configure this form's fields
|
|
5
|
+
? t("Configure a new form's fields.")
|
|
6
|
+
: t("Configure this form's fields, or review its submissions.") })] }));
|
|
7
7
|
}
|
|
8
8
|
//# sourceMappingURL=FormEditorHeaderText.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useWiredFormSubmissionDetail } from '../hooks/use-form-submission-detail.hooks.js';
|
|
2
|
+
import { type FormAnswerColumn } from '../../html-rules.js';
|
|
2
3
|
export interface FormSubmissionDetailProps {
|
|
3
4
|
formId: string;
|
|
4
5
|
submissionId: string;
|
|
@@ -8,6 +9,7 @@ export interface FormSubmissionDetailProps {
|
|
|
8
9
|
useFormSubmissionDetailHook?: typeof useWiredFormSubmissionDetail;
|
|
9
10
|
/** Translator closure — see `FormEditor()`'s own `t`. */
|
|
10
11
|
t: (key: string) => string;
|
|
12
|
+
answerColumns?: readonly FormAnswerColumn[];
|
|
11
13
|
}
|
|
12
|
-
export declare function FormSubmissionDetail({ formId, submissionId, onBack, onDeleted, useFormSubmissionDetailHook, t, }: FormSubmissionDetailProps, _optional?: Record<string, never>): import("react").JSX.Element;
|
|
14
|
+
export declare function FormSubmissionDetail({ formId, submissionId, onBack, onDeleted, useFormSubmissionDetailHook, t, answerColumns, }: FormSubmissionDetailProps, _optional?: Record<string, never>): import("react").JSX.Element;
|
|
13
15
|
//# sourceMappingURL=FormSubmissionDetail.d.ts.map
|
|
@@ -4,7 +4,8 @@ import { agentHandle } from '@jini-ai/agentic';
|
|
|
4
4
|
import { useWiredFormSubmissionDetail } from '../hooks/use-form-submission-detail.hooks.js';
|
|
5
5
|
import { useFormSubmissionDate } from '../hooks/use-form-submission-date.hooks.js';
|
|
6
6
|
import { FormSubmissionDate } from "./FormSubmissionDate.js";
|
|
7
|
-
|
|
7
|
+
import { formSubmissionRows } from '../../html-rules.js';
|
|
8
|
+
export function FormSubmissionDetail({ formId, submissionId, onBack, onDeleted, useFormSubmissionDetailHook = useWiredFormSubmissionDetail, t, answerColumns = [], }, _optional = {}) {
|
|
8
9
|
const formatDate = useFormSubmissionDate({});
|
|
9
10
|
const { submission, error, confirmOpen, deleting, requestDelete, cancelDelete, confirmDelete } = useFormSubmissionDetailHook({
|
|
10
11
|
formId,
|
|
@@ -21,7 +22,7 @@ export function FormSubmissionDetail({ formId, submissionId, onBack, onDeleted,
|
|
|
21
22
|
// button sat right on top of the table and Delete sat right underneath it. Same
|
|
22
23
|
// flex-column-plus-gap idiom `.field-group` already uses elsewhere for vertical rhythm,
|
|
23
24
|
// rather than one-off margins on each button.
|
|
24
|
-
_jsxs("div", { className: "form-submission-detail", children: [_jsxs("button", { type: "button", className: "btn-secondary", onClick: onBack, "aria-label": t("Back to submissions"), ...agentHandle({ handle: "form-submission-back" }, { role: "link", label: "Back to this form's list of submissions" }), children: ["\u2190 ", t("Back")] }), error ? _jsx("div", { className: "notice error", children: error }) : null, _jsx("div", { className: "table-scroll", children: _jsx("table", { className: "list-table", children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("th", { children: t("Submitted at") }), _jsx("td", { children: _jsx(FormSubmissionDate, { date: formatDate({ iso: submission.submittedAt }) }) })] }), _jsxs("tr", { children: [_jsx("th", { children: t("Source IP") }), _jsx("td", { children: submission.sourceIp })] }),
|
|
25
|
+
_jsxs("div", { className: "form-submission-detail", children: [_jsxs("button", { type: "button", className: "btn-secondary", onClick: onBack, "aria-label": t("Back to submissions"), ...agentHandle({ handle: "form-submission-back" }, { role: "link", label: "Back to this form's list of submissions" }), children: ["\u2190 ", t("Back")] }), error ? _jsx("div", { className: "notice error", children: error }) : null, _jsx("div", { className: "table-scroll", children: _jsx("table", { className: "list-table", children: _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("th", { children: t("Submitted at") }), _jsx("td", { children: _jsx(FormSubmissionDate, { date: formatDate({ iso: submission.submittedAt }) }) })] }), _jsxs("tr", { children: [_jsx("th", { children: t("Source IP") }), _jsx("td", { children: submission.sourceIp })] }), formSubmissionRows({ data: submission.data, answerColumns }, { unknownFieldLabel: t("Unknown field") }).map(({ key, label, value }) => (_jsxs("tr", { children: [_jsx("th", { className: "jini-submission-field-label", children: label }), _jsx("td", { children: value })] }, key)))] }) }) }), _jsx("button", { type: "button", className: "btn-danger", disabled: deleting, onClick: requestDelete, ...agentHandle({ handle: "form-submission-delete" }, {
|
|
25
26
|
role: "button",
|
|
26
27
|
label: "Move this submission to the trash. Opens a confirmation dialog first.",
|
|
27
28
|
}), children: t("Delete submission") }), _jsx(ConfirmDialog, { open: confirmOpen, agentHandle: "form-submission-delete-confirm", title: t("Move to trash?"), body: _jsx("p", { children: t("It will disappear from this list. You can restore it from the Trash.") }), confirmLabel: t("Move to trash"), destructive: true, pending: deleting, onConfirm: confirmDelete, onCancel: cancelDelete })] }));
|
|
@@ -12,7 +12,7 @@ export function FormSubmissions({ formId, useFormSubmissionsHook = useWiredFormS
|
|
|
12
12
|
const { submissions, nextCursor, error, selectedId, loadingMore } = controller;
|
|
13
13
|
const { rows, back, loadMore } = useSubmissionsView({ controller });
|
|
14
14
|
if (selectedId) {
|
|
15
|
-
return (_jsx(FormSubmissionDetail, { formId: formId, submissionId: selectedId, onBack: back, onDeleted: back, t: t }));
|
|
15
|
+
return (_jsx(FormSubmissionDetail, { formId: formId, submissionId: selectedId, onBack: back, onDeleted: back, t: t, answerColumns: answerColumns }));
|
|
16
16
|
}
|
|
17
17
|
if (error && !submissions)
|
|
18
18
|
return _jsx("div", { className: "notice error", children: error });
|
|
@@ -5,7 +5,7 @@ import type { FieldAttributesDialogController } from './use-field-attributes-dia
|
|
|
5
5
|
import type { FormSubmissionsController } from './use-form-submissions.hooks.js';
|
|
6
6
|
import type { FormEditorFieldsBodyProps } from '../components/FormEditorFieldsBody.js';
|
|
7
7
|
import type { AdminFormDefinition, AdminFormField } from '../../models.js';
|
|
8
|
-
/**
|
|
8
|
+
/** Publication actions keep their original DOM at the host slot. */
|
|
9
9
|
export declare function useFormsListView({ forms, t, formatFormDates, toggleStatus, setPendingDelete }: {
|
|
10
10
|
forms: AdminFormDefinition[] | null;
|
|
11
11
|
t: (key: string) => string;
|
|
@@ -31,11 +31,6 @@ export declare function useFormsListView({ forms, t, formatFormDates, toggleStat
|
|
|
31
31
|
notify: import("../../models.js").AdminFormNotify;
|
|
32
32
|
}[];
|
|
33
33
|
cancelDelete: () => void;
|
|
34
|
-
RecipientLabel: import("react").ComponentType<{
|
|
35
|
-
count: number;
|
|
36
|
-
}> | (({ count }: {
|
|
37
|
-
count: number;
|
|
38
|
-
}) => string);
|
|
39
34
|
headerActions: import("react").ReactNode;
|
|
40
35
|
};
|
|
41
36
|
/** Bind every field-row callback and focus ref outside the markup. */
|
|
@@ -2,11 +2,10 @@ import { buildAgentListHandles } from '@jini-ai/agentic';
|
|
|
2
2
|
import { fieldDisplayName, formRowMenuItems, FORM_TABS } from '../../rules.js';
|
|
3
3
|
import { useFormsOptions, FormsPortsContext } from './FormsPorts.hooks.js';
|
|
4
4
|
import { useContext } from 'react';
|
|
5
|
-
/**
|
|
5
|
+
/** Publication actions keep their original DOM at the host slot. */
|
|
6
6
|
export function useFormsListView({ forms, t, formatFormDates, toggleStatus, setPendingDelete }, _optional = {}) {
|
|
7
7
|
const ports = useContext(FormsPortsContext);
|
|
8
|
-
const {
|
|
9
|
-
const RecipientLabel = slots?.RecipientLabel ?? (({ count }) => t(count === 1 ? 'recipient' : 'recipients'));
|
|
8
|
+
const { headerActions, adminBase } = useFormsOptions();
|
|
10
9
|
const rowHandles = buildAgentListHandles({ prefix: 'forms-row', ids: (forms ?? []).map(form => form.id) });
|
|
11
10
|
const onEdit = (form) => {
|
|
12
11
|
if (!ports?.formsNavigation)
|
|
@@ -17,7 +16,7 @@ export function useFormsListView({ forms, t, formatFormDates, toggleStatus, setP
|
|
|
17
16
|
rows: (forms ?? []).map((form, index) => ({ ...form, rowHandle: rowHandles[index], dates: formatFormDates(form),
|
|
18
17
|
menuLabel: t('Actions for form "{name}"').replace('{name}', form.name),
|
|
19
18
|
menuItems: formRowMenuItems({ form, handlers: { onEdit, onToggleStatus: form => { void toggleStatus(form); }, onDelete: setPendingDelete }, t }), })),
|
|
20
|
-
cancelDelete: () => setPendingDelete(null),
|
|
19
|
+
cancelDelete: () => setPendingDelete(null), headerActions,
|
|
21
20
|
};
|
|
22
21
|
}
|
|
23
22
|
/** Bind every field-row callback and focus ref outside the markup. */
|
|
@@ -86,8 +86,8 @@ export declare function forms(_required: Record<string, never>, options?: FormsR
|
|
|
86
86
|
readonly "No submissions yet.": "No submissions yet.";
|
|
87
87
|
readonly "Create form": "Create form";
|
|
88
88
|
readonly Form: "Form";
|
|
89
|
-
readonly "Configure a new form's fields
|
|
90
|
-
readonly "Configure this form's fields
|
|
89
|
+
readonly "Configure a new form's fields.": "Configure a new form's fields.";
|
|
90
|
+
readonly "Configure this form's fields, or review its submissions.": "Configure this form's fields, or review its submissions.";
|
|
91
91
|
readonly Submissions: "Submissions";
|
|
92
92
|
readonly "Back to forms": "Back to forms";
|
|
93
93
|
readonly "Actions for form \"{name}\"": "Actions for form \"{name}\"";
|
|
@@ -119,6 +119,7 @@ export declare function forms(_required: Record<string, never>, options?: FormsR
|
|
|
119
119
|
readonly "failed to load submissions": "failed to load submissions";
|
|
120
120
|
readonly "failed to load submission": "failed to load submission";
|
|
121
121
|
readonly "delete failed": "delete failed";
|
|
122
|
+
readonly "Unknown field": "Unknown field";
|
|
122
123
|
};
|
|
123
124
|
pages: {
|
|
124
125
|
readonly list: {
|
|
@@ -231,8 +232,8 @@ export declare function forms(_required: Record<string, never>, options?: FormsR
|
|
|
231
232
|
readonly "No submissions yet.": "No submissions yet.";
|
|
232
233
|
readonly "Create form": "Create form";
|
|
233
234
|
readonly Form: "Form";
|
|
234
|
-
readonly "Configure a new form's fields
|
|
235
|
-
readonly "Configure this form's fields
|
|
235
|
+
readonly "Configure a new form's fields.": "Configure a new form's fields.";
|
|
236
|
+
readonly "Configure this form's fields, or review its submissions.": "Configure this form's fields, or review its submissions.";
|
|
236
237
|
readonly Submissions: "Submissions";
|
|
237
238
|
readonly "Back to forms": "Back to forms";
|
|
238
239
|
readonly "Actions for form \"{name}\"": "Actions for form \"{name}\"";
|
|
@@ -264,6 +265,7 @@ export declare function forms(_required: Record<string, never>, options?: FormsR
|
|
|
264
265
|
readonly "failed to load submissions": "failed to load submissions";
|
|
265
266
|
readonly "failed to load submission": "failed to load submission";
|
|
266
267
|
readonly "delete failed": "delete failed";
|
|
268
|
+
readonly "Unknown field": "Unknown field";
|
|
267
269
|
};
|
|
268
270
|
readonly pages: {
|
|
269
271
|
readonly list: {
|
|
@@ -380,8 +382,8 @@ export declare function forms(_required: Record<string, never>, options?: FormsR
|
|
|
380
382
|
readonly "No submissions yet.": "No submissions yet.";
|
|
381
383
|
readonly "Create form": "Create form";
|
|
382
384
|
readonly Form: "Form";
|
|
383
|
-
readonly "Configure a new form's fields
|
|
384
|
-
readonly "Configure this form's fields
|
|
385
|
+
readonly "Configure a new form's fields.": "Configure a new form's fields.";
|
|
386
|
+
readonly "Configure this form's fields, or review its submissions.": "Configure this form's fields, or review its submissions.";
|
|
385
387
|
readonly Submissions: "Submissions";
|
|
386
388
|
readonly "Back to forms": "Back to forms";
|
|
387
389
|
readonly "Actions for form \"{name}\"": "Actions for form \"{name}\"";
|
|
@@ -413,6 +415,7 @@ export declare function forms(_required: Record<string, never>, options?: FormsR
|
|
|
413
415
|
readonly "failed to load submissions": "failed to load submissions";
|
|
414
416
|
readonly "failed to load submission": "failed to load submission";
|
|
415
417
|
readonly "delete failed": "delete failed";
|
|
418
|
+
readonly "Unknown field": "Unknown field";
|
|
416
419
|
};
|
|
417
420
|
readonly pages: {
|
|
418
421
|
readonly list: {
|
|
@@ -2,6 +2,9 @@ import { createElement } from 'react';
|
|
|
2
2
|
import { bindReact } from '../../core/react/bind-react.js';
|
|
3
3
|
import { formsModule } from '../forms.module.js';
|
|
4
4
|
import { FormsPortsContext, FormsOptionsContext } from './hooks/FormsPorts.hooks.js';
|
|
5
|
+
// The list must mount its bounded API read even when long-lived streams occupy every socket.
|
|
6
|
+
// A native dynamic import would queue before that read and leave the loading notice forever.
|
|
7
|
+
import { FormsList } from './pages/FormsList.js';
|
|
5
8
|
export { FormsPortsContext, FormsOptionsContext, useFormsPorts } from './hooks/FormsPorts.hooks.js';
|
|
6
9
|
export { useFormsList } from './hooks/use-forms-list.hooks.js';
|
|
7
10
|
export { useFormEditor } from './hooks/use-form-editor.hooks.js';
|
|
@@ -9,7 +12,6 @@ export { useFormEditor } from './hooks/use-form-editor.hooks.js';
|
|
|
9
12
|
export function forms(_required, options = {}) {
|
|
10
13
|
const react = bindReact({ module: formsModule, views: {
|
|
11
14
|
list: { page: async () => {
|
|
12
|
-
const { FormsList } = await import('./pages/FormsList.js');
|
|
13
15
|
return { default: (_props) => createElement(FormsList) };
|
|
14
16
|
}, tabs: {} },
|
|
15
17
|
editor: { page: async () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ConfirmDialog } from '../../../react/components/ConfirmDialog/ConfirmDialog.js';
|
|
3
3
|
import { DataTable } from '../../../react/components/DataTable.js';
|
|
4
4
|
import { RowMenu } from '../../../react/components/RowMenu/RowMenu.js';
|
|
@@ -7,7 +7,7 @@ import { useWiredFormsList } from '../hooks/use-forms-list.hooks.js';
|
|
|
7
7
|
import { useFormsListView } from '../hooks/forms-view.hooks.js';
|
|
8
8
|
export function FormsList({ useFormsListHook = useWiredFormsList } = {}, _optional = {}) {
|
|
9
9
|
const { forms, formatFormDates, error, rowSavingId, toggleStatus, pendingDelete, setPendingDelete, removeForm, t } = useFormsListHook();
|
|
10
|
-
const { rows, cancelDelete,
|
|
10
|
+
const { rows, cancelDelete, headerActions } = useFormsListView({ forms, t, formatFormDates, toggleStatus, setPendingDelete });
|
|
11
11
|
if (error && !forms)
|
|
12
12
|
return _jsx("div", { className: "notice error", children: error });
|
|
13
13
|
if (!forms)
|
|
@@ -39,11 +39,6 @@ export function FormsList({ useFormsListHook = useWiredFormsList } = {}, _option
|
|
|
39
39
|
cell: (form) => (_jsx("span", { className: "forms-dates", children: form.dates.map((line) => (_jsxs("time", { className: "forms-dates-line", dateTime: line.dateTime, title: line.label, children: [_jsx("span", { "aria-hidden": "true", children: line.text }), _jsx("span", { className: "visually-hidden", children: line.label })] }, line.kind))) })),
|
|
40
40
|
},
|
|
41
41
|
{ key: "fields", header: t("Fields"), cell: (form) => form.fields.length },
|
|
42
|
-
{
|
|
43
|
-
key: "notify",
|
|
44
|
-
header: t("Notify"),
|
|
45
|
-
cell: (form) => (form.notify.enabled ? _jsxs(_Fragment, { children: [form.notify.recipients.length, " ", _jsx(RecipientLabel, { count: form.notify.recipients.length })] }) : t("off")),
|
|
46
|
-
},
|
|
47
42
|
{
|
|
48
43
|
key: "actions",
|
|
49
44
|
header: t("More"),
|
|
@@ -87,7 +87,9 @@ export declare function useConfirmDialogCancelLabel(_requiredArgs: Record<string
|
|
|
87
87
|
* Focus moves to the *cancel* action on open, not confirm — an operator whose first keystroke after
|
|
88
88
|
* a slow read-through is Enter should land on the safe action, not the destructive one.
|
|
89
89
|
*
|
|
90
|
-
*
|
|
90
|
+
* Native modal lifecycle restores the opener; panel-kit's shared trap contains Tab/Shift+Tab,
|
|
91
|
+
* including in DOM environments using the open-attribute fallback, and handles nested traps.
|
|
92
|
+
* @complexity O(1) per open/close transition; Tab boundaries scan focusable dialog content.
|
|
91
93
|
*/
|
|
92
94
|
export declare function useConfirmDialog({ open, onCancel, document }: {
|
|
93
95
|
readonly open: boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useContext, useMemo, useRef, } from 'react';
|
|
3
3
|
import { useNativeDialog } from '@jini-ai/ui-kit/react/native';
|
|
4
|
+
import { useFocusTrap } from '@jini-ai/ui/panel-kit';
|
|
4
5
|
/** Empty object, not `undefined` — {@link useConfirmDialogCancelLabel} can always read `cancelLabel`
|
|
5
6
|
* off it without an extra null check, whether or not a provider is mounted. */
|
|
6
7
|
const ConfirmDialogDefaultsContext = createContext({});
|
|
@@ -51,7 +52,9 @@ export function useConfirmDialogCancelLabel(_requiredArgs, { explicit } = {}) {
|
|
|
51
52
|
* Focus moves to the *cancel* action on open, not confirm — an operator whose first keystroke after
|
|
52
53
|
* a slow read-through is Enter should land on the safe action, not the destructive one.
|
|
53
54
|
*
|
|
54
|
-
*
|
|
55
|
+
* Native modal lifecycle restores the opener; panel-kit's shared trap contains Tab/Shift+Tab,
|
|
56
|
+
* including in DOM environments using the open-attribute fallback, and handles nested traps.
|
|
57
|
+
* @complexity O(1) per open/close transition; Tab boundaries scan focusable dialog content.
|
|
55
58
|
*/
|
|
56
59
|
export function useConfirmDialog({ open, onCancel, document }, { pending } = {}) {
|
|
57
60
|
// `useId()`, not a string literal — a hardcoded id breaks the moment a screen mounts two
|
|
@@ -78,6 +81,7 @@ export function useConfirmDialog({ open, onCancel, document }, { pending } = {})
|
|
|
78
81
|
const cancelRef = useRef(null);
|
|
79
82
|
const vm = useNativeDialog({ open, title: '', onClose: () => onCancel(), ...(pending === undefined ? {} : { pending }) }, { document, initialFocus: () => cancelRef.current?.focus() });
|
|
80
83
|
const titleId = vm.titleId, dialogRef = vm.props.ref;
|
|
84
|
+
useFocusTrap({ containerRef: dialogRef }, { active: open });
|
|
81
85
|
const handleNativeCancel = vm.props.onCancel, handleBackdropClick = vm.props.onClick;
|
|
82
86
|
return { titleId, dialogRef, cancelRef, handleNativeCancel, handleBackdropClick };
|
|
83
87
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ConfirmDialogProps } from './ConfirmDialog.js';
|
|
2
|
+
/** Host copy and consequence styling; ConfirmDialog remains the owner of focus and dismissal. */
|
|
3
|
+
export type ConfirmRequestPresentation = Pick<ConfirmDialogProps, 'title' | 'body' | 'confirmLabel' | 'cancelLabel' | 'tone'>;
|
|
4
|
+
export interface ConfirmRequestController {
|
|
5
|
+
confirm: (required: {
|
|
6
|
+
dialog: ConfirmRequestPresentation;
|
|
7
|
+
}, optional?: Record<string, never>) => Promise<boolean>;
|
|
8
|
+
/** Keep the shared dialog mounted, including while closed. */
|
|
9
|
+
dialog: ConfirmDialogProps;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Adapts an async confirmation callback to the shared controlled ConfirmDialog.
|
|
13
|
+
* Replacing a request or unmounting denies the outstanding action; stale answers cannot accept
|
|
14
|
+
* a newer request. No destructive effect runs here: the caller acts only after receiving true.
|
|
15
|
+
* @param _required - No required host dependencies.
|
|
16
|
+
* @param _optional - Reserved options.
|
|
17
|
+
* @returns Promise confirmer and props for one always-mounted ConfirmDialog.
|
|
18
|
+
* @example const approval = useConfirmRequest({}); // <ConfirmDialog {...approval.dialog} />
|
|
19
|
+
* @complexity O(1) time and space per request; at most one unresolved request.
|
|
20
|
+
*/
|
|
21
|
+
export declare function useConfirmRequest(_required: Record<string, never>, _optional?: Record<string, never>): ConfirmRequestController;
|
|
22
|
+
//# sourceMappingURL=use-confirm-request.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Adapts an async confirmation callback to the shared controlled ConfirmDialog.
|
|
4
|
+
* Replacing a request or unmounting denies the outstanding action; stale answers cannot accept
|
|
5
|
+
* a newer request. No destructive effect runs here: the caller acts only after receiving true.
|
|
6
|
+
* @param _required - No required host dependencies.
|
|
7
|
+
* @param _optional - Reserved options.
|
|
8
|
+
* @returns Promise confirmer and props for one always-mounted ConfirmDialog.
|
|
9
|
+
* @example const approval = useConfirmRequest({}); // <ConfirmDialog {...approval.dialog} />
|
|
10
|
+
* @complexity O(1) time and space per request; at most one unresolved request.
|
|
11
|
+
*/
|
|
12
|
+
export function useConfirmRequest(_required, _optional = {}) {
|
|
13
|
+
const [request, setRequest] = useState(null);
|
|
14
|
+
const pending = useRef(null);
|
|
15
|
+
const mounted = useRef(true);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
mounted.current = true;
|
|
18
|
+
return () => {
|
|
19
|
+
mounted.current = false;
|
|
20
|
+
pending.current?.resolve(false);
|
|
21
|
+
pending.current = null;
|
|
22
|
+
};
|
|
23
|
+
}, []);
|
|
24
|
+
/** Opens one request, denying any replaced request; retained callbacks fail closed after unmount. */
|
|
25
|
+
const confirm = useCallback(({ dialog }, _options = {}) => {
|
|
26
|
+
if (!mounted.current)
|
|
27
|
+
return Promise.resolve(false);
|
|
28
|
+
pending.current?.resolve(false);
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
const next = { presentation: dialog, resolve };
|
|
31
|
+
pending.current = next;
|
|
32
|
+
setRequest(next);
|
|
33
|
+
});
|
|
34
|
+
}, []);
|
|
35
|
+
/** Only the dialog that opened this request may settle it, once; delayed older clicks are ignored. */
|
|
36
|
+
function settle(accepted) {
|
|
37
|
+
if (!request || pending.current !== request)
|
|
38
|
+
return;
|
|
39
|
+
pending.current = null;
|
|
40
|
+
setRequest(null);
|
|
41
|
+
request.resolve(accepted);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
confirm,
|
|
45
|
+
dialog: {
|
|
46
|
+
...(request?.presentation ?? { title: '', body: '', confirmLabel: '' }),
|
|
47
|
+
open: request !== null,
|
|
48
|
+
onConfirm: () => settle(true),
|
|
49
|
+
onCancel: () => settle(false),
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=use-confirm-request.js.map
|