@dani-builder/strapi-plugin-builder 1.1.0 → 1.3.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.
Files changed (32) hide show
  1. package/dist/admin/api.d.ts +11 -0
  2. package/dist/admin/api.d.ts.map +1 -1
  3. package/dist/admin/app.d.ts +1 -0
  4. package/dist/admin/app.d.ts.map +1 -1
  5. package/dist/admin/index.d.ts.map +1 -1
  6. package/dist/admin/index.mjs +589 -228
  7. package/dist/admin/submission-page.d.ts +4 -0
  8. package/dist/admin/submission-page.d.ts.map +1 -0
  9. package/dist/admin/submission.d.ts +27 -0
  10. package/dist/admin/submission.d.ts.map +1 -0
  11. package/dist/server/src/bootstrap.d.ts.map +1 -1
  12. package/dist/server/src/bootstrap.js +6 -0
  13. package/dist/server/src/bootstrap.js.map +1 -1
  14. package/dist/server/src/config/builder.d.ts +1 -1
  15. package/dist/server/src/config/builder.d.ts.map +1 -1
  16. package/dist/server/src/config/builder.js.map +1 -1
  17. package/dist/server/src/config/forms.d.ts +9 -1
  18. package/dist/server/src/config/forms.d.ts.map +1 -1
  19. package/dist/server/src/config/forms.js.map +1 -1
  20. package/dist/server/src/content-types/submission/schema.json +1 -1
  21. package/dist/server/src/controllers/submission.d.ts +3 -0
  22. package/dist/server/src/controllers/submission.d.ts.map +1 -1
  23. package/dist/server/src/controllers/submission.js +41 -0
  24. package/dist/server/src/controllers/submission.js.map +1 -1
  25. package/dist/server/src/routes/admin.d.ts.map +1 -1
  26. package/dist/server/src/routes/admin.js +15 -6
  27. package/dist/server/src/routes/admin.js.map +1 -1
  28. package/dist/server/src/services/submission.d.ts +71 -3
  29. package/dist/server/src/services/submission.d.ts.map +1 -1
  30. package/dist/server/src/services/submission.js +121 -11
  31. package/dist/server/src/services/submission.js.map +1 -1
  32. package/package.json +5 -2
@@ -136,6 +136,30 @@ function downloadForm(value, slug) {
136
136
  anchor.click();
137
137
  URL.revokeObjectURL(url);
138
138
  }
139
+ function submissionApi(client) {
140
+ const root2 = "/plugins/builder/submissions";
141
+ return {
142
+ async list(params) {
143
+ return (await client.get(root2, { params })).data;
144
+ },
145
+ async get(id) {
146
+ return (await client.get(
147
+ `${root2}/${encodeURIComponent(id)}`
148
+ )).data.data;
149
+ },
150
+ async status(id, status) {
151
+ return (await client.put(
152
+ `${root2}/${encodeURIComponent(id)}/status`,
153
+ { data: { status } }
154
+ )).data.data;
155
+ },
156
+ async redeliver(id) {
157
+ return (await client.post(
158
+ `${root2}/${encodeURIComponent(id)}/redeliver`
159
+ )).data.data;
160
+ }
161
+ };
162
+ }
139
163
  var root;
140
164
  var init_api = __esm({
141
165
  "admin/src/api.ts"() {
@@ -145,6 +169,20 @@ var init_api = __esm({
145
169
  }
146
170
  });
147
171
 
172
+ // admin/src/submission.ts
173
+ var submissionStatuses;
174
+ var init_submission = __esm({
175
+ "admin/src/submission.ts"() {
176
+ "use strict";
177
+ submissionStatuses = [
178
+ "received",
179
+ "reviewing",
180
+ "responded",
181
+ "closed"
182
+ ];
183
+ }
184
+ });
185
+
148
186
  // admin/src/ui.tsx
149
187
  import { useId } from "react";
150
188
  import { Box, Flex, Typography } from "@strapi/design-system";
@@ -208,19 +246,309 @@ var init_ui = __esm({
208
246
  }
209
247
  });
210
248
 
249
+ // admin/src/submission-page.tsx
250
+ import { useCallback, useEffect, useRef, useState } from "react";
251
+ import {
252
+ Button,
253
+ Flex as Flex2,
254
+ Table,
255
+ Tbody,
256
+ Td,
257
+ Th,
258
+ Thead,
259
+ Tr,
260
+ Typography as Typography2
261
+ } from "@strapi/design-system";
262
+ import { Layouts, Page, useFetchClient, useRBAC } from "@strapi/strapi/admin";
263
+ import { Link } from "react-router-dom";
264
+ import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
265
+ function TargetLink({ row }) {
266
+ const { allowedActions, isLoading } = useRBAC([
267
+ {
268
+ action: "plugin::content-manager.explorer.read",
269
+ subject: row.targetUid
270
+ }
271
+ ]);
272
+ if (isLoading || !allowedActions.canRead || !row.targetDocumentId)
273
+ return null;
274
+ return /* @__PURE__ */ jsx3(
275
+ Link,
276
+ {
277
+ to: `/content-manager/collection-types/${encodeURIComponent(row.targetUid)}/${encodeURIComponent(row.targetDocumentId)}`,
278
+ children: "Open submitted record"
279
+ }
280
+ );
281
+ }
282
+ function SubmissionPage({ canUpdate }) {
283
+ const client = useFetchClient();
284
+ const { get } = client;
285
+ const [slug, setSlug] = useState("");
286
+ const [formFilter, setFormFilter] = useState("");
287
+ const [status, setStatus] = useState("");
288
+ const [page, setPage] = useState(1);
289
+ const [result, setResult] = useState(null);
290
+ const [selected, setSelected] = useState(null);
291
+ const [loading, setLoading] = useState(true);
292
+ const [busy, setBusy] = useState(false);
293
+ const [error, setError] = useState("");
294
+ const lock = useRef(false);
295
+ const generation = useRef(0);
296
+ const load = useCallback(async () => {
297
+ const current = ++generation.current;
298
+ setLoading(true);
299
+ setError("");
300
+ try {
301
+ const response = (await get("/plugins/builder/submissions", {
302
+ params: {
303
+ page,
304
+ ...formFilter ? { slug: formFilter } : {},
305
+ ...status ? { status } : {}
306
+ }
307
+ })).data;
308
+ if (current === generation.current) setResult(response);
309
+ } catch (cause) {
310
+ if (current === generation.current) setError(errorMessage(cause));
311
+ } finally {
312
+ if (current === generation.current) setLoading(false);
313
+ }
314
+ }, [get, page, formFilter, status]);
315
+ useEffect(() => {
316
+ setSelected(null);
317
+ void load();
318
+ return () => {
319
+ generation.current++;
320
+ };
321
+ }, [load]);
322
+ async function mutate(action) {
323
+ if (lock.current) return;
324
+ lock.current = true;
325
+ setBusy(true);
326
+ setError("");
327
+ try {
328
+ await action();
329
+ } catch (cause) {
330
+ setError(errorMessage(cause));
331
+ } finally {
332
+ lock.current = false;
333
+ setBusy(false);
334
+ }
335
+ }
336
+ const api = submissionApi(client);
337
+ return /* @__PURE__ */ jsxs3(Fragment, { children: [
338
+ /* @__PURE__ */ jsx3(
339
+ Layouts.Header,
340
+ {
341
+ title: "Builder Submissions",
342
+ subtitle: "Review receipt metadata, delivery history and processing status."
343
+ }
344
+ ),
345
+ /* @__PURE__ */ jsx3(Layouts.Content, { children: /* @__PURE__ */ jsxs3(Flex2, { direction: "column", alignItems: "stretch", gap: 6, children: [
346
+ /* @__PURE__ */ jsx3(ErrorNotice, { message: error }),
347
+ error && /* @__PURE__ */ jsx3(Button, { disabled: loading || busy, onClick: () => void load(), children: "Retry list" }),
348
+ /* @__PURE__ */ jsx3(Section, { title: "Filters", children: /* @__PURE__ */ jsxs3(Flex2, { gap: 4, wrap: "wrap", alignItems: "end", children: [
349
+ /* @__PURE__ */ jsx3(
350
+ TextField,
351
+ {
352
+ label: "Form slug",
353
+ value: slug,
354
+ onChange: setSlug,
355
+ disabled: busy
356
+ }
357
+ ),
358
+ /* @__PURE__ */ jsx3(
359
+ Button,
360
+ {
361
+ disabled: busy,
362
+ onClick: () => {
363
+ setPage(1);
364
+ setFormFilter(slug.trim());
365
+ },
366
+ children: "Apply form filter"
367
+ }
368
+ ),
369
+ /* @__PURE__ */ jsxs3("label", { children: [
370
+ "Status",
371
+ " ",
372
+ /* @__PURE__ */ jsxs3(
373
+ "select",
374
+ {
375
+ "aria-label": "Filter status",
376
+ value: status,
377
+ disabled: busy,
378
+ onChange: (event) => {
379
+ setPage(1);
380
+ setStatus(event.target.value);
381
+ },
382
+ children: [
383
+ /* @__PURE__ */ jsx3("option", { value: "", children: "All statuses" }),
384
+ submissionStatuses.map((value) => /* @__PURE__ */ jsx3("option", { children: value }, value))
385
+ ]
386
+ }
387
+ )
388
+ ] })
389
+ ] }) }),
390
+ /* @__PURE__ */ jsxs3(Section, { title: "Submissions", children: [
391
+ loading ? /* @__PURE__ */ jsx3(Page.Loading, {}) : !result?.data.length ? /* @__PURE__ */ jsx3(Typography2, { children: "No submissions found." }) : /* @__PURE__ */ jsxs3(Table, { colCount: 5, rowCount: result.data.length, children: [
392
+ /* @__PURE__ */ jsx3(Thead, { children: /* @__PURE__ */ jsx3(Tr, { children: [
393
+ "Form",
394
+ "Received",
395
+ "Status",
396
+ "Deliveries",
397
+ "Actions"
398
+ ].map((label) => /* @__PURE__ */ jsx3(Th, { children: /* @__PURE__ */ jsx3(Typography2, { variant: "sigma", children: label }) }, label)) }) }),
399
+ /* @__PURE__ */ jsx3(Tbody, { children: result.data.map((row) => /* @__PURE__ */ jsxs3(Tr, { children: [
400
+ /* @__PURE__ */ jsx3(Td, { children: /* @__PURE__ */ jsx3(Typography2, { children: row.form }) }),
401
+ /* @__PURE__ */ jsx3(Td, { children: /* @__PURE__ */ jsx3(Typography2, { children: row.createdAt }) }),
402
+ /* @__PURE__ */ jsx3(Td, { children: /* @__PURE__ */ jsx3(Typography2, { children: row.status }) }),
403
+ /* @__PURE__ */ jsx3(Td, { children: /* @__PURE__ */ jsx3(Flex2, { direction: "column", alignItems: "start", gap: 2, children: row.deliveries.length ? row.deliveries.map((delivery, i) => /* @__PURE__ */ jsxs3(Typography2, { children: [
404
+ delivery.channel,
405
+ ": ",
406
+ delivery.status,
407
+ " \xB7",
408
+ " ",
409
+ delivery.attempts,
410
+ " attempts \xB7",
411
+ " ",
412
+ delivery.sentAt ?? "Not sent"
413
+ ] }, i)) : /* @__PURE__ */ jsx3(Typography2, { children: "No deliveries recorded" }) }) }),
414
+ /* @__PURE__ */ jsx3(Td, { children: /* @__PURE__ */ jsxs3(
415
+ Button,
416
+ {
417
+ disabled: busy,
418
+ onClick: () => void mutate(async () => {
419
+ setSelected(await api.get(row.documentId));
420
+ }),
421
+ children: [
422
+ "View ",
423
+ row.documentId
424
+ ]
425
+ }
426
+ ) })
427
+ ] }, row.documentId)) })
428
+ ] }),
429
+ /* @__PURE__ */ jsxs3(Flex2, { gap: 3, children: [
430
+ /* @__PURE__ */ jsx3(
431
+ Button,
432
+ {
433
+ disabled: busy || loading || page <= 1,
434
+ onClick: () => setPage(page - 1),
435
+ children: "Previous"
436
+ }
437
+ ),
438
+ /* @__PURE__ */ jsxs3(Typography2, { children: [
439
+ "Page ",
440
+ page,
441
+ " \xB7 ",
442
+ result?.meta.pagination.total ?? 0,
443
+ " submissions"
444
+ ] }),
445
+ /* @__PURE__ */ jsx3(
446
+ Button,
447
+ {
448
+ disabled: busy || loading || page >= (result?.meta.pagination.pageCount ?? 0),
449
+ onClick: () => setPage(page + 1),
450
+ children: "Next"
451
+ }
452
+ )
453
+ ] })
454
+ ] }),
455
+ selected && /* @__PURE__ */ jsxs3(Section, { title: `Submission ${selected.documentId}`, children: [
456
+ /* @__PURE__ */ jsxs3(Typography2, { children: [
457
+ selected.form,
458
+ " \xB7 Received ",
459
+ selected.createdAt,
460
+ " \xB7 Updated",
461
+ " ",
462
+ selected.updatedAt
463
+ ] }),
464
+ /* @__PURE__ */ jsxs3(Typography2, { children: [
465
+ "Consent ",
466
+ selected.consentVersion,
467
+ " \xB7 ",
468
+ selected.consentAt
469
+ ] }),
470
+ /* @__PURE__ */ jsx3(TargetLink, { row: selected }),
471
+ /* @__PURE__ */ jsxs3("label", { children: [
472
+ "Processing status",
473
+ " ",
474
+ /* @__PURE__ */ jsx3(
475
+ "select",
476
+ {
477
+ "aria-label": "Processing status",
478
+ value: selected.status,
479
+ disabled: !canUpdate || busy,
480
+ onChange: (event) => {
481
+ const value = submissionStatuses.find(
482
+ (value2) => value2 === event.target.value
483
+ );
484
+ if (value)
485
+ void mutate(async () => {
486
+ const updated = await api.status(
487
+ selected.documentId,
488
+ value
489
+ );
490
+ await load();
491
+ setSelected(updated);
492
+ });
493
+ },
494
+ children: submissionStatuses.map((value) => /* @__PURE__ */ jsx3("option", { children: value }, value))
495
+ }
496
+ )
497
+ ] }),
498
+ selected.deliveries.map((delivery, i) => /* @__PURE__ */ jsxs3(Typography2, { children: [
499
+ delivery.channel,
500
+ ": ",
501
+ delivery.status,
502
+ " \xB7 ",
503
+ delivery.attempts,
504
+ " ",
505
+ "attempts \xB7 Sent: ",
506
+ delivery.sentAt ?? "Not sent",
507
+ delivery.lastError ? ` \xB7 ${delivery.lastError}` : ""
508
+ ] }, i)),
509
+ /* @__PURE__ */ jsx3(
510
+ Button,
511
+ {
512
+ disabled: !canUpdate || busy || !selected.deliveries.some(
513
+ (delivery) => delivery.channel !== "afterSave" && delivery.status === "failed"
514
+ ),
515
+ onClick: () => void mutate(async () => {
516
+ await api.redeliver(selected.documentId);
517
+ const updated = await api.get(selected.documentId);
518
+ await load();
519
+ setSelected(updated);
520
+ }),
521
+ children: "Retry notifications"
522
+ }
523
+ ),
524
+ !canUpdate && /* @__PURE__ */ jsx3(Typography2, { children: "Read only. Submission update permission is required to change status or retry notifications." })
525
+ ] })
526
+ ] }) })
527
+ ] });
528
+ }
529
+ var init_submission_page = __esm({
530
+ "admin/src/submission-page.tsx"() {
531
+ "use strict";
532
+ init_api();
533
+ init_form();
534
+ init_submission();
535
+ init_ui();
536
+ }
537
+ });
538
+
211
539
  // admin/src/import-form.tsx
212
- import { useRef, useState } from "react";
213
- import { Button, Flex as Flex2, Typography as Typography2 } from "@strapi/design-system";
214
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
540
+ import { useRef as useRef2, useState as useState2 } from "react";
541
+ import { Button as Button2, Flex as Flex3, Typography as Typography3 } from "@strapi/design-system";
542
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
215
543
  function ImportForm({
216
544
  replace,
217
545
  disabled,
218
546
  onImport
219
547
  }) {
220
- const [source, setSource] = useState("");
221
- const [error, setError] = useState("");
222
- const [busy, setBusy] = useState(false);
223
- const lock = useRef(false);
548
+ const [source, setSource] = useState2("");
549
+ const [error, setError] = useState2("");
550
+ const [busy, setBusy] = useState2(false);
551
+ const lock = useRef2(false);
224
552
  async function submit() {
225
553
  if (lock.current) return;
226
554
  lock.current = true;
@@ -235,16 +563,16 @@ function ImportForm({
235
563
  setBusy(false);
236
564
  }
237
565
  }
238
- return /* @__PURE__ */ jsxs3(Flex2, { direction: "column", alignItems: "stretch", gap: 3, children: [
239
- /* @__PURE__ */ jsxs3(Typography2, { children: [
566
+ return /* @__PURE__ */ jsxs4(Flex3, { direction: "column", alignItems: "stretch", gap: 3, children: [
567
+ /* @__PURE__ */ jsxs4(Typography3, { children: [
240
568
  replace ? "Replace this saved form with an exported definition. The slug must match. The server increments the form version." : "Import a form export. A new form starts at version 1.",
241
569
  " ",
242
570
  "Consent version is preserved. Existing slugs cannot be duplicated."
243
571
  ] }),
244
- /* @__PURE__ */ jsxs3("label", { children: [
572
+ /* @__PURE__ */ jsxs4("label", { children: [
245
573
  "Choose JSON file",
246
574
  " ",
247
- /* @__PURE__ */ jsx3(
575
+ /* @__PURE__ */ jsx4(
248
576
  "input",
249
577
  {
250
578
  type: "file",
@@ -259,7 +587,7 @@ function ImportForm({
259
587
  }
260
588
  )
261
589
  ] }),
262
- /* @__PURE__ */ jsx3(
590
+ /* @__PURE__ */ jsx4(
263
591
  TextField,
264
592
  {
265
593
  label: "Form JSON",
@@ -269,9 +597,9 @@ function ImportForm({
269
597
  disabled: busy || disabled
270
598
  }
271
599
  ),
272
- /* @__PURE__ */ jsx3(ErrorNotice, { message: error }),
273
- /* @__PURE__ */ jsx3(
274
- Button,
600
+ /* @__PURE__ */ jsx4(ErrorNotice, { message: error }),
601
+ /* @__PURE__ */ jsx4(
602
+ Button2,
275
603
  {
276
604
  type: "button",
277
605
  disabled: busy || disabled || !source.trim(),
@@ -290,32 +618,32 @@ var init_import_form = __esm({
290
618
  });
291
619
 
292
620
  // admin/src/form-list-page.tsx
293
- import { useCallback, useEffect, useRef as useRef2, useState as useState2 } from "react";
621
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef3, useState as useState3 } from "react";
294
622
  import {
295
- Button as Button2,
296
- Flex as Flex3,
297
- Table,
298
- Tbody,
299
- Td,
300
- Th,
301
- Thead,
302
- Tr,
303
- Typography as Typography3
623
+ Button as Button3,
624
+ Flex as Flex4,
625
+ Table as Table2,
626
+ Tbody as Tbody2,
627
+ Td as Td2,
628
+ Th as Th2,
629
+ Thead as Thead2,
630
+ Tr as Tr2,
631
+ Typography as Typography4
304
632
  } from "@strapi/design-system";
305
- import { Layouts, Page, useFetchClient } from "@strapi/strapi/admin";
633
+ import { Layouts as Layouts2, Page as Page2, useFetchClient as useFetchClient2 } from "@strapi/strapi/admin";
306
634
  import { useNavigate } from "react-router-dom";
307
- import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
635
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
308
636
  function FormListPage({ canUpdate }) {
309
- const client = useFetchClient();
637
+ const client = useFetchClient2();
310
638
  const { get } = client;
311
639
  const navigate = useNavigate();
312
- const [rows, setRows] = useState2([]);
313
- const [loading, setLoading] = useState2(true);
314
- const [error, setError] = useState2("");
315
- const [busy, setBusy] = useState2(false);
316
- const [removing, setRemoving] = useState2(null);
317
- const lock = useRef2(false);
318
- const load = useCallback(async () => {
640
+ const [rows, setRows] = useState3([]);
641
+ const [loading, setLoading] = useState3(true);
642
+ const [error, setError] = useState3("");
643
+ const [busy, setBusy] = useState3(false);
644
+ const [removing, setRemoving] = useState3(null);
645
+ const lock = useRef3(false);
646
+ const load = useCallback2(async () => {
319
647
  setLoading(true);
320
648
  setError("");
321
649
  try {
@@ -328,7 +656,7 @@ function FormListPage({ canUpdate }) {
328
656
  setLoading(false);
329
657
  }
330
658
  }, [get]);
331
- useEffect(() => {
659
+ useEffect2(() => {
332
660
  void load();
333
661
  }, [load]);
334
662
  async function mutate(action) {
@@ -345,14 +673,14 @@ function FormListPage({ canUpdate }) {
345
673
  setBusy(false);
346
674
  }
347
675
  }
348
- return /* @__PURE__ */ jsxs4(Fragment, { children: [
349
- /* @__PURE__ */ jsx4(
350
- Layouts.Header,
676
+ return /* @__PURE__ */ jsxs5(Fragment2, { children: [
677
+ /* @__PURE__ */ jsx5(
678
+ Layouts2.Header,
351
679
  {
352
680
  title: "Builder Forms",
353
681
  subtitle: "Manage public form definitions and consent.",
354
- primaryAction: canUpdate ? /* @__PURE__ */ jsx4(
355
- Button2,
682
+ primaryAction: canUpdate ? /* @__PURE__ */ jsx5(
683
+ Button3,
356
684
  {
357
685
  disabled: busy,
358
686
  onClick: () => navigate("/plugins/builder/forms/new"),
@@ -361,21 +689,21 @@ function FormListPage({ canUpdate }) {
361
689
  ) : void 0
362
690
  }
363
691
  ),
364
- /* @__PURE__ */ jsx4(Layouts.Content, { children: /* @__PURE__ */ jsxs4(Flex3, { direction: "column", alignItems: "stretch", gap: 6, children: [
365
- /* @__PURE__ */ jsx4(ErrorNotice, { message: error }),
366
- error && /* @__PURE__ */ jsx4(Button2, { onClick: () => void load(), disabled: busy || loading, children: "Retry list" }),
367
- /* @__PURE__ */ jsx4(Section, { title: "Forms", children: loading ? /* @__PURE__ */ jsx4(Page.Loading, {}) : rows.length === 0 ? /* @__PURE__ */ jsx4(Typography3, { children: "No forms yet." }) : /* @__PURE__ */ jsxs4(Table, { colCount: 5, rowCount: rows.length, children: [
368
- /* @__PURE__ */ jsx4(Thead, { children: /* @__PURE__ */ jsx4(Tr, { children: ["Name", "Slug", "Target", "Version", "Actions"].map(
369
- (name) => /* @__PURE__ */ jsx4(Th, { children: /* @__PURE__ */ jsx4(Typography3, { variant: "sigma", children: name }) }, name)
692
+ /* @__PURE__ */ jsx5(Layouts2.Content, { children: /* @__PURE__ */ jsxs5(Flex4, { direction: "column", alignItems: "stretch", gap: 6, children: [
693
+ /* @__PURE__ */ jsx5(ErrorNotice, { message: error }),
694
+ error && /* @__PURE__ */ jsx5(Button3, { onClick: () => void load(), disabled: busy || loading, children: "Retry list" }),
695
+ /* @__PURE__ */ jsx5(Section, { title: "Forms", children: loading ? /* @__PURE__ */ jsx5(Page2.Loading, {}) : rows.length === 0 ? /* @__PURE__ */ jsx5(Typography4, { children: "No forms yet." }) : /* @__PURE__ */ jsxs5(Table2, { colCount: 5, rowCount: rows.length, children: [
696
+ /* @__PURE__ */ jsx5(Thead2, { children: /* @__PURE__ */ jsx5(Tr2, { children: ["Name", "Slug", "Target", "Version", "Actions"].map(
697
+ (name) => /* @__PURE__ */ jsx5(Th2, { children: /* @__PURE__ */ jsx5(Typography4, { variant: "sigma", children: name }) }, name)
370
698
  ) }) }),
371
- /* @__PURE__ */ jsx4(Tbody, { children: rows.map((row) => /* @__PURE__ */ jsxs4(Tr, { children: [
372
- /* @__PURE__ */ jsx4(Td, { children: /* @__PURE__ */ jsx4(Typography3, { children: row.name }) }),
373
- /* @__PURE__ */ jsx4(Td, { children: /* @__PURE__ */ jsx4(Typography3, { children: row.slug }) }),
374
- /* @__PURE__ */ jsx4(Td, { children: /* @__PURE__ */ jsx4(Typography3, { children: row.targetUid }) }),
375
- /* @__PURE__ */ jsx4(Td, { children: /* @__PURE__ */ jsx4(Typography3, { children: row.version }) }),
376
- /* @__PURE__ */ jsx4(Td, { children: /* @__PURE__ */ jsxs4(Flex3, { gap: 2, children: [
377
- /* @__PURE__ */ jsxs4(
378
- Button2,
699
+ /* @__PURE__ */ jsx5(Tbody2, { children: rows.map((row) => /* @__PURE__ */ jsxs5(Tr2, { children: [
700
+ /* @__PURE__ */ jsx5(Td2, { children: /* @__PURE__ */ jsx5(Typography4, { children: row.name }) }),
701
+ /* @__PURE__ */ jsx5(Td2, { children: /* @__PURE__ */ jsx5(Typography4, { children: row.slug }) }),
702
+ /* @__PURE__ */ jsx5(Td2, { children: /* @__PURE__ */ jsx5(Typography4, { children: row.targetUid }) }),
703
+ /* @__PURE__ */ jsx5(Td2, { children: /* @__PURE__ */ jsx5(Typography4, { children: row.version }) }),
704
+ /* @__PURE__ */ jsx5(Td2, { children: /* @__PURE__ */ jsxs5(Flex4, { gap: 2, children: [
705
+ /* @__PURE__ */ jsxs5(
706
+ Button3,
379
707
  {
380
708
  variant: "secondary",
381
709
  disabled: busy,
@@ -389,8 +717,8 @@ function FormListPage({ canUpdate }) {
389
717
  ]
390
718
  }
391
719
  ),
392
- /* @__PURE__ */ jsx4(
393
- Button2,
720
+ /* @__PURE__ */ jsx5(
721
+ Button3,
394
722
  {
395
723
  variant: "tertiary",
396
724
  disabled: busy,
@@ -403,8 +731,8 @@ function FormListPage({ canUpdate }) {
403
731
  children: "Export"
404
732
  }
405
733
  ),
406
- canUpdate && /* @__PURE__ */ jsx4(
407
- Button2,
734
+ canUpdate && /* @__PURE__ */ jsx5(
735
+ Button3,
408
736
  {
409
737
  variant: "danger-light",
410
738
  disabled: busy,
@@ -415,11 +743,11 @@ function FormListPage({ canUpdate }) {
415
743
  ] }) })
416
744
  ] }, row.documentId)) })
417
745
  ] }) }),
418
- removing && /* @__PURE__ */ jsxs4(Section, { title: `Delete ${removing.name}?`, children: [
419
- /* @__PURE__ */ jsx4(Typography3, { children: "This permanently deletes the form definition. Forms with submissions cannot be deleted." }),
420
- /* @__PURE__ */ jsxs4(Flex3, { gap: 3, children: [
421
- /* @__PURE__ */ jsx4(
422
- Button2,
746
+ removing && /* @__PURE__ */ jsxs5(Section, { title: `Delete ${removing.name}?`, children: [
747
+ /* @__PURE__ */ jsx5(Typography4, { children: "This permanently deletes the form definition. Forms with submissions cannot be deleted." }),
748
+ /* @__PURE__ */ jsxs5(Flex4, { gap: 3, children: [
749
+ /* @__PURE__ */ jsx5(
750
+ Button3,
423
751
  {
424
752
  variant: "danger",
425
753
  disabled: busy,
@@ -431,8 +759,8 @@ function FormListPage({ canUpdate }) {
431
759
  children: "Confirm delete"
432
760
  }
433
761
  ),
434
- /* @__PURE__ */ jsx4(
435
- Button2,
762
+ /* @__PURE__ */ jsx5(
763
+ Button3,
436
764
  {
437
765
  variant: "secondary",
438
766
  disabled: busy,
@@ -442,7 +770,7 @@ function FormListPage({ canUpdate }) {
442
770
  )
443
771
  ] })
444
772
  ] }),
445
- canUpdate && /* @__PURE__ */ jsx4(Section, { title: "Import form", children: /* @__PURE__ */ jsx4(
773
+ canUpdate && /* @__PURE__ */ jsx5(Section, { title: "Import form", children: /* @__PURE__ */ jsx5(
446
774
  ImportForm,
447
775
  {
448
776
  disabled: busy,
@@ -474,8 +802,8 @@ var init_form_list_page = __esm({
474
802
  });
475
803
 
476
804
  // admin/src/field-editor.tsx
477
- import { Button as Button3, Flex as Flex4, Typography as Typography4 } from "@strapi/design-system";
478
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
805
+ import { Button as Button4, Flex as Flex5, Typography as Typography5 } from "@strapi/design-system";
806
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
479
807
  function FieldEditor({
480
808
  fields,
481
809
  schema,
@@ -485,10 +813,10 @@ function FieldEditor({
485
813
  function update(index, field) {
486
814
  onChange(fields.map((item, at) => at === index ? field : item));
487
815
  }
488
- return /* @__PURE__ */ jsxs5(Section, { title: "Fields", children: [
489
- /* @__PURE__ */ jsx5(Typography4, { children: "Select target fields, then adjust their order and presentation. Required target fields cannot be removed or made optional. Hidden fields remain part of submission validation." }),
490
- /* @__PURE__ */ jsx5(Flex4, { wrap: "wrap", gap: 4, children: schema.map((item) => /* @__PURE__ */ jsxs5("label", { children: [
491
- /* @__PURE__ */ jsx5(
816
+ return /* @__PURE__ */ jsxs6(Section, { title: "Fields", children: [
817
+ /* @__PURE__ */ jsx6(Typography5, { children: "Select target fields, then adjust their order and presentation. Required target fields cannot be removed or made optional. Hidden fields remain part of submission validation." }),
818
+ /* @__PURE__ */ jsx6(Flex5, { wrap: "wrap", gap: 4, children: schema.map((item) => /* @__PURE__ */ jsxs6("label", { children: [
819
+ /* @__PURE__ */ jsx6(
492
820
  "input",
493
821
  {
494
822
  type: "checkbox",
@@ -505,14 +833,14 @@ function FieldEditor({
505
833
  ] }, item.name)) }),
506
834
  fields.map((field, index) => {
507
835
  const attribute = schema.find((item) => item.name === field.name);
508
- return /* @__PURE__ */ jsxs5(
836
+ return /* @__PURE__ */ jsxs6(
509
837
  Section,
510
838
  {
511
839
  title: `${index + 1}. ${field.name} (${field.type})`,
512
840
  children: [
513
- /* @__PURE__ */ jsxs5(Flex4, { gap: 3, children: [
514
- /* @__PURE__ */ jsxs5(
515
- Button3,
841
+ /* @__PURE__ */ jsxs6(Flex5, { gap: 3, children: [
842
+ /* @__PURE__ */ jsxs6(
843
+ Button4,
516
844
  {
517
845
  type: "button",
518
846
  variant: "secondary",
@@ -525,8 +853,8 @@ function FieldEditor({
525
853
  ]
526
854
  }
527
855
  ),
528
- /* @__PURE__ */ jsxs5(
529
- Button3,
856
+ /* @__PURE__ */ jsxs6(
857
+ Button4,
530
858
  {
531
859
  type: "button",
532
860
  variant: "secondary",
@@ -540,7 +868,7 @@ function FieldEditor({
540
868
  }
541
869
  )
542
870
  ] }),
543
- /* @__PURE__ */ jsx5(
871
+ /* @__PURE__ */ jsx6(
544
872
  TextField,
545
873
  {
546
874
  label: "Label",
@@ -550,7 +878,7 @@ function FieldEditor({
550
878
  onChange: (label) => update(index, { ...field, label })
551
879
  }
552
880
  ),
553
- /* @__PURE__ */ jsx5(
881
+ /* @__PURE__ */ jsx6(
554
882
  TextField,
555
883
  {
556
884
  label: "Placeholder",
@@ -559,7 +887,7 @@ function FieldEditor({
559
887
  onChange: (placeholder) => update(index, { ...field, placeholder })
560
888
  }
561
889
  ),
562
- /* @__PURE__ */ jsx5(
890
+ /* @__PURE__ */ jsx6(
563
891
  TextField,
564
892
  {
565
893
  label: "Help text",
@@ -568,8 +896,8 @@ function FieldEditor({
568
896
  onChange: (help) => update(index, { ...field, help })
569
897
  }
570
898
  ),
571
- /* @__PURE__ */ jsxs5("label", { children: [
572
- /* @__PURE__ */ jsx5(
899
+ /* @__PURE__ */ jsxs6("label", { children: [
900
+ /* @__PURE__ */ jsx6(
573
901
  "input",
574
902
  {
575
903
  type: "checkbox",
@@ -588,8 +916,8 @@ function FieldEditor({
588
916
  "Required",
589
917
  attribute?.required ? " (required by target schema)" : ""
590
918
  ] }),
591
- /* @__PURE__ */ jsxs5("label", { children: [
592
- /* @__PURE__ */ jsx5(
919
+ /* @__PURE__ */ jsxs6("label", { children: [
920
+ /* @__PURE__ */ jsx6(
593
921
  "input",
594
922
  {
595
923
  type: "checkbox",
@@ -604,7 +932,7 @@ function FieldEditor({
604
932
  " ",
605
933
  "Hidden from form"
606
934
  ] }),
607
- (field.maxLength !== void 0 || field.min !== void 0 || field.max !== void 0) && /* @__PURE__ */ jsxs5(Typography4, { variant: "pi", children: [
935
+ (field.maxLength !== void 0 || field.min !== void 0 || field.max !== void 0) && /* @__PURE__ */ jsxs6(Typography5, { variant: "pi", children: [
608
936
  "Constraints:",
609
937
  " ",
610
938
  JSON.stringify({
@@ -613,7 +941,7 @@ function FieldEditor({
613
941
  max: field.max
614
942
  })
615
943
  ] }),
616
- field.options?.map((option, optionIndex) => /* @__PURE__ */ jsx5(
944
+ field.options?.map((option, optionIndex) => /* @__PURE__ */ jsx6(
617
945
  TextField,
618
946
  {
619
947
  label: `Option label: ${option.value}`,
@@ -722,9 +1050,9 @@ var init_notifications = __esm({
722
1050
  });
723
1051
 
724
1052
  // admin/src/notification-editor.tsx
725
- import { useState as useState3 } from "react";
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";
1053
+ import { useState as useState4 } from "react";
1054
+ import { Button as Button5, Flex as Flex6, Typography as Typography6 } from "@strapi/design-system";
1055
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
728
1056
  function NotificationEditor({
729
1057
  value,
730
1058
  fields,
@@ -734,7 +1062,7 @@ function NotificationEditor({
734
1062
  disabled,
735
1063
  saveError
736
1064
  }) {
737
- const [target, setTarget] = useState3("body");
1065
+ const [target, setTarget] = useState4("body");
738
1066
  const errors = notificationErrors(value, fields);
739
1067
  function toggle(channel, enabled) {
740
1068
  const next = { ...value };
@@ -749,9 +1077,9 @@ function NotificationEditor({
749
1077
  else next.webhook = { url: "" };
750
1078
  onChange(next);
751
1079
  }
752
- return /* @__PURE__ */ jsxs6(Section, { title: "Notifications", children: [
753
- /* @__PURE__ */ jsx6(
754
- Button4,
1080
+ return /* @__PURE__ */ jsxs7(Section, { title: "Notifications", children: [
1081
+ /* @__PURE__ */ jsx7(
1082
+ Button5,
755
1083
  {
756
1084
  type: "button",
757
1085
  variant: "tertiary",
@@ -760,11 +1088,11 @@ function NotificationEditor({
760
1088
  children: opened ? "Notification settings opened" : "Open notification settings"
761
1089
  }
762
1090
  ),
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(
1091
+ opened && /* @__PURE__ */ jsxs7(Fragment3, { children: [
1092
+ /* @__PURE__ */ jsx7(Typography6, { variant: "pi", children: "Enable a channel to send notifications. Changes take effect after saving the form." }),
1093
+ saveError.startsWith("notifications:") && /* @__PURE__ */ jsx7(ErrorNotice, { message: saveError }),
1094
+ /* @__PURE__ */ jsxs7("label", { children: [
1095
+ /* @__PURE__ */ jsx7(
768
1096
  "input",
769
1097
  {
770
1098
  type: "checkbox",
@@ -776,8 +1104,8 @@ function NotificationEditor({
776
1104
  " ",
777
1105
  "Email notifications"
778
1106
  ] }),
779
- value.email && /* @__PURE__ */ jsxs6(Fragment2, { children: [
780
- ["to", "cc"].map((key) => /* @__PURE__ */ jsx6(
1107
+ value.email && /* @__PURE__ */ jsxs7(Fragment3, { children: [
1108
+ ["to", "cc"].map((key) => /* @__PURE__ */ jsx7(
781
1109
  TextField,
782
1110
  {
783
1111
  label: key === "to" ? "To (one address per line)" : "Cc (one address per line)",
@@ -792,10 +1120,10 @@ function NotificationEditor({
792
1120
  },
793
1121
  key
794
1122
  )),
795
- /* @__PURE__ */ jsx6(Typography5, { variant: "pi", children: "Use fixed email addresses. Empty lines are ignored when saving." }),
796
- /* @__PURE__ */ jsxs6("label", { children: [
1123
+ /* @__PURE__ */ jsx7(Typography6, { variant: "pi", children: "Use fixed email addresses. Empty lines are ignored when saving." }),
1124
+ /* @__PURE__ */ jsxs7("label", { children: [
797
1125
  "Reply-to field",
798
- /* @__PURE__ */ jsxs6(
1126
+ /* @__PURE__ */ jsxs7(
799
1127
  "select",
800
1128
  {
801
1129
  style: { width: "100%", padding: 10 },
@@ -810,8 +1138,8 @@ function NotificationEditor({
810
1138
  onChange({ ...value, email });
811
1139
  },
812
1140
  children: [
813
- /* @__PURE__ */ jsx6("option", { value: "", children: "None" }),
814
- fields.filter((field) => field.type === "email").map((field) => /* @__PURE__ */ jsxs6("option", { value: field.name, children: [
1141
+ /* @__PURE__ */ jsx7("option", { value: "", children: "None" }),
1142
+ fields.filter((field) => field.type === "email").map((field) => /* @__PURE__ */ jsxs7("option", { value: field.name, children: [
815
1143
  field.label,
816
1144
  " (",
817
1145
  field.name,
@@ -821,8 +1149,8 @@ function NotificationEditor({
821
1149
  }
822
1150
  )
823
1151
  ] }),
824
- errors.replyToField && /* @__PURE__ */ jsx6(ErrorNotice, { message: errors.replyToField }),
825
- /* @__PURE__ */ jsx6(
1152
+ errors.replyToField && /* @__PURE__ */ jsx7(ErrorNotice, { message: errors.replyToField }),
1153
+ /* @__PURE__ */ jsx7(
826
1154
  TextField,
827
1155
  {
828
1156
  label: "Email subject",
@@ -832,7 +1160,7 @@ function NotificationEditor({
832
1160
  onChange: (subject) => onChange({ ...value, email: { ...value.email, subject } })
833
1161
  }
834
1162
  ),
835
- /* @__PURE__ */ jsx6(
1163
+ /* @__PURE__ */ jsx7(
836
1164
  TextField,
837
1165
  {
838
1166
  label: "Email body",
@@ -843,9 +1171,9 @@ function NotificationEditor({
843
1171
  onChange: (body) => onChange({ ...value, email: { ...value.email, body } })
844
1172
  }
845
1173
  ),
846
- /* @__PURE__ */ jsxs6("label", { children: [
1174
+ /* @__PURE__ */ jsxs7("label", { children: [
847
1175
  "Insert placeholder into",
848
- /* @__PURE__ */ jsxs6(
1176
+ /* @__PURE__ */ jsxs7(
849
1177
  "select",
850
1178
  {
851
1179
  style: { width: "100%", padding: 10 },
@@ -855,14 +1183,14 @@ function NotificationEditor({
855
1183
  event.target.value === "subject" ? "subject" : "body"
856
1184
  ),
857
1185
  children: [
858
- /* @__PURE__ */ jsx6("option", { value: "subject", children: "Subject" }),
859
- /* @__PURE__ */ jsx6("option", { value: "body", children: "Body" })
1186
+ /* @__PURE__ */ jsx7("option", { value: "subject", children: "Subject" }),
1187
+ /* @__PURE__ */ jsx7("option", { value: "body", children: "Body" })
860
1188
  ]
861
1189
  }
862
1190
  )
863
1191
  ] }),
864
- /* @__PURE__ */ jsx6(Flex5, { gap: 2, wrap: "wrap", children: placeholders(fields).map((key) => /* @__PURE__ */ jsx6(
865
- Button4,
1192
+ /* @__PURE__ */ jsx7(Flex6, { gap: 2, wrap: "wrap", children: placeholders(fields).map((key) => /* @__PURE__ */ jsx7(
1193
+ Button5,
866
1194
  {
867
1195
  type: "button",
868
1196
  style: {
@@ -886,8 +1214,8 @@ function NotificationEditor({
886
1214
  key
887
1215
  )) })
888
1216
  ] }),
889
- /* @__PURE__ */ jsxs6("label", { children: [
890
- /* @__PURE__ */ jsx6(
1217
+ /* @__PURE__ */ jsxs7("label", { children: [
1218
+ /* @__PURE__ */ jsx7(
891
1219
  "input",
892
1220
  {
893
1221
  type: "checkbox",
@@ -899,8 +1227,8 @@ function NotificationEditor({
899
1227
  " ",
900
1228
  "Webhook notifications"
901
1229
  ] }),
902
- value.webhook && /* @__PURE__ */ jsxs6(Fragment2, { children: [
903
- /* @__PURE__ */ jsx6(
1230
+ value.webhook && /* @__PURE__ */ jsxs7(Fragment3, { children: [
1231
+ /* @__PURE__ */ jsx7(
904
1232
  TextField,
905
1233
  {
906
1234
  label: "Webhook URL",
@@ -910,7 +1238,7 @@ function NotificationEditor({
910
1238
  onChange: (url) => onChange({ ...value, webhook: { ...value.webhook, url } })
911
1239
  }
912
1240
  ),
913
- /* @__PURE__ */ jsx6(
1241
+ /* @__PURE__ */ jsx7(
914
1242
  TextField,
915
1243
  {
916
1244
  label: "Webhook secret",
@@ -925,16 +1253,16 @@ function NotificationEditor({
925
1253
  }
926
1254
  }
927
1255
  ),
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." }),
1256
+ /* @__PURE__ */ jsx7(Typography6, { 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
1257
  Object.entries(value.webhook.headers ?? {}).map(
930
- ([name, header]) => /* @__PURE__ */ jsxs6(
931
- Flex5,
1258
+ ([name, header]) => /* @__PURE__ */ jsxs7(
1259
+ Flex6,
932
1260
  {
933
1261
  direction: "column",
934
1262
  alignItems: "stretch",
935
1263
  gap: 2,
936
1264
  children: [
937
- /* @__PURE__ */ jsx6(
1265
+ /* @__PURE__ */ jsx7(
938
1266
  TextField,
939
1267
  {
940
1268
  label: `Header: ${name}`,
@@ -953,8 +1281,8 @@ function NotificationEditor({
953
1281
  })
954
1282
  }
955
1283
  ),
956
- /* @__PURE__ */ jsxs6(
957
- Button4,
1284
+ /* @__PURE__ */ jsxs7(
1285
+ Button5,
958
1286
  {
959
1287
  type: "button",
960
1288
  variant: "tertiary",
@@ -978,7 +1306,7 @@ function NotificationEditor({
978
1306
  name
979
1307
  )
980
1308
  ),
981
- /* @__PURE__ */ jsx6(
1309
+ /* @__PURE__ */ jsx7(
982
1310
  AddHeader,
983
1311
  {
984
1312
  disabled,
@@ -1001,10 +1329,10 @@ function AddHeader({
1001
1329
  names,
1002
1330
  onAdd
1003
1331
  }) {
1004
- const [name, setName] = useState3("");
1005
- const [value, setValue] = useState3("");
1006
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1007
- /* @__PURE__ */ jsx6(
1332
+ const [name, setName] = useState4("");
1333
+ const [value, setValue] = useState4("");
1334
+ return /* @__PURE__ */ jsxs7(Fragment3, { children: [
1335
+ /* @__PURE__ */ jsx7(
1008
1336
  TextField,
1009
1337
  {
1010
1338
  label: "New header name",
@@ -1013,7 +1341,7 @@ function AddHeader({
1013
1341
  onChange: setName
1014
1342
  }
1015
1343
  ),
1016
- /* @__PURE__ */ jsx6(
1344
+ /* @__PURE__ */ jsx7(
1017
1345
  TextField,
1018
1346
  {
1019
1347
  label: "New header value",
@@ -1023,8 +1351,8 @@ function AddHeader({
1023
1351
  onChange: setValue
1024
1352
  }
1025
1353
  ),
1026
- /* @__PURE__ */ jsx6(
1027
- Button4,
1354
+ /* @__PURE__ */ jsx7(
1355
+ Button5,
1028
1356
  {
1029
1357
  type: "button",
1030
1358
  variant: "tertiary",
@@ -1044,37 +1372,37 @@ function NotificationTest({
1044
1372
  disabled,
1045
1373
  send
1046
1374
  }) {
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: [
1375
+ const [values, setValues] = useState4({});
1376
+ const [results, setResults] = useState4(null);
1377
+ const [error, setError] = useState4("");
1378
+ const [errors, setErrors] = useState4({});
1379
+ const [busy, setBusy] = useState4(false);
1380
+ return /* @__PURE__ */ jsxs7(Section, { title: "Test notifications", children: [
1381
+ /* @__PURE__ */ jsx7(Typography6, { 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." }),
1382
+ disabled && /* @__PURE__ */ jsx7(Typography6, { variant: "pi", children: "Testing requires a saved form, no unsaved changes and forms.update permission." }),
1383
+ fields.map((field) => /* @__PURE__ */ jsx7("div", { style: { display: "grid", gap: 6 }, children: field.type === "boolean" ? /* @__PURE__ */ jsxs7("label", { children: [
1384
+ /* @__PURE__ */ jsxs7(Typography6, { children: [
1057
1385
  field.label,
1058
1386
  " (",
1059
1387
  field.name,
1060
1388
  ")",
1061
1389
  field.visibility === "hidden" ? " \xB7 hidden" : ""
1062
1390
  ] }),
1063
- /* @__PURE__ */ jsxs6(
1391
+ /* @__PURE__ */ jsxs7(
1064
1392
  "select",
1065
1393
  {
1066
1394
  disabled: disabled || busy,
1067
1395
  value: values[field.name] ?? "",
1068
1396
  onChange: (event) => setValues({ ...values, [field.name]: event.target.value }),
1069
1397
  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" })
1398
+ /* @__PURE__ */ jsx7("option", { value: "", children: "Choose a value" }),
1399
+ /* @__PURE__ */ jsx7("option", { value: "true", children: "True" }),
1400
+ /* @__PURE__ */ jsx7("option", { value: "false", children: "False" })
1073
1401
  ]
1074
1402
  }
1075
1403
  ),
1076
- errors[field.name] && /* @__PURE__ */ jsx6(ErrorNotice, { message: errors[field.name] ?? "" })
1077
- ] }) : /* @__PURE__ */ jsx6(
1404
+ errors[field.name] && /* @__PURE__ */ jsx7(ErrorNotice, { message: errors[field.name] ?? "" })
1405
+ ] }) : /* @__PURE__ */ jsx7(
1078
1406
  TextField,
1079
1407
  {
1080
1408
  label: `${field.label} (${field.name})${field.visibility === "hidden" ? " \xB7 hidden" : ""}`,
@@ -1084,8 +1412,8 @@ function NotificationTest({
1084
1412
  onChange: (next) => setValues({ ...values, [field.name]: next })
1085
1413
  }
1086
1414
  ) }, field.name)),
1087
- /* @__PURE__ */ jsx6(
1088
- Button4,
1415
+ /* @__PURE__ */ jsx7(
1416
+ Button5,
1089
1417
  {
1090
1418
  type: "button",
1091
1419
  disabled: disabled || busy,
@@ -1106,15 +1434,15 @@ function NotificationTest({
1106
1434
  children: busy ? "Sending test\u2026" : "Send test notifications"
1107
1435
  }
1108
1436
  ),
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(
1437
+ /* @__PURE__ */ jsx7(ErrorNotice, { message: error }),
1438
+ /* @__PURE__ */ jsxs7("div", { "aria-live": "polite", children: [
1439
+ results?.length === 0 && /* @__PURE__ */ jsx7(Typography6, { children: "No notification channels are enabled in the saved settings." }),
1440
+ results?.map((delivery) => /* @__PURE__ */ jsx7(
1113
1441
  "div",
1114
1442
  {
1115
1443
  style: { overflowWrap: "anywhere", marginTop: 12 },
1116
- children: /* @__PURE__ */ jsxs6(
1117
- Typography5,
1444
+ children: /* @__PURE__ */ jsxs7(
1445
+ Typography6,
1118
1446
  {
1119
1447
  textColor: delivery.status === "sent" ? "success700" : "danger700",
1120
1448
  children: [
@@ -1147,31 +1475,31 @@ var init_notification_editor = __esm({
1147
1475
  });
1148
1476
 
1149
1477
  // 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";
1152
- import { Layouts as Layouts2, Page as Page2, useFetchClient as useFetchClient2 } from "@strapi/strapi/admin";
1478
+ import { useEffect as useEffect3, useRef as useRef4, useState as useState5 } from "react";
1479
+ import { Button as Button6, Flex as Flex7, Typography as Typography7 } from "@strapi/design-system";
1480
+ import { Layouts as Layouts3, Page as Page3, useFetchClient as useFetchClient3 } from "@strapi/strapi/admin";
1153
1481
  import { useNavigate as useNavigate2, useParams } from "react-router-dom";
1154
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1482
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1155
1483
  function FormEditorPage({ canUpdate }) {
1156
1484
  const { documentId } = useParams();
1157
- const client = useFetchClient2();
1485
+ const client = useFetchClient3();
1158
1486
  const { get } = client;
1159
1487
  const navigate = useNavigate2();
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);
1172
- const lock = useRef3(false);
1173
- const targetRequest = useRef3(0);
1174
- useEffect2(() => {
1488
+ const [draft, setDraft] = useState5(emptyForm);
1489
+ const [notificationsOpened, setNotificationsOpened] = useState5(false);
1490
+ const [savedDraft, setSavedDraft] = useState5("");
1491
+ const [version, setVersion] = useState5(null);
1492
+ const [targets, setTargets] = useState5([]);
1493
+ const [schema, setSchema] = useState5([]);
1494
+ const [loading, setLoading] = useState5(true);
1495
+ const [fieldsLoading, setFieldsLoading] = useState5(false);
1496
+ const [error, setError] = useState5("");
1497
+ const [loaded, setLoaded] = useState5(false);
1498
+ const [busy, setBusy] = useState5(false);
1499
+ const [retry, setRetry] = useState5(0);
1500
+ const lock = useRef4(false);
1501
+ const targetRequest = useRef4(0);
1502
+ useEffect3(() => {
1175
1503
  let active = true;
1176
1504
  setLoading(true);
1177
1505
  setNotificationsOpened(false);
@@ -1244,16 +1572,16 @@ function FormEditorPage({ canUpdate }) {
1244
1572
  setBusy(false);
1245
1573
  }
1246
1574
  }
1247
- if (loading) return /* @__PURE__ */ jsx7(Page2.Loading, {});
1575
+ if (loading) return /* @__PURE__ */ jsx8(Page3.Loading, {});
1248
1576
  const disabled = !canUpdate || busy || fieldsLoading;
1249
- return /* @__PURE__ */ jsxs7(Fragment3, { children: [
1250
- /* @__PURE__ */ jsx7(
1251
- Layouts2.Header,
1577
+ return /* @__PURE__ */ jsxs8(Fragment4, { children: [
1578
+ /* @__PURE__ */ jsx8(
1579
+ Layouts3.Header,
1252
1580
  {
1253
1581
  title: documentId ? draft.name || "Form" : "Create form",
1254
1582
  subtitle: version ? `Form version ${version} \xB7 assigned by server on every save` : "New forms start at version 1.",
1255
- navigationAction: /* @__PURE__ */ jsx7(
1256
- Button5,
1583
+ navigationAction: /* @__PURE__ */ jsx8(
1584
+ Button6,
1257
1585
  {
1258
1586
  variant: "tertiary",
1259
1587
  onClick: () => navigate("/plugins/builder/forms"),
@@ -1262,10 +1590,10 @@ function FormEditorPage({ canUpdate }) {
1262
1590
  )
1263
1591
  }
1264
1592
  ),
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(
1593
+ /* @__PURE__ */ jsx8(Layouts3.Content, { children: /* @__PURE__ */ jsxs8(Flex7, { direction: "column", alignItems: "stretch", gap: 6, children: [
1594
+ /* @__PURE__ */ jsx8(ErrorNotice, { message: error }),
1595
+ !loaded ? /* @__PURE__ */ jsx8(Button6, { onClick: () => setRetry((value) => value + 1), children: "Retry loading form" }) : /* @__PURE__ */ jsxs8(Fragment4, { children: [
1596
+ /* @__PURE__ */ jsx8(
1269
1597
  "form",
1270
1598
  {
1271
1599
  onSubmit: (event) => {
@@ -1286,9 +1614,9 @@ function FormEditorPage({ canUpdate }) {
1286
1614
  });
1287
1615
  });
1288
1616
  },
1289
- children: /* @__PURE__ */ jsxs7(Flex6, { direction: "column", alignItems: "stretch", gap: 6, children: [
1290
- /* @__PURE__ */ jsxs7(Section, { title: "Definition", children: [
1291
- /* @__PURE__ */ jsx7(
1617
+ children: /* @__PURE__ */ jsxs8(Flex7, { direction: "column", alignItems: "stretch", gap: 6, children: [
1618
+ /* @__PURE__ */ jsxs8(Section, { title: "Definition", children: [
1619
+ /* @__PURE__ */ jsx8(
1292
1620
  TextField,
1293
1621
  {
1294
1622
  label: "Name",
@@ -1298,7 +1626,7 @@ function FormEditorPage({ canUpdate }) {
1298
1626
  onChange: (name) => setDraft({ ...draft, name })
1299
1627
  }
1300
1628
  ),
1301
- /* @__PURE__ */ jsx7(
1629
+ /* @__PURE__ */ jsx8(
1302
1630
  TextField,
1303
1631
  {
1304
1632
  label: "Slug",
@@ -1308,11 +1636,11 @@ function FormEditorPage({ canUpdate }) {
1308
1636
  onChange: (slug) => setDraft({ ...draft, slug })
1309
1637
  }
1310
1638
  ),
1311
- /* @__PURE__ */ jsx7(Typography6, { variant: "pi", children: "Use lowercase letters, numbers and hyphens. The slug cannot change after creation." }),
1312
- /* @__PURE__ */ jsxs7("label", { children: [
1639
+ /* @__PURE__ */ jsx8(Typography7, { variant: "pi", children: "Use lowercase letters, numbers and hyphens. The slug cannot change after creation." }),
1640
+ /* @__PURE__ */ jsxs8("label", { children: [
1313
1641
  "Target content type",
1314
1642
  " ",
1315
- /* @__PURE__ */ jsxs7(
1643
+ /* @__PURE__ */ jsxs8(
1316
1644
  "select",
1317
1645
  {
1318
1646
  required: true,
@@ -1321,8 +1649,8 @@ function FormEditorPage({ canUpdate }) {
1321
1649
  onChange: (event) => void selectTarget(event.target.value),
1322
1650
  style: { padding: 10, width: "100%" },
1323
1651
  children: [
1324
- /* @__PURE__ */ jsx7("option", { value: "", children: "Select a target" }),
1325
- targets.map((target) => /* @__PURE__ */ jsxs7("option", { value: target.uid, children: [
1652
+ /* @__PURE__ */ jsx8("option", { value: "", children: "Select a target" }),
1653
+ targets.map((target) => /* @__PURE__ */ jsxs8("option", { value: target.uid, children: [
1326
1654
  target.name,
1327
1655
  " \u2014 ",
1328
1656
  target.uid
@@ -1331,9 +1659,9 @@ function FormEditorPage({ canUpdate }) {
1331
1659
  }
1332
1660
  )
1333
1661
  ] }),
1334
- fieldsLoading && /* @__PURE__ */ jsx7(Typography6, { children: "Loading target fields\u2026" })
1662
+ fieldsLoading && /* @__PURE__ */ jsx8(Typography7, { children: "Loading target fields\u2026" })
1335
1663
  ] }),
1336
- /* @__PURE__ */ jsx7(
1664
+ /* @__PURE__ */ jsx8(
1337
1665
  FieldEditor,
1338
1666
  {
1339
1667
  fields: draft.fields,
@@ -1342,8 +1670,8 @@ function FormEditorPage({ canUpdate }) {
1342
1670
  onChange: (fields) => setDraft({ ...draft, fields })
1343
1671
  }
1344
1672
  ),
1345
- /* @__PURE__ */ jsxs7(Section, { title: "Consent", children: [
1346
- /* @__PURE__ */ jsx7(
1673
+ /* @__PURE__ */ jsxs8(Section, { title: "Consent", children: [
1674
+ /* @__PURE__ */ jsx8(
1347
1675
  TextField,
1348
1676
  {
1349
1677
  label: "Consent label",
@@ -1357,7 +1685,7 @@ function FormEditorPage({ canUpdate }) {
1357
1685
  })
1358
1686
  }
1359
1687
  ),
1360
- /* @__PURE__ */ jsx7(
1688
+ /* @__PURE__ */ jsx8(
1361
1689
  TextField,
1362
1690
  {
1363
1691
  label: "Consent version",
@@ -1373,9 +1701,9 @@ function FormEditorPage({ canUpdate }) {
1373
1701
  })
1374
1702
  }
1375
1703
  ),
1376
- /* @__PURE__ */ jsx7(Typography6, { variant: "pi", children: "Consent version identifies the terms accepted by the visitor and is separate from the server form version." })
1704
+ /* @__PURE__ */ jsx8(Typography7, { variant: "pi", children: "Consent version identifies the terms accepted by the visitor and is separate from the server form version." })
1377
1705
  ] }),
1378
- /* @__PURE__ */ jsx7(
1706
+ /* @__PURE__ */ jsx8(
1379
1707
  NotificationEditor,
1380
1708
  {
1381
1709
  value: draft.notifications ?? {},
@@ -1387,8 +1715,8 @@ function FormEditorPage({ canUpdate }) {
1387
1715
  saveError: error
1388
1716
  }
1389
1717
  ),
1390
- canUpdate && /* @__PURE__ */ jsx7(
1391
- Button5,
1718
+ canUpdate && /* @__PURE__ */ jsx8(
1719
+ Button6,
1392
1720
  {
1393
1721
  type: "submit",
1394
1722
  disabled: disabled || !draft.targetUid || !draft.fields.length,
@@ -1398,7 +1726,7 @@ function FormEditorPage({ canUpdate }) {
1398
1726
  ] })
1399
1727
  }
1400
1728
  ),
1401
- notificationsOpened && /* @__PURE__ */ jsx7(
1729
+ notificationsOpened && /* @__PURE__ */ jsx8(
1402
1730
  NotificationTest,
1403
1731
  {
1404
1732
  fields: draft.fields,
@@ -1407,9 +1735,9 @@ function FormEditorPage({ canUpdate }) {
1407
1735
  },
1408
1736
  savedDraft
1409
1737
  ),
1410
- documentId && /* @__PURE__ */ jsxs7(Section, { title: "Portable definition", children: [
1411
- /* @__PURE__ */ jsx7(
1412
- Button5,
1738
+ documentId && /* @__PURE__ */ jsxs8(Section, { title: "Portable definition", children: [
1739
+ /* @__PURE__ */ jsx8(
1740
+ Button6,
1413
1741
  {
1414
1742
  disabled: busy,
1415
1743
  onClick: () => void run(
@@ -1421,8 +1749,8 @@ function FormEditorPage({ canUpdate }) {
1421
1749
  children: "Export saved form JSON"
1422
1750
  }
1423
1751
  ),
1424
- /* @__PURE__ */ jsx7(Typography6, { variant: "pi", children: "Export uses the saved definition. Notification credentials are masked by the server." }),
1425
- canUpdate && /* @__PURE__ */ jsx7(
1752
+ /* @__PURE__ */ jsx8(Typography7, { variant: "pi", children: "Export uses the saved definition. Notification credentials are masked by the server." }),
1753
+ canUpdate && /* @__PURE__ */ jsx8(
1426
1754
  ImportForm,
1427
1755
  {
1428
1756
  replace: true,
@@ -1473,50 +1801,69 @@ var init_form_editor_page = __esm({
1473
1801
  // admin/src/app.tsx
1474
1802
  var app_exports = {};
1475
1803
  __export(app_exports, {
1804
+ SubmissionsApp: () => SubmissionsApp,
1476
1805
  default: () => App
1477
1806
  });
1478
1807
  import { Navigate, Route, Routes } from "react-router-dom";
1479
- import { Layouts as Layouts3, Page as Page3, useRBAC } from "@strapi/strapi/admin";
1480
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1481
- function App() {
1482
- const { allowedActions, isLoading } = useRBAC(
1808
+ import { Layouts as Layouts4, Page as Page4, useRBAC as useRBAC2 } from "@strapi/strapi/admin";
1809
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1810
+ function FormsApp() {
1811
+ const { allowedActions, isLoading } = useRBAC2(
1483
1812
  ["read", "update"].map((action) => ({
1484
1813
  action: `plugin::builder.forms.${action}`,
1485
1814
  subject: null
1486
1815
  }))
1487
1816
  );
1488
- if (isLoading) return /* @__PURE__ */ jsx8(Page3.Loading, {});
1489
- if (!allowedActions.canRead) return /* @__PURE__ */ jsx8(Page3.NoPermissions, {});
1817
+ if (isLoading) return /* @__PURE__ */ jsx9(Page4.Loading, {});
1818
+ if (!allowedActions.canRead) return /* @__PURE__ */ jsx9(Page4.NoPermissions, {});
1490
1819
  const canUpdate = allowedActions.canUpdate === true;
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(
1820
+ return /* @__PURE__ */ jsx9(Layouts4.Root, { children: /* @__PURE__ */ jsxs9(Routes, { children: [
1821
+ /* @__PURE__ */ jsx9(Route, { index: true, element: /* @__PURE__ */ jsx9(Navigate, { to: "forms", replace: true }) }),
1822
+ /* @__PURE__ */ jsx9(Route, { path: "forms", element: /* @__PURE__ */ jsx9(FormListPage, { canUpdate }) }),
1823
+ /* @__PURE__ */ jsx9(
1495
1824
  Route,
1496
1825
  {
1497
1826
  path: "forms/new",
1498
- element: canUpdate ? /* @__PURE__ */ jsx8(FormEditorPage, { canUpdate: true }) : /* @__PURE__ */ jsx8(Page3.NoPermissions, {})
1827
+ element: canUpdate ? /* @__PURE__ */ jsx9(FormEditorPage, { canUpdate: true }) : /* @__PURE__ */ jsx9(Page4.NoPermissions, {})
1499
1828
  }
1500
1829
  ),
1501
- /* @__PURE__ */ jsx8(
1830
+ /* @__PURE__ */ jsx9(
1502
1831
  Route,
1503
1832
  {
1504
1833
  path: "forms/:documentId",
1505
- element: /* @__PURE__ */ jsx8(FormEditorPage, { canUpdate })
1834
+ element: /* @__PURE__ */ jsx9(FormEditorPage, { canUpdate })
1506
1835
  }
1507
1836
  ),
1508
- /* @__PURE__ */ jsx8(
1837
+ /* @__PURE__ */ jsx9(
1509
1838
  Route,
1510
1839
  {
1511
1840
  path: "*",
1512
- element: /* @__PURE__ */ jsx8(Page3.Error, { content: "Form page not found." })
1841
+ element: /* @__PURE__ */ jsx9(Page4.Error, { content: "Form page not found." })
1513
1842
  }
1514
1843
  )
1515
1844
  ] }) });
1516
1845
  }
1846
+ function SubmissionsApp() {
1847
+ const { allowedActions, isLoading } = useRBAC2(
1848
+ ["read", "update"].map((action) => ({
1849
+ action: `plugin::builder.submissions.${action}`,
1850
+ subject: null
1851
+ }))
1852
+ );
1853
+ if (isLoading) return /* @__PURE__ */ jsx9(Page4.Loading, {});
1854
+ if (!allowedActions.canRead) return /* @__PURE__ */ jsx9(Page4.NoPermissions, {});
1855
+ return /* @__PURE__ */ jsx9(Layouts4.Root, { children: /* @__PURE__ */ jsx9(SubmissionPage, { canUpdate: allowedActions.canUpdate === true }) });
1856
+ }
1857
+ function App() {
1858
+ return /* @__PURE__ */ jsxs9(Routes, { children: [
1859
+ /* @__PURE__ */ jsx9(Route, { path: "submissions/*", element: /* @__PURE__ */ jsx9(SubmissionsApp, {}) }),
1860
+ /* @__PURE__ */ jsx9(Route, { path: "*", element: /* @__PURE__ */ jsx9(FormsApp, {}) })
1861
+ ] });
1862
+ }
1517
1863
  var init_app = __esm({
1518
1864
  "admin/src/app.tsx"() {
1519
1865
  "use strict";
1866
+ init_submission_page();
1520
1867
  init_form_list_page();
1521
1868
  init_form_editor_page();
1522
1869
  }
@@ -1562,6 +1909,20 @@ var index_default = {
1562
1909
  Component: () => Promise.resolve().then(() => (init_app(), app_exports)),
1563
1910
  permissions: [{ action: "plugin::builder.forms.read", subject: null }]
1564
1911
  });
1912
+ app.addMenuLink({
1913
+ to: "plugins/builder/submissions",
1914
+ icon: BuilderIcon,
1915
+ intlLabel: {
1916
+ id: "builder.submissions",
1917
+ defaultMessage: "Builder Submissions"
1918
+ },
1919
+ Component: () => Promise.resolve().then(() => (init_app(), app_exports)).then(({ SubmissionsApp: SubmissionsApp2 }) => ({
1920
+ default: SubmissionsApp2
1921
+ })),
1922
+ permissions: [
1923
+ { action: "plugin::builder.submissions.read", subject: null }
1924
+ ]
1925
+ });
1565
1926
  app.registerPlugin({ id: "builder", name: "Builder" });
1566
1927
  }
1567
1928
  };