@aglyn/plugins-forms 1.0.0-beta.221 → 1.0.0-beta.222
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/README.md +1 -0
- package/package.json +14 -12
- package/src/lib/components/form-detail-card.js +166 -29
- package/src/lib/components/form-detail-card.js.map +1 -1
- package/src/lib/components/form-submissions-card.component.d.ts +12 -0
- package/src/lib/components/form-submissions-card.component.js +35 -2
- package/src/lib/components/form-submissions-card.component.js.map +1 -1
- package/src/lib/components/form.js +2 -1
- package/src/lib/components/form.js.map +1 -1
- package/src/lib/components/host-forms-card.component.d.ts +8 -0
- package/src/lib/components/host-forms-card.component.js +26 -1
- package/src/lib/components/host-forms-card.component.js.map +1 -1
- package/src/lib/components/site-package-form-preview.component.d.ts +13 -0
- package/src/lib/components/site-package-form-preview.component.js +57 -0
- package/src/lib/components/site-package-form-preview.component.js.map +1 -0
- package/src/lib/constants/site-package.d.ts +18 -0
- package/src/lib/constants/site-package.js +18 -0
- package/src/lib/constants/site-package.js.map +1 -0
- package/src/lib/declarations.console-server.d.ts +39 -0
- package/src/lib/declarations.console-server.js +68 -0
- package/src/lib/declarations.console-server.js.map +1 -0
- package/src/lib/declarations.server.js +22 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/model/form-door.d.ts +37 -0
- package/src/lib/model/form-door.js +50 -0
- package/src/lib/model/form-door.js.map +1 -0
- package/src/lib/model/form-submission-counts.d.ts +26 -0
- package/src/lib/model/form-submission-counts.js +35 -0
- package/src/lib/model/form-submission-counts.js.map +1 -0
- package/src/lib/model/form-submission-list.d.ts +40 -0
- package/src/lib/model/form-submission-list.js +66 -0
- package/src/lib/model/form-submission-list.js.map +1 -0
- package/src/lib/model/form-submission-record.d.ts +50 -0
- package/src/lib/model/form-submission-record.js +68 -0
- package/src/lib/model/form-submission-record.js.map +1 -0
- package/src/lib/model/form-unavailable.d.ts +83 -0
- package/src/lib/model/form-unavailable.js +88 -0
- package/src/lib/model/form-unavailable.js.map +1 -0
- package/src/lib/model/forms-record-lists.d.ts +31 -0
- package/src/lib/model/forms-record-lists.js +50 -0
- package/src/lib/model/forms-record-lists.js.map +1 -0
- package/src/lib/plan-entitlements.js +6 -6
- package/src/lib/plan-entitlements.js.map +1 -1
- package/src/lib/plugin.js +34 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/api-v1/form-submissions.d.ts +34 -0
- package/src/lib/server/api-v1/form-submissions.js +273 -0
- package/src/lib/server/api-v1/form-submissions.js.map +1 -0
- package/src/lib/server/api-v1/openapi.d.ts +26 -0
- package/src/lib/server/api-v1/openapi.js +133 -0
- package/src/lib/server/api-v1/openapi.js.map +1 -0
- package/src/lib/server/form-intake-gate.d.ts +34 -0
- package/src/lib/server/form-intake-gate.js +44 -0
- package/src/lib/server/form-intake-gate.js.map +1 -0
- package/src/lib/server/form-submission-index.d.ts +26 -0
- package/src/lib/server/form-submission-index.js +51 -0
- package/src/lib/server/form-submission-index.js.map +1 -0
- package/src/lib/server/form-submit.d.ts +43 -0
- package/src/lib/server/form-submit.js +769 -0
- package/src/lib/server/form-submit.js.map +1 -0
- package/src/lib/server/increment-form-stats.d.ts +70 -0
- package/src/lib/server/increment-form-stats.js +112 -0
- package/src/lib/server/increment-form-stats.js.map +1 -0
- package/src/lib/server.d.ts +10 -0
- package/src/lib/server.js +15 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/transfer/form-submissions-transfer-key.d.ts +37 -0
- package/src/lib/transfer/form-submissions-transfer-key.js +40 -0
- package/src/lib/transfer/form-submissions-transfer-key.js.map +1 -0
- package/src/lib/transfer/form-submissions-transfer.d.ts +98 -0
- package/src/lib/transfer/form-submissions-transfer.js +563 -0
- package/src/lib/transfer/form-submissions-transfer.js.map +1 -0
- package/src/lib/transfer/form-submissions-transfer.server.d.ts +26 -0
- package/src/lib/transfer/form-submissions-transfer.server.js +31 -0
- package/src/lib/transfer/form-submissions-transfer.server.js.map +1 -0
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ Presets for both, including a composed contact section, appear in the Besigner's
|
|
|
25
25
|
|
|
26
26
|
- A `Forms` nav item at `/forms`: the form catalog and one form's own page (declaration, metrics, versions, promotion, design preview). The nav item owns its subtree, because a form's URL names a document id. The page is code-split and loads when opened.
|
|
27
27
|
- Two zones the plugin hosts on a form's page for other plugins to fill, registered with `registerPluginZone`: `formSubmissions`, where a reader of that form's submissions is drawn, and a zone for mapping a form's fields onto a person's fields.
|
|
28
|
+
- The `formSubmission` kind on the record seams: a console list source (`plugin-record-lists`) — the newest of a site's, the base the Inbox walks, one submission's document, and a site's submissions by name — a server record index (`plugin-record-index`), and a count source (`plugin-record-counts`) for how many a site received. The Inbox and a campaign's conversion report read submissions through these instead of the collection.
|
|
28
29
|
|
|
29
30
|
**Ids are persisted.** `componentId` (`form`, `formField`) is stored in every screen document and does not change. `pluginId` is stored beside it and is `forms` (`BUNDLE_ID`); the renderer reads it to decide which bundles must register before first paint, so a page with no form on it does not wait for this package.
|
|
30
31
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aglyn/plugins-forms",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.222",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"homepage": "https://aglyn.com",
|
|
6
6
|
"repository": {
|
|
@@ -25,17 +25,19 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aglyn/aglyn": "1.0.0-beta.
|
|
29
|
-
"@aglyn/shared-data-enums": "1.0.0-beta.
|
|
30
|
-
"@aglyn/shared-data-mdi": "1.0.0-beta.
|
|
31
|
-
"@aglyn/shared-ui-jsx": "1.0.0-beta.
|
|
32
|
-
"@aglyn/shared-ui-jsx-forms": "1.0.0-beta.
|
|
33
|
-
"@aglyn/shared-ui-snackstack": "1.0.0-beta.
|
|
34
|
-
"@aglyn/shared-util-
|
|
35
|
-
"@aglyn/shared-util-
|
|
36
|
-
"@aglyn/shared-util-
|
|
37
|
-
"@aglyn/
|
|
38
|
-
"@aglyn/tenant-
|
|
28
|
+
"@aglyn/aglyn": "1.0.0-beta.222",
|
|
29
|
+
"@aglyn/shared-data-enums": "1.0.0-beta.222",
|
|
30
|
+
"@aglyn/shared-data-mdi": "1.0.0-beta.222",
|
|
31
|
+
"@aglyn/shared-ui-jsx": "1.0.0-beta.222",
|
|
32
|
+
"@aglyn/shared-ui-jsx-forms": "1.0.0-beta.222",
|
|
33
|
+
"@aglyn/shared-ui-snackstack": "1.0.0-beta.222",
|
|
34
|
+
"@aglyn/shared-util-email": "1.0.0-beta.222",
|
|
35
|
+
"@aglyn/shared-util-http": "1.0.0-beta.222",
|
|
36
|
+
"@aglyn/shared-util-timestamp": "1.0.0-beta.222",
|
|
37
|
+
"@aglyn/shared-util-tools": "1.0.0-beta.222",
|
|
38
|
+
"@aglyn/tenant-data-admin": "1.0.0-beta.222",
|
|
39
|
+
"@aglyn/tenant-feature-instance": "1.0.0-beta.222",
|
|
40
|
+
"@aglyn/tenant-runtime": "1.0.0-beta.222",
|
|
39
41
|
"@swc/helpers": "0.5.23"
|
|
40
42
|
},
|
|
41
43
|
"peerDependencies": {
|
|
@@ -25,7 +25,7 @@ import { useConsoleWidgetSlot } from "@aglyn/aglyn/app-utils/console-widget-slot
|
|
|
25
25
|
// page. The same import the Inbox makes for the same reason.
|
|
26
26
|
import { pluginRecordFilteredHref } from "@aglyn/aglyn/plugin-manager/plugin-record-routes";
|
|
27
27
|
import { ICON_VARIANT_BESIGNER } from "@aglyn/shared-data-enums";
|
|
28
|
-
import { AppLink, CardDisplay, GridItems, MdiIcon, useLoading } from "@aglyn/shared-ui-jsx";
|
|
28
|
+
import { AppLink, CardDisplay, GridItems, MdiIcon, useConfirmationContext, useLoading } from "@aglyn/shared-ui-jsx";
|
|
29
29
|
import { ScrollTable } from "@aglyn/shared-ui-jsx/components/scroll-table.component";
|
|
30
30
|
import { useSnackbar } from "@aglyn/shared-ui-snackstack";
|
|
31
31
|
import { Timestamp } from "@aglyn/shared-util-timestamp";
|
|
@@ -34,7 +34,7 @@ import { useConsoleHostRoute, useFirestore, useFirestoreCollection, useFirestore
|
|
|
34
34
|
import ContainerPicker from "@aglyn/tenant-feature-instance/components/container-picker";
|
|
35
35
|
import { collection, doc, limit, query, updateDoc } from "firebase/firestore";
|
|
36
36
|
import { useRouter } from "next/navigation";
|
|
37
|
-
import { useCallback, useMemo, useState } from "react";
|
|
37
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
38
38
|
import { FORM_CONTACT_FIELDS_ZONE } from "./form-zones.js";
|
|
39
39
|
import { FORMS_DOCUMENT_SEGMENT } from "../constants/bundle-common.js";
|
|
40
40
|
import FormDesignPreview from "./form-design-preview.component.js";
|
|
@@ -73,8 +73,8 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
73
73
|
* about the design crosses the wire inbound and there is no version of this
|
|
74
74
|
* surface that can publish a design the check has not seen.
|
|
75
75
|
*/ export function FormDetailCard(props) {
|
|
76
|
-
var _ref, _ref1;
|
|
77
|
-
var _form_routing;
|
|
76
|
+
var _ref, _ref1, _ref2, _ref3;
|
|
77
|
+
var _form_routing, _form_routing1;
|
|
78
78
|
const { hostId, formId, basePath, canPublish, hostRoleLoaded } = props;
|
|
79
79
|
const firestore = useFirestore();
|
|
80
80
|
const ContactFieldsZone = useConsoleWidgetSlot();
|
|
@@ -101,6 +101,7 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
101
101
|
const router = useRouter();
|
|
102
102
|
const { enqueueSnackbar } = useSnackbar();
|
|
103
103
|
const { queueLoading } = useLoading();
|
|
104
|
+
const { confirm } = useConfirmationContext();
|
|
104
105
|
const { data: form, status } = useFirestoreDoc(()=>doc(firestore, 'hosts', hostId, 'forms', formId), [
|
|
105
106
|
firestore,
|
|
106
107
|
hostId,
|
|
@@ -225,31 +226,79 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
225
226
|
effectiveLead,
|
|
226
227
|
effectiveConsent
|
|
227
228
|
]);
|
|
228
|
-
|
|
229
|
+
/**
|
|
230
|
+
* WHICH CARD IS WRITING, or null.
|
|
231
|
+
*
|
|
232
|
+
* Each card saves what it shows and nothing else (AGL-3508). One handler
|
|
233
|
+
* used to write the name, the campaigns, the lead switch and the consent
|
|
234
|
+
* field together from a button in Details, so a routing change made in its
|
|
235
|
+
* own card sat unsaved until somebody pressed Save somewhere else — and was
|
|
236
|
+
* dropped without a word when they left instead.
|
|
237
|
+
*/ const [saving, setSaving] = useState(null);
|
|
238
|
+
/**
|
|
239
|
+
* Details: the name and the campaigns. Never `routing` or the consent
|
|
240
|
+
* field, so saving a rename cannot also commit a half-made routing change.
|
|
241
|
+
*/ const handleSaveDetails = useCallback(async ()=>{
|
|
229
242
|
const dequeue = queueLoading();
|
|
243
|
+
setSaving('details');
|
|
230
244
|
try {
|
|
231
|
-
var _ref;
|
|
232
|
-
var _form_routing;
|
|
233
245
|
await updateDoc(doc(firestore, 'hosts', hostId, 'forms', formId), _extends({}, name != null ? _extends({
|
|
234
246
|
displayName: name.trim()
|
|
235
247
|
}, Aglyn.formListFields({
|
|
236
248
|
id: formId,
|
|
237
249
|
displayName: name.trim(),
|
|
238
250
|
slug: form == null ? void 0 : form.slug
|
|
239
|
-
})) : {},
|
|
251
|
+
})) : {}, campaignIds != null ? Aglyn.formCampaignFields(campaignIds) : {}, {
|
|
252
|
+
updatedAt: Timestamp.now()
|
|
253
|
+
}));
|
|
254
|
+
setName(null);
|
|
255
|
+
setCampaignIds(null);
|
|
256
|
+
enqueueSnackbar('Details saved', {
|
|
257
|
+
variant: 'success',
|
|
258
|
+
persist: false
|
|
259
|
+
});
|
|
260
|
+
} catch (error) {
|
|
261
|
+
console.error(error);
|
|
262
|
+
enqueueSnackbar('An error has occurred', {
|
|
263
|
+
variant: 'error',
|
|
264
|
+
allowDuplicate: true
|
|
265
|
+
});
|
|
266
|
+
} finally{
|
|
267
|
+
setSaving(null);
|
|
268
|
+
dequeue();
|
|
269
|
+
}
|
|
270
|
+
}, [
|
|
271
|
+
firestore,
|
|
272
|
+
hostId,
|
|
273
|
+
formId,
|
|
274
|
+
form,
|
|
275
|
+
name,
|
|
276
|
+
campaignIds,
|
|
277
|
+
queueLoading,
|
|
278
|
+
enqueueSnackbar
|
|
279
|
+
]);
|
|
280
|
+
/**
|
|
281
|
+
* CRM routing: the lead switch and the consent field, together, because
|
|
282
|
+
* the contract judges them together — a lead surface with no opt-in field
|
|
283
|
+
* is one of the breaks the warning in this card names.
|
|
284
|
+
*/ const handleSaveRouting = useCallback(async ()=>{
|
|
285
|
+
const dequeue = queueLoading();
|
|
286
|
+
setSaving('routing');
|
|
287
|
+
try {
|
|
288
|
+
var _ref;
|
|
289
|
+
var _form_routing;
|
|
290
|
+
await updateDoc(doc(firestore, 'hosts', hostId, 'forms', formId), _extends({}, lead != null ? {
|
|
240
291
|
routing: _extends({}, (_ref = form == null ? void 0 : form.routing) != null ? _ref : {}, {
|
|
241
292
|
lead
|
|
242
293
|
})
|
|
243
294
|
} : {}, consentField != null ? {
|
|
244
295
|
consentFieldName: consentField.trim()
|
|
245
|
-
} : {},
|
|
296
|
+
} : {}, {
|
|
246
297
|
updatedAt: Timestamp.now()
|
|
247
298
|
}));
|
|
248
|
-
setName(null);
|
|
249
299
|
setLead(null);
|
|
250
300
|
setConsentField(null);
|
|
251
|
-
|
|
252
|
-
enqueueSnackbar('Form saved', {
|
|
301
|
+
enqueueSnackbar('CRM routing saved', {
|
|
253
302
|
variant: 'success',
|
|
254
303
|
persist: false
|
|
255
304
|
});
|
|
@@ -272,6 +321,7 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
272
321
|
allowDuplicate: true
|
|
273
322
|
});
|
|
274
323
|
} finally{
|
|
324
|
+
setSaving(null);
|
|
275
325
|
dequeue();
|
|
276
326
|
}
|
|
277
327
|
}, [
|
|
@@ -279,14 +329,57 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
279
329
|
hostId,
|
|
280
330
|
formId,
|
|
281
331
|
form,
|
|
282
|
-
name,
|
|
283
332
|
lead,
|
|
284
333
|
consentField,
|
|
285
|
-
campaignIds,
|
|
286
334
|
queueLoading,
|
|
287
335
|
enqueueSnackbar,
|
|
288
336
|
recountFormStats
|
|
289
337
|
]);
|
|
338
|
+
/*
|
|
339
|
+
* Whether each card holds a change, measured against what is stored rather
|
|
340
|
+
* than against "was touched": flipping the switch and flipping it back is
|
|
341
|
+
* no change, and a Save that writes nothing should not be offered.
|
|
342
|
+
*/ const detailsDirty = name != null && name.trim() !== String((_ref1 = form == null ? void 0 : form.displayName) != null ? _ref1 : '') || campaignIds != null && !Aglyn.containerMembershipUnchanged(storedCampaigns, campaignIds);
|
|
343
|
+
const routingDirty = lead != null && lead !== ((form == null ? void 0 : (_form_routing1 = form.routing) == null ? void 0 : _form_routing1.lead) === true) || consentField != null && consentField.trim() !== String((_ref2 = form == null ? void 0 : form.consentFieldName) != null ? _ref2 : '');
|
|
344
|
+
const dirty = detailsDirty || routingDirty;
|
|
345
|
+
/**
|
|
346
|
+
* Read from inside handlers that must not re-subscribe on every keystroke:
|
|
347
|
+
* `beforeunload` and the besigner buttons need the CURRENT answer.
|
|
348
|
+
*/ const dirtyRef = useRef(false);
|
|
349
|
+
dirtyRef.current = dirty;
|
|
350
|
+
/**
|
|
351
|
+
* The exits a router cannot mediate: reload, tab close, or a link out of
|
|
352
|
+
* the console. Registered only while a card holds a change, so a clean page
|
|
353
|
+
* never nags — the same guard a content entry's editor keeps. The App
|
|
354
|
+
* Router has no navigation-blocking hook, so an in-console link is guarded
|
|
355
|
+
* only where this surface makes the move itself (the besigner buttons,
|
|
356
|
+
* below); everywhere else the enabled Save and Discard in each card's
|
|
357
|
+
* header are what say a change is waiting.
|
|
358
|
+
*/ useEffect(()=>{
|
|
359
|
+
if (!dirty) return;
|
|
360
|
+
const warn = (event)=>{
|
|
361
|
+
event.preventDefault();
|
|
362
|
+
event.returnValue = '';
|
|
363
|
+
};
|
|
364
|
+
window.addEventListener('beforeunload', warn);
|
|
365
|
+
return ()=>window.removeEventListener('beforeunload', warn);
|
|
366
|
+
}, [
|
|
367
|
+
dirty
|
|
368
|
+
]);
|
|
369
|
+
/**
|
|
370
|
+
* Asked before this surface navigates away on its own — opening the
|
|
371
|
+
* besigner — while a card holds a change. `confirm` resolves with no value
|
|
372
|
+
* and REJECTS on cancel.
|
|
373
|
+
*/ const confirmLeave = useCallback(async ()=>{
|
|
374
|
+
if (!dirtyRef.current) return true;
|
|
375
|
+
return confirm({
|
|
376
|
+
title: 'Discard unsaved changes?',
|
|
377
|
+
description: 'This form has changes that have not been saved yet. Opening the ' + 'besigner now discards them.',
|
|
378
|
+
confirmationText: 'Discard and open'
|
|
379
|
+
}).then(()=>true).catch(()=>false);
|
|
380
|
+
}, [
|
|
381
|
+
confirm
|
|
382
|
+
]);
|
|
290
383
|
/**
|
|
291
384
|
* Makes one version the version the live sites serve.
|
|
292
385
|
*
|
|
@@ -352,6 +445,7 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
352
445
|
* on the form the site was ALREADY submitting rather than a blank canvas.
|
|
353
446
|
*/ const handleOpen = useCallback((targetVersionId)=>async ()=>{
|
|
354
447
|
if (opening) return;
|
|
448
|
+
if (!await confirmLeave()) return;
|
|
355
449
|
setOpening(true);
|
|
356
450
|
try {
|
|
357
451
|
var _ref;
|
|
@@ -415,6 +509,7 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
415
509
|
}
|
|
416
510
|
}, [
|
|
417
511
|
opening,
|
|
512
|
+
confirmLeave,
|
|
418
513
|
createHostVersion,
|
|
419
514
|
publishedVersionId,
|
|
420
515
|
versions,
|
|
@@ -427,7 +522,6 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
427
522
|
host,
|
|
428
523
|
enqueueSnackbar
|
|
429
524
|
]);
|
|
430
|
-
const dirty = name != null || lead != null || consentField != null || campaignIds != null && !Aglyn.containerMembershipUnchanged(storedCampaigns, campaignIds);
|
|
431
525
|
// Named before it is used on every row, so a role denial reads as a reason
|
|
432
526
|
// rather than as a control that does nothing.
|
|
433
527
|
const publishBlock = hostRoleLoaded ? 'Your role on this site can edit content but not publish it' : 'Checking what your role can do…';
|
|
@@ -473,6 +567,18 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
473
567
|
anchor: '#build-a-form',
|
|
474
568
|
excerpt: 'The id is what every submission is filed under, so ' + 'renaming a form never splits its history.'
|
|
475
569
|
}),
|
|
570
|
+
HeaderProps: {
|
|
571
|
+
action: /*#__PURE__*/ _jsx(FormDetailCardActions, {
|
|
572
|
+
section: "details",
|
|
573
|
+
dirty: detailsDirty,
|
|
574
|
+
saving: saving,
|
|
575
|
+
onDiscard: ()=>{
|
|
576
|
+
setName(null);
|
|
577
|
+
setCampaignIds(null);
|
|
578
|
+
},
|
|
579
|
+
onSave: handleSaveDetails
|
|
580
|
+
})
|
|
581
|
+
},
|
|
476
582
|
contentGutterX: true,
|
|
477
583
|
contentGutterY: true,
|
|
478
584
|
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
@@ -481,7 +587,7 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
481
587
|
/*#__PURE__*/ _jsx(TextField, {
|
|
482
588
|
label: "Display name",
|
|
483
589
|
size: "small",
|
|
484
|
-
value: (
|
|
590
|
+
value: (_ref3 = name != null ? name : form == null ? void 0 : form.displayName) != null ? _ref3 : '',
|
|
485
591
|
onChange: (event)=>setName(event.target.value),
|
|
486
592
|
fullWidth: true
|
|
487
593
|
}),
|
|
@@ -497,18 +603,6 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
497
603
|
onChange: setCampaignIds,
|
|
498
604
|
helperText: "The campaigns this form is part of. It does not change who a campaign mails.",
|
|
499
605
|
empty: siteCampaigns.ready && !siteCampaigns.options.length
|
|
500
|
-
}),
|
|
501
|
-
/*#__PURE__*/ _jsx(Stack, {
|
|
502
|
-
direction: "row",
|
|
503
|
-
spacing: 1,
|
|
504
|
-
children: /*#__PURE__*/ _jsx(Button, {
|
|
505
|
-
variant: "contained",
|
|
506
|
-
color: "primary",
|
|
507
|
-
size: "small",
|
|
508
|
-
disabled: !dirty,
|
|
509
|
-
onClick: handleSave,
|
|
510
|
-
children: 'Save'
|
|
511
|
-
})
|
|
512
606
|
})
|
|
513
607
|
]
|
|
514
608
|
})
|
|
@@ -525,6 +619,18 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
525
619
|
anchor: '#where-submissions-go',
|
|
526
620
|
excerpt: 'Every submission reaches the Inbox and updates the ' + 'contact. Lead routing and the consent field decide ' + 'what else happens to it.'
|
|
527
621
|
}),
|
|
622
|
+
HeaderProps: {
|
|
623
|
+
action: /*#__PURE__*/ _jsx(FormDetailCardActions, {
|
|
624
|
+
section: "routing",
|
|
625
|
+
dirty: routingDirty,
|
|
626
|
+
saving: saving,
|
|
627
|
+
onDiscard: ()=>{
|
|
628
|
+
setLead(null);
|
|
629
|
+
setConsentField(null);
|
|
630
|
+
},
|
|
631
|
+
onSave: handleSaveRouting
|
|
632
|
+
})
|
|
633
|
+
},
|
|
528
634
|
contentGutterX: true,
|
|
529
635
|
contentGutterY: true,
|
|
530
636
|
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
@@ -780,7 +886,8 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
780
886
|
},
|
|
781
887
|
children: /*#__PURE__*/ _jsx(FormSubmissionsCard, {
|
|
782
888
|
hostId: hostId,
|
|
783
|
-
formId: formId
|
|
889
|
+
formId: formId,
|
|
890
|
+
formName: form == null ? void 0 : form.displayName
|
|
784
891
|
})
|
|
785
892
|
},
|
|
786
893
|
{
|
|
@@ -831,6 +938,36 @@ import useFormStatsRecountApi from "./use-form-stats-recount-api.js";
|
|
|
831
938
|
});
|
|
832
939
|
}
|
|
833
940
|
FormDetailCard.displayName = 'FormDetailCard';
|
|
941
|
+
/**
|
|
942
|
+
* One card's Discard and Save, in that card's header (AGL-3508).
|
|
943
|
+
*
|
|
944
|
+
* Each is disabled until its card holds a change, and both while either card
|
|
945
|
+
* is writing: the two saves touch one document, and a second write landing
|
|
946
|
+
* mid-flight would be judged against a form the first is still changing.
|
|
947
|
+
*/ function FormDetailCardActions(props) {
|
|
948
|
+
const { section, dirty, saving, onDiscard, onSave } = props;
|
|
949
|
+
return /*#__PURE__*/ _jsxs(Stack, {
|
|
950
|
+
direction: "row",
|
|
951
|
+
spacing: 1,
|
|
952
|
+
children: [
|
|
953
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
954
|
+
size: "small",
|
|
955
|
+
variant: "text",
|
|
956
|
+
disabled: !dirty || saving !== null,
|
|
957
|
+
onClick: onDiscard,
|
|
958
|
+
children: 'Discard changes'
|
|
959
|
+
}),
|
|
960
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
961
|
+
size: "small",
|
|
962
|
+
variant: "contained",
|
|
963
|
+
color: "primary",
|
|
964
|
+
disabled: !dirty || saving !== null,
|
|
965
|
+
onClick: ()=>void onSave(),
|
|
966
|
+
children: saving === section ? 'Saving…' : 'Save'
|
|
967
|
+
})
|
|
968
|
+
]
|
|
969
|
+
});
|
|
970
|
+
}
|
|
834
971
|
/**
|
|
835
972
|
* The `form` node inside a published design.
|
|
836
973
|
*
|