@dani-builder/strapi-plugin-builder 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/api.d.ts +3 -2
- package/dist/admin/api.d.ts.map +1 -1
- package/dist/admin/form-editor-page.d.ts.map +1 -1
- package/dist/admin/form.d.ts +24 -2
- package/dist/admin/form.d.ts.map +1 -1
- package/dist/admin/index.mjs +626 -62
- package/dist/admin/notification-editor.d.ts +16 -0
- package/dist/admin/notification-editor.d.ts.map +1 -0
- package/dist/admin/notifications.d.ts +8 -0
- package/dist/admin/notifications.d.ts.map +1 -0
- package/dist/admin/ui.d.ts +3 -1
- package/dist/admin/ui.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/admin/api.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { useFetchClient } from "@strapi/strapi/admin";
|
|
2
|
-
import type { FormDocument, FormDraft, FormField, FormTarget } from "./form";
|
|
2
|
+
import type { Delivery, FormDocument, FormDraft, FormField, FormTarget } from "./form";
|
|
3
3
|
type Client = ReturnType<typeof useFetchClient>;
|
|
4
4
|
export declare function formApi(client: Client): {
|
|
5
5
|
list(): Promise<FormDocument[]>;
|
|
6
6
|
get(id: string): Promise<FormDocument>;
|
|
7
7
|
targets(): Promise<FormTarget[]>;
|
|
8
8
|
fields(targetUid: string): Promise<FormField[]>;
|
|
9
|
-
save(form: FormDraft, id?: string): Promise<FormDocument>;
|
|
9
|
+
save(form: FormDraft, id?: string, includeNotifications?: boolean): Promise<FormDocument>;
|
|
10
|
+
testSend(id: string, sampleFields: Record<string, unknown>): Promise<Delivery[]>;
|
|
10
11
|
remove(id: string): Promise<void>;
|
|
11
12
|
export(id: string): Promise<Record<string, unknown>>;
|
|
12
13
|
import(data: Record<string, unknown>, id?: string): Promise<FormDocument>;
|
package/dist/admin/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../admin/src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../admin/src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,SAAS,EACT,UAAU,EACX,MAAM,QAAQ,CAAC;AAEhB,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAEhD,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM;;YAKpB,MAAM;;sBAWI,MAAM;eAOb,SAAS,OAAO,MAAM;iBAWpB,MAAM,gBAAgB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;eAQ/C,MAAM;eAGN,MAAM;iBAOJ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,MAAM;EAa1D;AACD,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,QASxE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-editor-page.d.ts","sourceRoot":"","sources":["../../admin/src/form-editor-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"form-editor-page.d.ts","sourceRoot":"","sources":["../../admin/src/form-editor-page.tsx"],"names":[],"mappings":"AAWA,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,OAAO,CAAA;CAAE,+BA+TnE"}
|
package/dist/admin/form.d.ts
CHANGED
|
@@ -14,7 +14,29 @@ export interface FormField {
|
|
|
14
14
|
min?: number;
|
|
15
15
|
max?: number;
|
|
16
16
|
}
|
|
17
|
+
export interface NotificationConfig {
|
|
18
|
+
email?: {
|
|
19
|
+
to: string[];
|
|
20
|
+
cc?: string[];
|
|
21
|
+
replyToField?: string;
|
|
22
|
+
subject: string;
|
|
23
|
+
body: string;
|
|
24
|
+
};
|
|
25
|
+
webhook?: {
|
|
26
|
+
url: string;
|
|
27
|
+
secret?: string;
|
|
28
|
+
headers?: Record<string, string>;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface Delivery {
|
|
32
|
+
channel: "email" | "webhook" | "afterSave";
|
|
33
|
+
status: "sent" | "failed";
|
|
34
|
+
attempts: number;
|
|
35
|
+
lastError: string | null;
|
|
36
|
+
sentAt: string | null;
|
|
37
|
+
}
|
|
17
38
|
export interface FormDraft {
|
|
39
|
+
notifications?: NotificationConfig;
|
|
18
40
|
slug: string;
|
|
19
41
|
name: string;
|
|
20
42
|
targetUid: string;
|
|
@@ -33,8 +55,8 @@ export interface FormTarget {
|
|
|
33
55
|
name: string;
|
|
34
56
|
}
|
|
35
57
|
export declare const emptyForm: FormDraft;
|
|
36
|
-
/**
|
|
37
|
-
export declare function savePayload(form: FormDraft): FormDraft;
|
|
58
|
+
/** Only send notifications after the editor has been opened. */
|
|
59
|
+
export declare function savePayload(form: FormDraft, includeNotifications?: boolean): FormDraft;
|
|
38
60
|
export declare function moveField(fields: FormField[], index: number, direction: number): FormField[];
|
|
39
61
|
export declare function setRequired(field: FormField, required: boolean, schema: FormField): FormField;
|
|
40
62
|
export declare function parseImport(source: string): Record<string, unknown>;
|
package/dist/admin/form.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../admin/src/form.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AACD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AACD,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AACD,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AACD,eAAO,MAAM,SAAS,EAAE,SAMvB,CAAC;AACF,
|
|
1
|
+
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../admin/src/form.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AACD,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE;QACN,EAAE,EAAE,MAAM,EAAE,CAAC;QACb,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;QACd,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,OAAO,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;CAC9E;AACD,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC;IAC3C,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AACD,MAAM,WAAW,SAAS;IACxB,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7C;AACD,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AACD,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AACD,eAAO,MAAM,SAAS,EAAE,SAMvB,CAAC;AACF,gEAAgE;AAChE,wBAAgB,WAAW,CACzB,IAAI,EAAE,SAAS,EACf,oBAAoB,UAAQ,GAC3B,SAAS,CAqBX;AACD,wBAAgB,SAAS,CACvB,MAAM,EAAE,SAAS,EAAE,EACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,SAAS,EAAE,CAQb;AACD,wBAAgB,WAAW,CACzB,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,OAAO,EACjB,MAAM,EAAE,SAAS,GAChB,SAAS,CAEX;AACD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUnE;AACD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAUnD"}
|
package/dist/admin/index.mjs
CHANGED
|
@@ -14,8 +14,14 @@ var __export = (target, all) => {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
// admin/src/form.ts
|
|
17
|
-
function savePayload(form) {
|
|
17
|
+
function savePayload(form, includeNotifications = false) {
|
|
18
|
+
const notifications = structuredClone(form.notifications ?? {});
|
|
19
|
+
if (notifications.email) {
|
|
20
|
+
notifications.email.to = notifications.email.to.map((value) => value.trim()).filter(Boolean);
|
|
21
|
+
notifications.email.cc = notifications.email.cc?.map((value) => value.trim()).filter(Boolean);
|
|
22
|
+
}
|
|
18
23
|
return {
|
|
24
|
+
...includeNotifications ? { notifications } : {},
|
|
19
25
|
slug: form.slug,
|
|
20
26
|
name: form.name,
|
|
21
27
|
targetUid: form.targetUid,
|
|
@@ -89,13 +95,19 @@ function formApi(client) {
|
|
|
89
95
|
params: { targetUid }
|
|
90
96
|
})).data.data;
|
|
91
97
|
},
|
|
92
|
-
async save(form, id) {
|
|
93
|
-
const body = { data: savePayload(form) };
|
|
98
|
+
async save(form, id, includeNotifications = false) {
|
|
99
|
+
const body = { data: savePayload(form, includeNotifications) };
|
|
94
100
|
return (id ? await client.put(
|
|
95
101
|
`${root}/${encodeURIComponent(id)}`,
|
|
96
102
|
body
|
|
97
103
|
) : await client.post(root, body)).data.data;
|
|
98
104
|
},
|
|
105
|
+
async testSend(id, sampleFields) {
|
|
106
|
+
return (await client.post(
|
|
107
|
+
`${root}/${encodeURIComponent(id)}/test-send`,
|
|
108
|
+
{ data: sampleFields }
|
|
109
|
+
)).data.data;
|
|
110
|
+
},
|
|
99
111
|
async remove(id) {
|
|
100
112
|
await client.del(`${root}/${encodeURIComponent(id)}`);
|
|
101
113
|
},
|
|
@@ -134,6 +146,7 @@ var init_api = __esm({
|
|
|
134
146
|
});
|
|
135
147
|
|
|
136
148
|
// admin/src/ui.tsx
|
|
149
|
+
import { useId } from "react";
|
|
137
150
|
import { Box, Flex, Typography } from "@strapi/design-system";
|
|
138
151
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
139
152
|
function Section({
|
|
@@ -151,9 +164,13 @@ function TextField({
|
|
|
151
164
|
onChange,
|
|
152
165
|
disabled,
|
|
153
166
|
required,
|
|
154
|
-
multiline
|
|
167
|
+
multiline,
|
|
168
|
+
secret,
|
|
169
|
+
error
|
|
155
170
|
}) {
|
|
171
|
+
const errorId = useId();
|
|
156
172
|
const props = {
|
|
173
|
+
"aria-describedby": error ? errorId : void 0,
|
|
157
174
|
value,
|
|
158
175
|
disabled,
|
|
159
176
|
required,
|
|
@@ -170,7 +187,16 @@ function TextField({
|
|
|
170
187
|
};
|
|
171
188
|
return /* @__PURE__ */ jsxs2("label", { style: { display: "grid", gap: 6 }, children: [
|
|
172
189
|
/* @__PURE__ */ jsx2(Typography, { children: label }),
|
|
173
|
-
multiline ? /* @__PURE__ */ jsx2("textarea", { ...props, rows: 3 }) : /* @__PURE__ */ jsx2(
|
|
190
|
+
multiline ? /* @__PURE__ */ jsx2("textarea", { ...props, rows: 3, "aria-invalid": Boolean(error) }) : /* @__PURE__ */ jsx2(
|
|
191
|
+
"input",
|
|
192
|
+
{
|
|
193
|
+
...props,
|
|
194
|
+
type: secret ? "password" : "text",
|
|
195
|
+
autoComplete: secret ? "new-password" : void 0,
|
|
196
|
+
"aria-invalid": Boolean(error)
|
|
197
|
+
}
|
|
198
|
+
),
|
|
199
|
+
error && /* @__PURE__ */ jsx2(Typography, { id: errorId, textColor: "danger700", role: "alert", children: error })
|
|
174
200
|
] });
|
|
175
201
|
}
|
|
176
202
|
function ErrorNotice({ message }) {
|
|
@@ -618,32 +644,537 @@ var init_field_editor = __esm({
|
|
|
618
644
|
}
|
|
619
645
|
});
|
|
620
646
|
|
|
621
|
-
// admin/src/
|
|
622
|
-
|
|
647
|
+
// admin/src/notifications.ts
|
|
648
|
+
function placeholders(fields) {
|
|
649
|
+
return [
|
|
650
|
+
...fields.map((field) => `field.${field.name}`),
|
|
651
|
+
"form.name",
|
|
652
|
+
"submission.id",
|
|
653
|
+
"submission.receivedAt"
|
|
654
|
+
];
|
|
655
|
+
}
|
|
656
|
+
function templateError(value, fields) {
|
|
657
|
+
const allowed = placeholders(fields);
|
|
658
|
+
let invalid = false;
|
|
659
|
+
const rest = value.replace(/{{\s*([^{}]+?)\s*}}/g, (_, key) => {
|
|
660
|
+
if (!allowed.includes(key)) invalid = true;
|
|
661
|
+
return "";
|
|
662
|
+
});
|
|
663
|
+
return invalid || rest.includes("{{") || rest.includes("}}") ? "Use only the available placeholders." : "";
|
|
664
|
+
}
|
|
665
|
+
function notificationErrors(config, fields) {
|
|
666
|
+
const errors = {};
|
|
667
|
+
if (config.email) {
|
|
668
|
+
if (config.email.replyToField && !fields.some(
|
|
669
|
+
(field) => field.type === "email" && field.name === config.email?.replyToField
|
|
670
|
+
))
|
|
671
|
+
errors.replyToField = "Choose an email field in this form, or None.";
|
|
672
|
+
for (const key of ["subject", "body"]) {
|
|
673
|
+
const error = templateError(config.email[key], fields);
|
|
674
|
+
if (error) errors[key] = error;
|
|
675
|
+
}
|
|
676
|
+
for (const key of ["to", "cc"]) {
|
|
677
|
+
if (config.email[key]?.some((value) => /[{}]/.test(value)))
|
|
678
|
+
errors[key] = "Recipients must be fixed email addresses; placeholders are not allowed.";
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (config.webhook) {
|
|
682
|
+
try {
|
|
683
|
+
const url = new URL(config.webhook.url);
|
|
684
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password)
|
|
685
|
+
throw Error("url");
|
|
686
|
+
} catch {
|
|
687
|
+
errors.url = "Enter an HTTP or HTTPS URL without credentials.";
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return errors;
|
|
691
|
+
}
|
|
692
|
+
function samplePayload(fields, values) {
|
|
693
|
+
return Object.fromEntries(
|
|
694
|
+
fields.flatMap((field) => {
|
|
695
|
+
const value = values[field.name] ?? "";
|
|
696
|
+
if (!value && !field.required) return [];
|
|
697
|
+
return [
|
|
698
|
+
[
|
|
699
|
+
field.name,
|
|
700
|
+
value && ["integer", "decimal"].includes(field.type) ? Number(value) : value && field.type === "boolean" ? value === "true" : value
|
|
701
|
+
]
|
|
702
|
+
];
|
|
703
|
+
})
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
function sampleErrors(error) {
|
|
707
|
+
const response = error && typeof error === "object" && "response" in error ? error.response : void 0;
|
|
708
|
+
const details = response?.data?.error?.details?.errors;
|
|
709
|
+
if (!Array.isArray(details)) return {};
|
|
710
|
+
return Object.fromEntries(
|
|
711
|
+
details.flatMap((entry) => {
|
|
712
|
+
if (!entry || typeof entry !== "object" || !("field" in entry) || !("code" in entry) || typeof entry.field !== "string" || typeof entry.code !== "string")
|
|
713
|
+
return [];
|
|
714
|
+
return [[entry.field, entry.code]];
|
|
715
|
+
})
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
var init_notifications = __esm({
|
|
719
|
+
"admin/src/notifications.ts"() {
|
|
720
|
+
"use strict";
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
// admin/src/notification-editor.tsx
|
|
725
|
+
import { useState as useState3 } from "react";
|
|
623
726
|
import { Button as Button4, Flex as Flex5, Typography as Typography5 } from "@strapi/design-system";
|
|
727
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
728
|
+
function NotificationEditor({
|
|
729
|
+
value,
|
|
730
|
+
fields,
|
|
731
|
+
opened,
|
|
732
|
+
onOpen,
|
|
733
|
+
onChange,
|
|
734
|
+
disabled,
|
|
735
|
+
saveError
|
|
736
|
+
}) {
|
|
737
|
+
const [target, setTarget] = useState3("body");
|
|
738
|
+
const errors = notificationErrors(value, fields);
|
|
739
|
+
function toggle(channel, enabled) {
|
|
740
|
+
const next = { ...value };
|
|
741
|
+
if (!enabled) delete next[channel];
|
|
742
|
+
else if (channel === "email")
|
|
743
|
+
next.email = {
|
|
744
|
+
to: [],
|
|
745
|
+
cc: [],
|
|
746
|
+
subject: "New submission: {{form.name}}",
|
|
747
|
+
body: "Submission {{submission.id}} received at {{submission.receivedAt}}"
|
|
748
|
+
};
|
|
749
|
+
else next.webhook = { url: "" };
|
|
750
|
+
onChange(next);
|
|
751
|
+
}
|
|
752
|
+
return /* @__PURE__ */ jsxs6(Section, { title: "Notifications", children: [
|
|
753
|
+
/* @__PURE__ */ jsx6(
|
|
754
|
+
Button4,
|
|
755
|
+
{
|
|
756
|
+
type: "button",
|
|
757
|
+
variant: "tertiary",
|
|
758
|
+
onClick: onOpen,
|
|
759
|
+
disabled: opened,
|
|
760
|
+
children: opened ? "Notification settings opened" : "Open notification settings"
|
|
761
|
+
}
|
|
762
|
+
),
|
|
763
|
+
opened && /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
764
|
+
/* @__PURE__ */ jsx6(Typography5, { variant: "pi", children: "Enable a channel to send notifications. Changes take effect after saving the form." }),
|
|
765
|
+
saveError.startsWith("notifications:") && /* @__PURE__ */ jsx6(ErrorNotice, { message: saveError }),
|
|
766
|
+
/* @__PURE__ */ jsxs6("label", { children: [
|
|
767
|
+
/* @__PURE__ */ jsx6(
|
|
768
|
+
"input",
|
|
769
|
+
{
|
|
770
|
+
type: "checkbox",
|
|
771
|
+
disabled,
|
|
772
|
+
checked: Boolean(value.email),
|
|
773
|
+
onChange: (event) => toggle("email", event.target.checked)
|
|
774
|
+
}
|
|
775
|
+
),
|
|
776
|
+
" ",
|
|
777
|
+
"Email notifications"
|
|
778
|
+
] }),
|
|
779
|
+
value.email && /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
780
|
+
["to", "cc"].map((key) => /* @__PURE__ */ jsx6(
|
|
781
|
+
TextField,
|
|
782
|
+
{
|
|
783
|
+
label: key === "to" ? "To (one address per line)" : "Cc (one address per line)",
|
|
784
|
+
multiline: true,
|
|
785
|
+
value: (value.email?.[key] ?? []).join("\n"),
|
|
786
|
+
disabled,
|
|
787
|
+
error: errors[key],
|
|
788
|
+
onChange: (text) => onChange({
|
|
789
|
+
...value,
|
|
790
|
+
email: { ...value.email, [key]: text.split("\n") }
|
|
791
|
+
})
|
|
792
|
+
},
|
|
793
|
+
key
|
|
794
|
+
)),
|
|
795
|
+
/* @__PURE__ */ jsx6(Typography5, { variant: "pi", children: "Use fixed email addresses. Empty lines are ignored when saving." }),
|
|
796
|
+
/* @__PURE__ */ jsxs6("label", { children: [
|
|
797
|
+
"Reply-to field",
|
|
798
|
+
/* @__PURE__ */ jsxs6(
|
|
799
|
+
"select",
|
|
800
|
+
{
|
|
801
|
+
style: { width: "100%", padding: 10 },
|
|
802
|
+
disabled,
|
|
803
|
+
value: value.email.replyToField ?? "",
|
|
804
|
+
"aria-invalid": Boolean(errors.replyToField),
|
|
805
|
+
onChange: (event) => {
|
|
806
|
+
const email = { ...value.email };
|
|
807
|
+
delete email.replyToField;
|
|
808
|
+
if (event.target.value)
|
|
809
|
+
email.replyToField = event.target.value;
|
|
810
|
+
onChange({ ...value, email });
|
|
811
|
+
},
|
|
812
|
+
children: [
|
|
813
|
+
/* @__PURE__ */ jsx6("option", { value: "", children: "None" }),
|
|
814
|
+
fields.filter((field) => field.type === "email").map((field) => /* @__PURE__ */ jsxs6("option", { value: field.name, children: [
|
|
815
|
+
field.label,
|
|
816
|
+
" (",
|
|
817
|
+
field.name,
|
|
818
|
+
")"
|
|
819
|
+
] }, field.name))
|
|
820
|
+
]
|
|
821
|
+
}
|
|
822
|
+
)
|
|
823
|
+
] }),
|
|
824
|
+
errors.replyToField && /* @__PURE__ */ jsx6(ErrorNotice, { message: errors.replyToField }),
|
|
825
|
+
/* @__PURE__ */ jsx6(
|
|
826
|
+
TextField,
|
|
827
|
+
{
|
|
828
|
+
label: "Email subject",
|
|
829
|
+
value: value.email.subject,
|
|
830
|
+
disabled,
|
|
831
|
+
error: errors.subject,
|
|
832
|
+
onChange: (subject) => onChange({ ...value, email: { ...value.email, subject } })
|
|
833
|
+
}
|
|
834
|
+
),
|
|
835
|
+
/* @__PURE__ */ jsx6(
|
|
836
|
+
TextField,
|
|
837
|
+
{
|
|
838
|
+
label: "Email body",
|
|
839
|
+
multiline: true,
|
|
840
|
+
value: value.email.body,
|
|
841
|
+
disabled,
|
|
842
|
+
error: errors.body,
|
|
843
|
+
onChange: (body) => onChange({ ...value, email: { ...value.email, body } })
|
|
844
|
+
}
|
|
845
|
+
),
|
|
846
|
+
/* @__PURE__ */ jsxs6("label", { children: [
|
|
847
|
+
"Insert placeholder into",
|
|
848
|
+
/* @__PURE__ */ jsxs6(
|
|
849
|
+
"select",
|
|
850
|
+
{
|
|
851
|
+
style: { width: "100%", padding: 10 },
|
|
852
|
+
disabled,
|
|
853
|
+
value: target,
|
|
854
|
+
onChange: (event) => setTarget(
|
|
855
|
+
event.target.value === "subject" ? "subject" : "body"
|
|
856
|
+
),
|
|
857
|
+
children: [
|
|
858
|
+
/* @__PURE__ */ jsx6("option", { value: "subject", children: "Subject" }),
|
|
859
|
+
/* @__PURE__ */ jsx6("option", { value: "body", children: "Body" })
|
|
860
|
+
]
|
|
861
|
+
}
|
|
862
|
+
)
|
|
863
|
+
] }),
|
|
864
|
+
/* @__PURE__ */ jsx6(Flex5, { gap: 2, wrap: "wrap", children: placeholders(fields).map((key) => /* @__PURE__ */ jsx6(
|
|
865
|
+
Button4,
|
|
866
|
+
{
|
|
867
|
+
type: "button",
|
|
868
|
+
style: {
|
|
869
|
+
maxWidth: "100%",
|
|
870
|
+
height: "auto",
|
|
871
|
+
whiteSpace: "normal",
|
|
872
|
+
overflowWrap: "anywhere"
|
|
873
|
+
},
|
|
874
|
+
size: "S",
|
|
875
|
+
variant: "tertiary",
|
|
876
|
+
disabled,
|
|
877
|
+
onClick: () => onChange({
|
|
878
|
+
...value,
|
|
879
|
+
email: {
|
|
880
|
+
...value.email,
|
|
881
|
+
[target]: `${value.email[target]}{{${key}}}`
|
|
882
|
+
}
|
|
883
|
+
}),
|
|
884
|
+
children: `{{${key}}}`
|
|
885
|
+
},
|
|
886
|
+
key
|
|
887
|
+
)) })
|
|
888
|
+
] }),
|
|
889
|
+
/* @__PURE__ */ jsxs6("label", { children: [
|
|
890
|
+
/* @__PURE__ */ jsx6(
|
|
891
|
+
"input",
|
|
892
|
+
{
|
|
893
|
+
type: "checkbox",
|
|
894
|
+
disabled,
|
|
895
|
+
checked: Boolean(value.webhook),
|
|
896
|
+
onChange: (event) => toggle("webhook", event.target.checked)
|
|
897
|
+
}
|
|
898
|
+
),
|
|
899
|
+
" ",
|
|
900
|
+
"Webhook notifications"
|
|
901
|
+
] }),
|
|
902
|
+
value.webhook && /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
903
|
+
/* @__PURE__ */ jsx6(
|
|
904
|
+
TextField,
|
|
905
|
+
{
|
|
906
|
+
label: "Webhook URL",
|
|
907
|
+
value: value.webhook.url,
|
|
908
|
+
disabled,
|
|
909
|
+
error: errors.url,
|
|
910
|
+
onChange: (url) => onChange({ ...value, webhook: { ...value.webhook, url } })
|
|
911
|
+
}
|
|
912
|
+
),
|
|
913
|
+
/* @__PURE__ */ jsx6(
|
|
914
|
+
TextField,
|
|
915
|
+
{
|
|
916
|
+
label: "Webhook secret",
|
|
917
|
+
secret: true,
|
|
918
|
+
value: value.webhook.secret ?? "",
|
|
919
|
+
disabled,
|
|
920
|
+
onChange: (secret) => {
|
|
921
|
+
const webhook = { ...value.webhook };
|
|
922
|
+
delete webhook.secret;
|
|
923
|
+
if (secret) webhook.secret = secret;
|
|
924
|
+
onChange({ ...value, webhook });
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
),
|
|
928
|
+
/* @__PURE__ */ jsx6(Typography5, { variant: "pi", children: "Stored secrets and header values appear as ********. Leave them untouched to preserve them, or enter a replacement. Clear a secret or remove a header to delete it." }),
|
|
929
|
+
Object.entries(value.webhook.headers ?? {}).map(
|
|
930
|
+
([name, header]) => /* @__PURE__ */ jsxs6(
|
|
931
|
+
Flex5,
|
|
932
|
+
{
|
|
933
|
+
direction: "column",
|
|
934
|
+
alignItems: "stretch",
|
|
935
|
+
gap: 2,
|
|
936
|
+
children: [
|
|
937
|
+
/* @__PURE__ */ jsx6(
|
|
938
|
+
TextField,
|
|
939
|
+
{
|
|
940
|
+
label: `Header: ${name}`,
|
|
941
|
+
secret: true,
|
|
942
|
+
value: header,
|
|
943
|
+
disabled,
|
|
944
|
+
onChange: (next) => onChange({
|
|
945
|
+
...value,
|
|
946
|
+
webhook: {
|
|
947
|
+
...value.webhook,
|
|
948
|
+
headers: {
|
|
949
|
+
...value.webhook.headers,
|
|
950
|
+
[name]: next
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
})
|
|
954
|
+
}
|
|
955
|
+
),
|
|
956
|
+
/* @__PURE__ */ jsxs6(
|
|
957
|
+
Button4,
|
|
958
|
+
{
|
|
959
|
+
type: "button",
|
|
960
|
+
variant: "tertiary",
|
|
961
|
+
disabled,
|
|
962
|
+
onClick: () => {
|
|
963
|
+
const headers = { ...value.webhook.headers };
|
|
964
|
+
delete headers[name];
|
|
965
|
+
onChange({
|
|
966
|
+
...value,
|
|
967
|
+
webhook: { ...value.webhook, headers }
|
|
968
|
+
});
|
|
969
|
+
},
|
|
970
|
+
children: [
|
|
971
|
+
"Remove ",
|
|
972
|
+
name
|
|
973
|
+
]
|
|
974
|
+
}
|
|
975
|
+
)
|
|
976
|
+
]
|
|
977
|
+
},
|
|
978
|
+
name
|
|
979
|
+
)
|
|
980
|
+
),
|
|
981
|
+
/* @__PURE__ */ jsx6(
|
|
982
|
+
AddHeader,
|
|
983
|
+
{
|
|
984
|
+
disabled,
|
|
985
|
+
names: Object.keys(value.webhook.headers ?? {}),
|
|
986
|
+
onAdd: (name, header) => onChange({
|
|
987
|
+
...value,
|
|
988
|
+
webhook: {
|
|
989
|
+
...value.webhook,
|
|
990
|
+
headers: { ...value.webhook.headers, [name]: header }
|
|
991
|
+
}
|
|
992
|
+
})
|
|
993
|
+
}
|
|
994
|
+
)
|
|
995
|
+
] })
|
|
996
|
+
] })
|
|
997
|
+
] });
|
|
998
|
+
}
|
|
999
|
+
function AddHeader({
|
|
1000
|
+
disabled,
|
|
1001
|
+
names,
|
|
1002
|
+
onAdd
|
|
1003
|
+
}) {
|
|
1004
|
+
const [name, setName] = useState3("");
|
|
1005
|
+
const [value, setValue] = useState3("");
|
|
1006
|
+
return /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1007
|
+
/* @__PURE__ */ jsx6(
|
|
1008
|
+
TextField,
|
|
1009
|
+
{
|
|
1010
|
+
label: "New header name",
|
|
1011
|
+
value: name,
|
|
1012
|
+
disabled,
|
|
1013
|
+
onChange: setName
|
|
1014
|
+
}
|
|
1015
|
+
),
|
|
1016
|
+
/* @__PURE__ */ jsx6(
|
|
1017
|
+
TextField,
|
|
1018
|
+
{
|
|
1019
|
+
label: "New header value",
|
|
1020
|
+
secret: true,
|
|
1021
|
+
value,
|
|
1022
|
+
disabled,
|
|
1023
|
+
onChange: setValue
|
|
1024
|
+
}
|
|
1025
|
+
),
|
|
1026
|
+
/* @__PURE__ */ jsx6(
|
|
1027
|
+
Button4,
|
|
1028
|
+
{
|
|
1029
|
+
type: "button",
|
|
1030
|
+
variant: "tertiary",
|
|
1031
|
+
disabled: disabled || !name.trim() || !value || names.some((item) => item.toLowerCase() === name.trim().toLowerCase()),
|
|
1032
|
+
onClick: () => {
|
|
1033
|
+
onAdd(name.trim(), value);
|
|
1034
|
+
setName("");
|
|
1035
|
+
setValue("");
|
|
1036
|
+
},
|
|
1037
|
+
children: "Add header"
|
|
1038
|
+
}
|
|
1039
|
+
)
|
|
1040
|
+
] });
|
|
1041
|
+
}
|
|
1042
|
+
function NotificationTest({
|
|
1043
|
+
fields,
|
|
1044
|
+
disabled,
|
|
1045
|
+
send
|
|
1046
|
+
}) {
|
|
1047
|
+
const [values, setValues] = useState3({});
|
|
1048
|
+
const [results, setResults] = useState3(null);
|
|
1049
|
+
const [error, setError] = useState3("");
|
|
1050
|
+
const [errors, setErrors] = useState3({});
|
|
1051
|
+
const [busy, setBusy] = useState3(false);
|
|
1052
|
+
return /* @__PURE__ */ jsxs6(Section, { title: "Test notifications", children: [
|
|
1053
|
+
/* @__PURE__ */ jsx6(Typography5, { variant: "pi", children: "Sends real notifications using the saved settings and these sample values, including hidden fields. Save all changes before testing. No submission record is created." }),
|
|
1054
|
+
disabled && /* @__PURE__ */ jsx6(Typography5, { variant: "pi", children: "Testing requires a saved form, no unsaved changes and forms.update permission." }),
|
|
1055
|
+
fields.map((field) => /* @__PURE__ */ jsx6("div", { style: { display: "grid", gap: 6 }, children: field.type === "boolean" ? /* @__PURE__ */ jsxs6("label", { children: [
|
|
1056
|
+
/* @__PURE__ */ jsxs6(Typography5, { children: [
|
|
1057
|
+
field.label,
|
|
1058
|
+
" (",
|
|
1059
|
+
field.name,
|
|
1060
|
+
")",
|
|
1061
|
+
field.visibility === "hidden" ? " \xB7 hidden" : ""
|
|
1062
|
+
] }),
|
|
1063
|
+
/* @__PURE__ */ jsxs6(
|
|
1064
|
+
"select",
|
|
1065
|
+
{
|
|
1066
|
+
disabled: disabled || busy,
|
|
1067
|
+
value: values[field.name] ?? "",
|
|
1068
|
+
onChange: (event) => setValues({ ...values, [field.name]: event.target.value }),
|
|
1069
|
+
children: [
|
|
1070
|
+
/* @__PURE__ */ jsx6("option", { value: "", children: "Choose a value" }),
|
|
1071
|
+
/* @__PURE__ */ jsx6("option", { value: "true", children: "True" }),
|
|
1072
|
+
/* @__PURE__ */ jsx6("option", { value: "false", children: "False" })
|
|
1073
|
+
]
|
|
1074
|
+
}
|
|
1075
|
+
),
|
|
1076
|
+
errors[field.name] && /* @__PURE__ */ jsx6(ErrorNotice, { message: errors[field.name] ?? "" })
|
|
1077
|
+
] }) : /* @__PURE__ */ jsx6(
|
|
1078
|
+
TextField,
|
|
1079
|
+
{
|
|
1080
|
+
label: `${field.label} (${field.name})${field.visibility === "hidden" ? " \xB7 hidden" : ""}`,
|
|
1081
|
+
value: values[field.name] ?? "",
|
|
1082
|
+
disabled: disabled || busy,
|
|
1083
|
+
error: errors[field.name],
|
|
1084
|
+
onChange: (next) => setValues({ ...values, [field.name]: next })
|
|
1085
|
+
}
|
|
1086
|
+
) }, field.name)),
|
|
1087
|
+
/* @__PURE__ */ jsx6(
|
|
1088
|
+
Button4,
|
|
1089
|
+
{
|
|
1090
|
+
type: "button",
|
|
1091
|
+
disabled: disabled || busy,
|
|
1092
|
+
onClick: async () => {
|
|
1093
|
+
setBusy(true);
|
|
1094
|
+
setError("");
|
|
1095
|
+
setErrors({});
|
|
1096
|
+
setResults(null);
|
|
1097
|
+
try {
|
|
1098
|
+
setResults(await send(samplePayload(fields, values)));
|
|
1099
|
+
} catch (cause) {
|
|
1100
|
+
setError(errorMessage(cause));
|
|
1101
|
+
setErrors(sampleErrors(cause));
|
|
1102
|
+
} finally {
|
|
1103
|
+
setBusy(false);
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
children: busy ? "Sending test\u2026" : "Send test notifications"
|
|
1107
|
+
}
|
|
1108
|
+
),
|
|
1109
|
+
/* @__PURE__ */ jsx6(ErrorNotice, { message: error }),
|
|
1110
|
+
/* @__PURE__ */ jsxs6("div", { "aria-live": "polite", children: [
|
|
1111
|
+
results?.length === 0 && /* @__PURE__ */ jsx6(Typography5, { children: "No notification channels are enabled in the saved settings." }),
|
|
1112
|
+
results?.map((delivery) => /* @__PURE__ */ jsx6(
|
|
1113
|
+
"div",
|
|
1114
|
+
{
|
|
1115
|
+
style: { overflowWrap: "anywhere", marginTop: 12 },
|
|
1116
|
+
children: /* @__PURE__ */ jsxs6(
|
|
1117
|
+
Typography5,
|
|
1118
|
+
{
|
|
1119
|
+
textColor: delivery.status === "sent" ? "success700" : "danger700",
|
|
1120
|
+
children: [
|
|
1121
|
+
delivery.channel,
|
|
1122
|
+
":",
|
|
1123
|
+
" ",
|
|
1124
|
+
delivery.status === "sent" ? "Success" : "Failed",
|
|
1125
|
+
" \xB7",
|
|
1126
|
+
" ",
|
|
1127
|
+
delivery.attempts,
|
|
1128
|
+
" attempt(s)",
|
|
1129
|
+
delivery.lastError ? ` \xB7 ${delivery.lastError}` : "",
|
|
1130
|
+
delivery.sentAt ? ` \xB7 ${delivery.sentAt}` : ""
|
|
1131
|
+
]
|
|
1132
|
+
}
|
|
1133
|
+
)
|
|
1134
|
+
},
|
|
1135
|
+
delivery.channel
|
|
1136
|
+
))
|
|
1137
|
+
] })
|
|
1138
|
+
] });
|
|
1139
|
+
}
|
|
1140
|
+
var init_notification_editor = __esm({
|
|
1141
|
+
"admin/src/notification-editor.tsx"() {
|
|
1142
|
+
"use strict";
|
|
1143
|
+
init_form();
|
|
1144
|
+
init_notifications();
|
|
1145
|
+
init_ui();
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
// admin/src/form-editor-page.tsx
|
|
1150
|
+
import { useEffect as useEffect2, useRef as useRef3, useState as useState4 } from "react";
|
|
1151
|
+
import { Button as Button5, Flex as Flex6, Typography as Typography6 } from "@strapi/design-system";
|
|
624
1152
|
import { Layouts as Layouts2, Page as Page2, useFetchClient as useFetchClient2 } from "@strapi/strapi/admin";
|
|
625
1153
|
import { useNavigate as useNavigate2, useParams } from "react-router-dom";
|
|
626
|
-
import { Fragment as
|
|
1154
|
+
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
627
1155
|
function FormEditorPage({ canUpdate }) {
|
|
628
1156
|
const { documentId } = useParams();
|
|
629
1157
|
const client = useFetchClient2();
|
|
630
1158
|
const { get } = client;
|
|
631
1159
|
const navigate = useNavigate2();
|
|
632
|
-
const [draft, setDraft] =
|
|
633
|
-
const [
|
|
634
|
-
const [
|
|
635
|
-
const [
|
|
636
|
-
const [
|
|
637
|
-
const [
|
|
638
|
-
const [
|
|
639
|
-
const [
|
|
640
|
-
const [
|
|
641
|
-
const [
|
|
1160
|
+
const [draft, setDraft] = useState4(emptyForm);
|
|
1161
|
+
const [notificationsOpened, setNotificationsOpened] = useState4(false);
|
|
1162
|
+
const [savedDraft, setSavedDraft] = useState4("");
|
|
1163
|
+
const [version, setVersion] = useState4(null);
|
|
1164
|
+
const [targets, setTargets] = useState4([]);
|
|
1165
|
+
const [schema, setSchema] = useState4([]);
|
|
1166
|
+
const [loading, setLoading] = useState4(true);
|
|
1167
|
+
const [fieldsLoading, setFieldsLoading] = useState4(false);
|
|
1168
|
+
const [error, setError] = useState4("");
|
|
1169
|
+
const [loaded, setLoaded] = useState4(false);
|
|
1170
|
+
const [busy, setBusy] = useState4(false);
|
|
1171
|
+
const [retry, setRetry] = useState4(0);
|
|
642
1172
|
const lock = useRef3(false);
|
|
643
1173
|
const targetRequest = useRef3(0);
|
|
644
1174
|
useEffect2(() => {
|
|
645
1175
|
let active = true;
|
|
646
1176
|
setLoading(true);
|
|
1177
|
+
setNotificationsOpened(false);
|
|
647
1178
|
setLoaded(false);
|
|
648
1179
|
setError("");
|
|
649
1180
|
async function load() {
|
|
@@ -663,6 +1194,7 @@ function FormEditorPage({ canUpdate }) {
|
|
|
663
1194
|
consent: { ...emptyForm.consent }
|
|
664
1195
|
}
|
|
665
1196
|
);
|
|
1197
|
+
setSavedDraft(saved ? JSON.stringify(savePayload(saved, true)) : "");
|
|
666
1198
|
setVersion(saved?.version ?? null);
|
|
667
1199
|
setSchema(attrs);
|
|
668
1200
|
setLoaded(true);
|
|
@@ -712,16 +1244,16 @@ function FormEditorPage({ canUpdate }) {
|
|
|
712
1244
|
setBusy(false);
|
|
713
1245
|
}
|
|
714
1246
|
}
|
|
715
|
-
if (loading) return /* @__PURE__ */
|
|
1247
|
+
if (loading) return /* @__PURE__ */ jsx7(Page2.Loading, {});
|
|
716
1248
|
const disabled = !canUpdate || busy || fieldsLoading;
|
|
717
|
-
return /* @__PURE__ */
|
|
718
|
-
/* @__PURE__ */
|
|
1249
|
+
return /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
1250
|
+
/* @__PURE__ */ jsx7(
|
|
719
1251
|
Layouts2.Header,
|
|
720
1252
|
{
|
|
721
1253
|
title: documentId ? draft.name || "Form" : "Create form",
|
|
722
1254
|
subtitle: version ? `Form version ${version} \xB7 assigned by server on every save` : "New forms start at version 1.",
|
|
723
|
-
navigationAction: /* @__PURE__ */
|
|
724
|
-
|
|
1255
|
+
navigationAction: /* @__PURE__ */ jsx7(
|
|
1256
|
+
Button5,
|
|
725
1257
|
{
|
|
726
1258
|
variant: "tertiary",
|
|
727
1259
|
onClick: () => navigate("/plugins/builder/forms"),
|
|
@@ -730,18 +1262,23 @@ function FormEditorPage({ canUpdate }) {
|
|
|
730
1262
|
)
|
|
731
1263
|
}
|
|
732
1264
|
),
|
|
733
|
-
/* @__PURE__ */
|
|
734
|
-
/* @__PURE__ */
|
|
735
|
-
!loaded ? /* @__PURE__ */
|
|
736
|
-
/* @__PURE__ */
|
|
1265
|
+
/* @__PURE__ */ jsx7(Layouts2.Content, { children: /* @__PURE__ */ jsxs7(Flex6, { direction: "column", alignItems: "stretch", gap: 6, children: [
|
|
1266
|
+
/* @__PURE__ */ jsx7(ErrorNotice, { message: error }),
|
|
1267
|
+
!loaded ? /* @__PURE__ */ jsx7(Button5, { onClick: () => setRetry((value) => value + 1), children: "Retry loading form" }) : /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
1268
|
+
/* @__PURE__ */ jsx7(
|
|
737
1269
|
"form",
|
|
738
1270
|
{
|
|
739
1271
|
onSubmit: (event) => {
|
|
740
1272
|
event.preventDefault();
|
|
741
1273
|
if (disabled) return;
|
|
742
1274
|
void run(async () => {
|
|
743
|
-
const saved = await formApi(client).save(
|
|
1275
|
+
const saved = await formApi(client).save(
|
|
1276
|
+
draft,
|
|
1277
|
+
documentId,
|
|
1278
|
+
notificationsOpened
|
|
1279
|
+
);
|
|
744
1280
|
setDraft(saved);
|
|
1281
|
+
setSavedDraft(JSON.stringify(savePayload(saved, true)));
|
|
745
1282
|
setVersion(saved.version);
|
|
746
1283
|
if (!documentId)
|
|
747
1284
|
navigate(`/plugins/builder/forms/${saved.documentId}`, {
|
|
@@ -749,9 +1286,9 @@ function FormEditorPage({ canUpdate }) {
|
|
|
749
1286
|
});
|
|
750
1287
|
});
|
|
751
1288
|
},
|
|
752
|
-
children: /* @__PURE__ */
|
|
753
|
-
/* @__PURE__ */
|
|
754
|
-
/* @__PURE__ */
|
|
1289
|
+
children: /* @__PURE__ */ jsxs7(Flex6, { direction: "column", alignItems: "stretch", gap: 6, children: [
|
|
1290
|
+
/* @__PURE__ */ jsxs7(Section, { title: "Definition", children: [
|
|
1291
|
+
/* @__PURE__ */ jsx7(
|
|
755
1292
|
TextField,
|
|
756
1293
|
{
|
|
757
1294
|
label: "Name",
|
|
@@ -761,7 +1298,7 @@ function FormEditorPage({ canUpdate }) {
|
|
|
761
1298
|
onChange: (name) => setDraft({ ...draft, name })
|
|
762
1299
|
}
|
|
763
1300
|
),
|
|
764
|
-
/* @__PURE__ */
|
|
1301
|
+
/* @__PURE__ */ jsx7(
|
|
765
1302
|
TextField,
|
|
766
1303
|
{
|
|
767
1304
|
label: "Slug",
|
|
@@ -771,11 +1308,11 @@ function FormEditorPage({ canUpdate }) {
|
|
|
771
1308
|
onChange: (slug) => setDraft({ ...draft, slug })
|
|
772
1309
|
}
|
|
773
1310
|
),
|
|
774
|
-
/* @__PURE__ */
|
|
775
|
-
/* @__PURE__ */
|
|
1311
|
+
/* @__PURE__ */ jsx7(Typography6, { variant: "pi", children: "Use lowercase letters, numbers and hyphens. The slug cannot change after creation." }),
|
|
1312
|
+
/* @__PURE__ */ jsxs7("label", { children: [
|
|
776
1313
|
"Target content type",
|
|
777
1314
|
" ",
|
|
778
|
-
/* @__PURE__ */
|
|
1315
|
+
/* @__PURE__ */ jsxs7(
|
|
779
1316
|
"select",
|
|
780
1317
|
{
|
|
781
1318
|
required: true,
|
|
@@ -784,8 +1321,8 @@ function FormEditorPage({ canUpdate }) {
|
|
|
784
1321
|
onChange: (event) => void selectTarget(event.target.value),
|
|
785
1322
|
style: { padding: 10, width: "100%" },
|
|
786
1323
|
children: [
|
|
787
|
-
/* @__PURE__ */
|
|
788
|
-
targets.map((target) => /* @__PURE__ */
|
|
1324
|
+
/* @__PURE__ */ jsx7("option", { value: "", children: "Select a target" }),
|
|
1325
|
+
targets.map((target) => /* @__PURE__ */ jsxs7("option", { value: target.uid, children: [
|
|
789
1326
|
target.name,
|
|
790
1327
|
" \u2014 ",
|
|
791
1328
|
target.uid
|
|
@@ -794,9 +1331,9 @@ function FormEditorPage({ canUpdate }) {
|
|
|
794
1331
|
}
|
|
795
1332
|
)
|
|
796
1333
|
] }),
|
|
797
|
-
fieldsLoading && /* @__PURE__ */
|
|
1334
|
+
fieldsLoading && /* @__PURE__ */ jsx7(Typography6, { children: "Loading target fields\u2026" })
|
|
798
1335
|
] }),
|
|
799
|
-
/* @__PURE__ */
|
|
1336
|
+
/* @__PURE__ */ jsx7(
|
|
800
1337
|
FieldEditor,
|
|
801
1338
|
{
|
|
802
1339
|
fields: draft.fields,
|
|
@@ -805,8 +1342,8 @@ function FormEditorPage({ canUpdate }) {
|
|
|
805
1342
|
onChange: (fields) => setDraft({ ...draft, fields })
|
|
806
1343
|
}
|
|
807
1344
|
),
|
|
808
|
-
/* @__PURE__ */
|
|
809
|
-
/* @__PURE__ */
|
|
1345
|
+
/* @__PURE__ */ jsxs7(Section, { title: "Consent", children: [
|
|
1346
|
+
/* @__PURE__ */ jsx7(
|
|
810
1347
|
TextField,
|
|
811
1348
|
{
|
|
812
1349
|
label: "Consent label",
|
|
@@ -820,7 +1357,7 @@ function FormEditorPage({ canUpdate }) {
|
|
|
820
1357
|
})
|
|
821
1358
|
}
|
|
822
1359
|
),
|
|
823
|
-
/* @__PURE__ */
|
|
1360
|
+
/* @__PURE__ */ jsx7(
|
|
824
1361
|
TextField,
|
|
825
1362
|
{
|
|
826
1363
|
label: "Consent version",
|
|
@@ -836,10 +1373,22 @@ function FormEditorPage({ canUpdate }) {
|
|
|
836
1373
|
})
|
|
837
1374
|
}
|
|
838
1375
|
),
|
|
839
|
-
/* @__PURE__ */
|
|
1376
|
+
/* @__PURE__ */ jsx7(Typography6, { variant: "pi", children: "Consent version identifies the terms accepted by the visitor and is separate from the server form version." })
|
|
840
1377
|
] }),
|
|
841
|
-
|
|
842
|
-
|
|
1378
|
+
/* @__PURE__ */ jsx7(
|
|
1379
|
+
NotificationEditor,
|
|
1380
|
+
{
|
|
1381
|
+
value: draft.notifications ?? {},
|
|
1382
|
+
fields: draft.fields,
|
|
1383
|
+
opened: notificationsOpened,
|
|
1384
|
+
onOpen: () => setNotificationsOpened(true),
|
|
1385
|
+
onChange: (notifications) => setDraft({ ...draft, notifications }),
|
|
1386
|
+
disabled,
|
|
1387
|
+
saveError: error
|
|
1388
|
+
}
|
|
1389
|
+
),
|
|
1390
|
+
canUpdate && /* @__PURE__ */ jsx7(
|
|
1391
|
+
Button5,
|
|
843
1392
|
{
|
|
844
1393
|
type: "submit",
|
|
845
1394
|
disabled: disabled || !draft.targetUid || !draft.fields.length,
|
|
@@ -849,9 +1398,18 @@ function FormEditorPage({ canUpdate }) {
|
|
|
849
1398
|
] })
|
|
850
1399
|
}
|
|
851
1400
|
),
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
1401
|
+
notificationsOpened && /* @__PURE__ */ jsx7(
|
|
1402
|
+
NotificationTest,
|
|
1403
|
+
{
|
|
1404
|
+
fields: draft.fields,
|
|
1405
|
+
disabled: disabled || !documentId || JSON.stringify(savePayload(draft, true)) !== savedDraft,
|
|
1406
|
+
send: (sample) => formApi(client).testSend(documentId, sample)
|
|
1407
|
+
},
|
|
1408
|
+
savedDraft
|
|
1409
|
+
),
|
|
1410
|
+
documentId && /* @__PURE__ */ jsxs7(Section, { title: "Portable definition", children: [
|
|
1411
|
+
/* @__PURE__ */ jsx7(
|
|
1412
|
+
Button5,
|
|
855
1413
|
{
|
|
856
1414
|
disabled: busy,
|
|
857
1415
|
onClick: () => void run(
|
|
@@ -863,8 +1421,8 @@ function FormEditorPage({ canUpdate }) {
|
|
|
863
1421
|
children: "Export saved form JSON"
|
|
864
1422
|
}
|
|
865
1423
|
),
|
|
866
|
-
/* @__PURE__ */
|
|
867
|
-
canUpdate && /* @__PURE__ */
|
|
1424
|
+
/* @__PURE__ */ jsx7(Typography6, { variant: "pi", children: "Export uses the saved definition. Notification credentials are masked by the server." }),
|
|
1425
|
+
canUpdate && /* @__PURE__ */ jsx7(
|
|
868
1426
|
ImportForm,
|
|
869
1427
|
{
|
|
870
1428
|
replace: true,
|
|
@@ -879,6 +1437,11 @@ function FormEditorPage({ canUpdate }) {
|
|
|
879
1437
|
documentId
|
|
880
1438
|
);
|
|
881
1439
|
setDraft(saved);
|
|
1440
|
+
setSavedDraft(
|
|
1441
|
+
JSON.stringify(savePayload(saved, true))
|
|
1442
|
+
);
|
|
1443
|
+
setNotificationsOpened(false);
|
|
1444
|
+
setError("");
|
|
882
1445
|
setVersion(saved.version);
|
|
883
1446
|
setSchema(
|
|
884
1447
|
await formApi(client).fields(saved.targetUid)
|
|
@@ -902,6 +1465,7 @@ var init_form_editor_page = __esm({
|
|
|
902
1465
|
init_form();
|
|
903
1466
|
init_ui();
|
|
904
1467
|
init_field_editor();
|
|
1468
|
+
init_notification_editor();
|
|
905
1469
|
init_import_form();
|
|
906
1470
|
}
|
|
907
1471
|
});
|
|
@@ -913,7 +1477,7 @@ __export(app_exports, {
|
|
|
913
1477
|
});
|
|
914
1478
|
import { Navigate, Route, Routes } from "react-router-dom";
|
|
915
1479
|
import { Layouts as Layouts3, Page as Page3, useRBAC } from "@strapi/strapi/admin";
|
|
916
|
-
import { jsx as
|
|
1480
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
917
1481
|
function App() {
|
|
918
1482
|
const { allowedActions, isLoading } = useRBAC(
|
|
919
1483
|
["read", "update"].map((action) => ({
|
|
@@ -921,31 +1485,31 @@ function App() {
|
|
|
921
1485
|
subject: null
|
|
922
1486
|
}))
|
|
923
1487
|
);
|
|
924
|
-
if (isLoading) return /* @__PURE__ */
|
|
925
|
-
if (!allowedActions.canRead) return /* @__PURE__ */
|
|
1488
|
+
if (isLoading) return /* @__PURE__ */ jsx8(Page3.Loading, {});
|
|
1489
|
+
if (!allowedActions.canRead) return /* @__PURE__ */ jsx8(Page3.NoPermissions, {});
|
|
926
1490
|
const canUpdate = allowedActions.canUpdate === true;
|
|
927
|
-
return /* @__PURE__ */
|
|
928
|
-
/* @__PURE__ */
|
|
929
|
-
/* @__PURE__ */
|
|
930
|
-
/* @__PURE__ */
|
|
1491
|
+
return /* @__PURE__ */ jsx8(Layouts3.Root, { children: /* @__PURE__ */ jsxs8(Routes, { children: [
|
|
1492
|
+
/* @__PURE__ */ jsx8(Route, { index: true, element: /* @__PURE__ */ jsx8(Navigate, { to: "forms", replace: true }) }),
|
|
1493
|
+
/* @__PURE__ */ jsx8(Route, { path: "forms", element: /* @__PURE__ */ jsx8(FormListPage, { canUpdate }) }),
|
|
1494
|
+
/* @__PURE__ */ jsx8(
|
|
931
1495
|
Route,
|
|
932
1496
|
{
|
|
933
1497
|
path: "forms/new",
|
|
934
|
-
element: canUpdate ? /* @__PURE__ */
|
|
1498
|
+
element: canUpdate ? /* @__PURE__ */ jsx8(FormEditorPage, { canUpdate: true }) : /* @__PURE__ */ jsx8(Page3.NoPermissions, {})
|
|
935
1499
|
}
|
|
936
1500
|
),
|
|
937
|
-
/* @__PURE__ */
|
|
1501
|
+
/* @__PURE__ */ jsx8(
|
|
938
1502
|
Route,
|
|
939
1503
|
{
|
|
940
1504
|
path: "forms/:documentId",
|
|
941
|
-
element: /* @__PURE__ */
|
|
1505
|
+
element: /* @__PURE__ */ jsx8(FormEditorPage, { canUpdate })
|
|
942
1506
|
}
|
|
943
1507
|
),
|
|
944
|
-
/* @__PURE__ */
|
|
1508
|
+
/* @__PURE__ */ jsx8(
|
|
945
1509
|
Route,
|
|
946
1510
|
{
|
|
947
1511
|
path: "*",
|
|
948
|
-
element: /* @__PURE__ */
|
|
1512
|
+
element: /* @__PURE__ */ jsx8(Page3.Error, { content: "Form page not found." })
|
|
949
1513
|
}
|
|
950
1514
|
)
|
|
951
1515
|
] }) });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Delivery, FormField, NotificationConfig } from "./form";
|
|
2
|
+
export declare function NotificationEditor({ value, fields, opened, onOpen, onChange, disabled, saveError, }: {
|
|
3
|
+
value: NotificationConfig;
|
|
4
|
+
fields: FormField[];
|
|
5
|
+
opened: boolean;
|
|
6
|
+
onOpen: () => void;
|
|
7
|
+
onChange: (value: NotificationConfig) => void;
|
|
8
|
+
disabled: boolean;
|
|
9
|
+
saveError: string;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
export declare function NotificationTest({ fields, disabled, send, }: {
|
|
12
|
+
fields: FormField[];
|
|
13
|
+
disabled: boolean;
|
|
14
|
+
send: (sample: Record<string, unknown>) => Promise<Delivery[]>;
|
|
15
|
+
}): import("react").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=notification-editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-editor.d.ts","sourceRoot":"","sources":["../../admin/src/notification-editor.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAUtE,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,MAAM,EACN,MAAM,EACN,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,EAAE;IACD,KAAK,EAAE,kBAAkB,CAAC;IAC1B,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAC9C,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,+BAoQA;AA+CD,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,QAAQ,EACR,IAAI,GACL,EAAE;IACD,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;CAChE,+BAqGA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FormField, NotificationConfig } from "./form";
|
|
2
|
+
export declare function placeholders(fields: FormField[]): string[];
|
|
3
|
+
export declare function templateError(value: string, fields: FormField[]): string;
|
|
4
|
+
export declare function notificationErrors(config: NotificationConfig, fields: FormField[]): Record<string, string>;
|
|
5
|
+
/** Empty optional values are omitted; hidden fields use the same types as visible fields. */
|
|
6
|
+
export declare function samplePayload(fields: FormField[], values: Record<string, string>): Record<string, unknown>;
|
|
7
|
+
export declare function sampleErrors(error: unknown): Record<string, string>;
|
|
8
|
+
//# sourceMappingURL=notifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../admin/src/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAE5D,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAO1D;AACD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAUxE;AACD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,SAAS,EAAE,GAClB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmCxB;AACD,6FAA6F;AAC7F,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,EAAE,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAiBzB;AACD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAuBnE"}
|
package/dist/admin/ui.d.ts
CHANGED
|
@@ -3,13 +3,15 @@ export declare function Section({ title, children, }: {
|
|
|
3
3
|
title: string;
|
|
4
4
|
children: ReactNode;
|
|
5
5
|
}): import("react").JSX.Element;
|
|
6
|
-
export declare function TextField({ label, value, onChange, disabled, required, multiline, }: {
|
|
6
|
+
export declare function TextField({ label, value, onChange, disabled, required, multiline, secret, error, }: {
|
|
7
7
|
label: string;
|
|
8
8
|
value: string;
|
|
9
9
|
onChange: (value: string) => void;
|
|
10
10
|
disabled?: boolean;
|
|
11
11
|
required?: boolean;
|
|
12
12
|
multiline?: boolean;
|
|
13
|
+
secret?: boolean;
|
|
14
|
+
error?: string;
|
|
13
15
|
}): import("react").JSX.Element;
|
|
14
16
|
export declare function ErrorNotice({ message }: {
|
|
15
17
|
message: string;
|
package/dist/admin/ui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../admin/src/ui.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../admin/src/ui.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS,CAAC;CACrB,+BAWA;AACD,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,MAAM,EACN,KAAK,GACN,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,+BAwCA;AACD,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,sCAM3D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dani-builder/strapi-plugin-builder",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Daniworks Builder — reusable CMS components, content types, and smart API for Strapi v5",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"strapi": {
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"directory": "packages/strapi-plugin-builder"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@dani-builder/strapi-client": "^1.
|
|
75
|
+
"@dani-builder/strapi-client": "^1.1.0"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@strapi/strapi": "5.54.0",
|