@jini-ai/admin 0.4.3 → 0.4.4
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/pages/FormsList.js +2 -7
- package/dist/react/components/RowMenu/RowMenu.hooks.js +14 -3
- 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 +5 -5
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: {
|
|
@@ -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"),
|
|
@@ -12,13 +12,14 @@ const VIEWPORT_MARGIN = 8;
|
|
|
12
12
|
* visible jump. Recomputed on scroll and resize too: a portaled menu is a DOM sibling of its anchor,
|
|
13
13
|
* not a child, so it does not track the anchor's position on its own the way an in-flow popup would.
|
|
14
14
|
*
|
|
15
|
-
* Click-outside
|
|
15
|
+
* Click-outside and Escape close. Escape is document-scoped so it also works after focus
|
|
16
|
+
* moves away from the popup. Scoped to the open window only, same lifecycle discipline as
|
|
16
17
|
* `ConfirmButton`'s armed-only document listeners — a closed, idle `RowMenu` costs nothing beyond
|
|
17
18
|
* its trigger button even with many mounted per table.
|
|
18
19
|
*
|
|
19
20
|
* @complexity O(1) per call and per scroll/resize/mousedown event.
|
|
20
21
|
*/
|
|
21
|
-
function trackOpenMenu({ trigger, menu }, { window, document, onPosition, onDismiss }) {
|
|
22
|
+
function trackOpenMenu({ trigger, menu }, { window, document, onPosition, onDismiss, onEscape }) {
|
|
22
23
|
function reposition() {
|
|
23
24
|
const rect = trigger.getBoundingClientRect();
|
|
24
25
|
const menuHeight = menu.offsetHeight;
|
|
@@ -41,14 +42,23 @@ function trackOpenMenu({ trigger, menu }, { window, document, onPosition, onDism
|
|
|
41
42
|
return;
|
|
42
43
|
onDismiss();
|
|
43
44
|
}
|
|
45
|
+
function onDocKeyDown(e) {
|
|
46
|
+
if (e.key !== 'Escape' || e.defaultPrevented)
|
|
47
|
+
return;
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
e.stopPropagation();
|
|
50
|
+
onEscape();
|
|
51
|
+
}
|
|
44
52
|
reposition();
|
|
45
53
|
window.addEventListener('scroll', reposition, true);
|
|
46
54
|
window.addEventListener('resize', reposition);
|
|
47
55
|
document.addEventListener('mousedown', onDocMouseDown);
|
|
56
|
+
document.addEventListener('keydown', onDocKeyDown);
|
|
48
57
|
return () => {
|
|
49
58
|
window.removeEventListener('scroll', reposition, true);
|
|
50
59
|
window.removeEventListener('resize', reposition);
|
|
51
60
|
document.removeEventListener('mousedown', onDocMouseDown);
|
|
61
|
+
document.removeEventListener('keydown', onDocKeyDown);
|
|
52
62
|
};
|
|
53
63
|
}
|
|
54
64
|
/**
|
|
@@ -89,7 +99,7 @@ export function useRowMenu({ itemCount, window, document }) {
|
|
|
89
99
|
let stop;
|
|
90
100
|
return (menu) => {
|
|
91
101
|
if (menu)
|
|
92
|
-
stop = trackOpenMenu({ trigger: anchor, menu }, { window, document, onPosition: setPosition, onDismiss: () => close(false) });
|
|
102
|
+
stop = trackOpenMenu({ trigger: anchor, menu }, { window, document, onPosition: setPosition, onDismiss: () => close(false), onEscape: () => close(true) });
|
|
93
103
|
else
|
|
94
104
|
stop();
|
|
95
105
|
};
|
|
@@ -140,6 +150,7 @@ export function useRowMenu({ itemCount, window, document }) {
|
|
|
140
150
|
break;
|
|
141
151
|
case 'Escape':
|
|
142
152
|
e.preventDefault();
|
|
153
|
+
e.stopPropagation();
|
|
143
154
|
close(true);
|
|
144
155
|
break;
|
|
145
156
|
case 'Tab':
|
|
@@ -9,8 +9,7 @@ export declare const redirectsMessagesEn: {
|
|
|
9
9
|
readonly Import: "Import";
|
|
10
10
|
readonly Marketing: "Marketing";
|
|
11
11
|
readonly Redirects: "Redirects";
|
|
12
|
-
readonly "
|
|
13
|
-
readonly ") also show up here.": ") also show up here.";
|
|
12
|
+
readonly "Manage URL redirects, including those created automatically when a page's address changes.": "Manage URL redirects, including those created automatically when a page's address changes.";
|
|
14
13
|
readonly "Match type": "Match type";
|
|
15
14
|
readonly "From path": "From path";
|
|
16
15
|
readonly "To target": "To target";
|
|
@@ -31,9 +30,9 @@ export declare const redirectsMessagesEn: {
|
|
|
31
30
|
readonly "Loading redirects\u2026": "Loading redirects…";
|
|
32
31
|
readonly "Not valid JSON.": "Not valid JSON.";
|
|
33
32
|
readonly "Must be a JSON array of rule objects.": "Must be a JSON array of rule objects.";
|
|
34
|
-
readonly exact: "
|
|
35
|
-
readonly prefix: "
|
|
36
|
-
readonly wildcard: "
|
|
33
|
+
readonly exact: "Exact match";
|
|
34
|
+
readonly prefix: "Starts with";
|
|
35
|
+
readonly wildcard: "Wildcard";
|
|
37
36
|
readonly Status: "Status";
|
|
38
37
|
readonly More: "More";
|
|
39
38
|
readonly Delete: "Delete";
|
|
@@ -41,8 +40,8 @@ export declare const redirectsMessagesEn: {
|
|
|
41
40
|
readonly Enable: "Enable";
|
|
42
41
|
readonly "Saving\u2026": "Saving…";
|
|
43
42
|
readonly Created: "Created";
|
|
44
|
-
readonly active: "
|
|
45
|
-
readonly disabled: "
|
|
43
|
+
readonly active: "Active";
|
|
44
|
+
readonly disabled: "Disabled";
|
|
46
45
|
readonly "{created} created, {failed} failed.": "{created} created, {failed} failed.";
|
|
47
46
|
readonly "Item {index} ({code})": "Item {index} ({code})";
|
|
48
47
|
readonly "Actions for redirect rule from \"{fromPattern}\"": "Actions for redirect rule from \"{fromPattern}\"";
|
|
@@ -50,6 +49,9 @@ export declare const redirectsMessagesEn: {
|
|
|
50
49
|
readonly " rule objects (1-500 items)": " rule objects (1-500 items)";
|
|
51
50
|
readonly "Delete the redirect rule from \"": "Delete the redirect rule from \"";
|
|
52
51
|
readonly "\"?": "\"?";
|
|
52
|
+
readonly manual: "Manual";
|
|
53
|
+
readonly auto_slug_change: "URL change";
|
|
54
|
+
readonly import: "Imported";
|
|
53
55
|
};
|
|
54
56
|
/** English fallback with the same named-placeholder interpolation as the host dictionary. */
|
|
55
57
|
export declare const translateRedirectsEn: RedirectsTranslate;
|
|
@@ -8,8 +8,7 @@ export const redirectsMessagesEn = {
|
|
|
8
8
|
"Import": "Import",
|
|
9
9
|
"Marketing": "Marketing",
|
|
10
10
|
"Redirects": "Redirects",
|
|
11
|
-
"
|
|
12
|
-
") also show up here.": ") also show up here.",
|
|
11
|
+
"Manage URL redirects, including those created automatically when a page's address changes.": "Manage URL redirects, including those created automatically when a page's address changes.",
|
|
13
12
|
"Match type": "Match type",
|
|
14
13
|
"From path": "From path",
|
|
15
14
|
"To target": "To target",
|
|
@@ -30,9 +29,9 @@ export const redirectsMessagesEn = {
|
|
|
30
29
|
"Loading redirects…": "Loading redirects…",
|
|
31
30
|
"Not valid JSON.": "Not valid JSON.",
|
|
32
31
|
"Must be a JSON array of rule objects.": "Must be a JSON array of rule objects.",
|
|
33
|
-
"exact": "
|
|
34
|
-
"prefix": "
|
|
35
|
-
"wildcard": "
|
|
32
|
+
"exact": "Exact match",
|
|
33
|
+
"prefix": "Starts with",
|
|
34
|
+
"wildcard": "Wildcard",
|
|
36
35
|
"Status": "Status",
|
|
37
36
|
"More": "More",
|
|
38
37
|
"Delete": "Delete",
|
|
@@ -40,16 +39,22 @@ export const redirectsMessagesEn = {
|
|
|
40
39
|
"Enable": "Enable",
|
|
41
40
|
"Saving…": "Saving…",
|
|
42
41
|
"Created": "Created",
|
|
43
|
-
"active": "
|
|
44
|
-
"disabled": "
|
|
42
|
+
"active": "Active",
|
|
43
|
+
"disabled": "Disabled",
|
|
45
44
|
"{created} created, {failed} failed.": "{created} created, {failed} failed.",
|
|
46
45
|
"Item {index} ({code})": "Item {index} ({code})",
|
|
47
46
|
"Actions for redirect rule from \"{fromPattern}\"": "Actions for redirect rule from \"{fromPattern}\"",
|
|
48
47
|
"Paste a JSON array of ": "Paste a JSON array of ",
|
|
49
48
|
" rule objects (1-500 items)": " rule objects (1-500 items)",
|
|
50
49
|
"Delete the redirect rule from \"": "Delete the redirect rule from \"",
|
|
51
|
-
"\"?": "\"?"
|
|
50
|
+
"\"?": "\"?",
|
|
51
|
+
"manual": "Manual",
|
|
52
|
+
"auto_slug_change": "URL change",
|
|
53
|
+
"import": "Imported",
|
|
52
54
|
};
|
|
53
55
|
/** English fallback with the same named-placeholder interpolation as the host dictionary. */
|
|
54
|
-
export const translateRedirectsEn = (key, vars = {}) =>
|
|
56
|
+
export const translateRedirectsEn = (key, vars = {}) => {
|
|
57
|
+
const template = Object.hasOwn(redirectsMessagesEn, key) ? redirectsMessagesEn[key] : key;
|
|
58
|
+
return template.replace(/\{(\w+)\}/g, (match, name) => String(vars[name] ?? match));
|
|
59
|
+
};
|
|
55
60
|
//# sourceMappingURL=messages.en.js.map
|
|
@@ -12,9 +12,9 @@ export function useRedirectsPage({ useRedirectsHook = useWiredRedirects }, _opti
|
|
|
12
12
|
const columns = [
|
|
13
13
|
{ key: 'from', header: t('From'), cell: (rule) => rule.fromPattern },
|
|
14
14
|
{ key: 'to', header: t('To'), cell: (rule) => rule.toTarget },
|
|
15
|
-
{ key: 'type', header: t('Type'), cell: (rule) => rule.matchType },
|
|
15
|
+
{ key: 'type', header: t('Type'), cell: (rule) => t(rule.matchType, { serverLabel: rule.matchType }) },
|
|
16
16
|
{ key: 'code', header: t('Code'), cell: (rule) => rule.statusCode },
|
|
17
|
-
{ key: 'source', header: t('Source'), cell: (rule) => rule.source },
|
|
17
|
+
{ key: 'source', header: t('Source'), cell: (rule) => t(rule.source, { serverLabel: rule.source }) },
|
|
18
18
|
// The host's shared serverLabel owns enum localization, including unknown-value passthrough.
|
|
19
19
|
{ key: 'status', header: t('Status'), cell: (rule) => createElement('span', { className: `status status-${rule.status}` }, t(rule.status, { serverLabel: rule.status })) },
|
|
20
20
|
{ key: 'hits', header: t('Hits'), cell: (rule, index) => createElement(HitCountCell, { t, redirectId: rule.id, ...(rowMenuHandles[index] ? { agentHandleBase: rowMenuHandles[index] } : {}) }) },
|