@oneblink/apps-react 13.0.0-beta.1 → 13.0.0-beta.11

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 (123) hide show
  1. package/dist/OneBlinkFormBase.d.ts +23 -3
  2. package/dist/OneBlinkFormBase.js +351 -193
  3. package/dist/OneBlinkFormBase.js.map +1 -1
  4. package/dist/OneBlinkReadOnlyForm.d.ts +17 -7
  5. package/dist/OneBlinkReadOnlyForm.js +24 -8
  6. package/dist/OneBlinkReadOnlyForm.js.map +1 -1
  7. package/dist/apps/approvals-service.d.ts +2 -60
  8. package/dist/apps/approvals-service.js +1 -181
  9. package/dist/apps/approvals-service.js.map +1 -1
  10. package/dist/apps/services/api/submissions.d.ts +3 -2
  11. package/dist/apps/services/api/submissions.js +3 -6
  12. package/dist/apps/services/api/submissions.js.map +1 -1
  13. package/dist/apps/submission-service.d.ts +11 -5
  14. package/dist/apps/submission-service.js +9 -4
  15. package/dist/apps/submission-service.js.map +1 -1
  16. package/dist/components/renderer/LookupButton.d.ts +1 -2
  17. package/dist/components/renderer/LookupButton.js +7 -6
  18. package/dist/components/renderer/LookupButton.js.map +1 -1
  19. package/dist/components/renderer/LookupNotification.js +31 -24
  20. package/dist/components/renderer/LookupNotification.js.map +1 -1
  21. package/dist/components/renderer/OneBlinkFormElements.d.ts +5 -7
  22. package/dist/components/renderer/OneBlinkFormElements.js +10 -35
  23. package/dist/components/renderer/OneBlinkFormElements.js.map +1 -1
  24. package/dist/components/renderer/PageFormElements.js +8 -3
  25. package/dist/components/renderer/PageFormElements.js.map +1 -1
  26. package/dist/components/renderer/ReverseGeocode.js +6 -11
  27. package/dist/components/renderer/ReverseGeocode.js.map +1 -1
  28. package/dist/form-elements/FormElementABN.js +1 -1
  29. package/dist/form-elements/FormElementABN.js.map +1 -1
  30. package/dist/form-elements/FormElementBSB.js +1 -1
  31. package/dist/form-elements/FormElementBSB.js.map +1 -1
  32. package/dist/form-elements/FormElementBarcodeScanner.js +1 -1
  33. package/dist/form-elements/FormElementBarcodeScanner.js.map +1 -1
  34. package/dist/form-elements/FormElementCalculation.js +1 -0
  35. package/dist/form-elements/FormElementCalculation.js.map +1 -1
  36. package/dist/form-elements/FormElementCheckBoxes.js +1 -1
  37. package/dist/form-elements/FormElementCheckBoxes.js.map +1 -1
  38. package/dist/form-elements/FormElementDate.js +1 -1
  39. package/dist/form-elements/FormElementDate.js.map +1 -1
  40. package/dist/form-elements/FormElementDateTime.js +1 -1
  41. package/dist/form-elements/FormElementDateTime.js.map +1 -1
  42. package/dist/form-elements/FormElementEmail.js +1 -1
  43. package/dist/form-elements/FormElementEmail.js.map +1 -1
  44. package/dist/form-elements/FormElementForm.d.ts +8 -9
  45. package/dist/form-elements/FormElementForm.js +4 -3
  46. package/dist/form-elements/FormElementForm.js.map +1 -1
  47. package/dist/form-elements/FormElementLookupButton.js +4 -3
  48. package/dist/form-elements/FormElementLookupButton.js.map +1 -1
  49. package/dist/form-elements/FormElementNumber.js +1 -1
  50. package/dist/form-elements/FormElementNumber.js.map +1 -1
  51. package/dist/form-elements/FormElementRepeatableSet.d.ts +4 -4
  52. package/dist/form-elements/FormElementRepeatableSet.js +6 -4
  53. package/dist/form-elements/FormElementRepeatableSet.js.map +1 -1
  54. package/dist/form-elements/FormElementSection.d.ts +3 -3
  55. package/dist/form-elements/FormElementSection.js +2 -2
  56. package/dist/form-elements/FormElementSection.js.map +1 -1
  57. package/dist/form-elements/FormElementSelect.js +1 -1
  58. package/dist/form-elements/FormElementSelect.js.map +1 -1
  59. package/dist/form-elements/FormElementSummary.js +2 -0
  60. package/dist/form-elements/FormElementSummary.js.map +1 -1
  61. package/dist/form-elements/FormElementTelephone.js +1 -1
  62. package/dist/form-elements/FormElementTelephone.js.map +1 -1
  63. package/dist/form-elements/FormElementText.js +1 -1
  64. package/dist/form-elements/FormElementText.js.map +1 -1
  65. package/dist/form-elements/FormElementTextarea.js +1 -1
  66. package/dist/form-elements/FormElementTextarea.js.map +1 -1
  67. package/dist/form-elements/FormElementTime.js +1 -1
  68. package/dist/form-elements/FormElementTime.js.map +1 -1
  69. package/dist/hooks/useAreLookupsDisallowed.d.ts +2 -0
  70. package/dist/hooks/useAreLookupsDisallowed.js +12 -0
  71. package/dist/hooks/useAreLookupsDisallowed.js.map +1 -0
  72. package/dist/hooks/useEditableFormElementIds.d.ts +3 -0
  73. package/dist/hooks/useEditableFormElementIds.js +6 -0
  74. package/dist/hooks/useEditableFormElementIds.js.map +1 -0
  75. package/dist/hooks/useFormElementOptions.js +2 -0
  76. package/dist/hooks/useFormElementOptions.js.map +1 -1
  77. package/dist/hooks/useFormIsDirty.d.ts +56 -0
  78. package/dist/hooks/useFormIsDirty.js +121 -0
  79. package/dist/hooks/useFormIsDirty.js.map +1 -0
  80. package/dist/hooks/useFormSubmissionAttempt.d.ts +88 -0
  81. package/dist/hooks/useFormSubmissionAttempt.js +103 -0
  82. package/dist/hooks/useFormSubmissionAttempt.js.map +1 -0
  83. package/dist/hooks/useFormSubmissionAutoSaveState.d.ts +2 -9
  84. package/dist/hooks/useFormSubmissionAutoSaveState.js +4 -2
  85. package/dist/hooks/useFormSubmissionAutoSaveState.js.map +1 -1
  86. package/dist/hooks/useFormValidation.d.ts +1 -1
  87. package/dist/hooks/useFormValidation.js +2 -1
  88. package/dist/hooks/useFormValidation.js.map +1 -1
  89. package/dist/hooks/useInjectPages.d.ts +2 -1
  90. package/dist/hooks/useInjectPages.js.map +1 -1
  91. package/dist/hooks/useIsFormElementReadOnly.d.ts +2 -0
  92. package/dist/hooks/useIsFormElementReadOnly.js +13 -0
  93. package/dist/hooks/useIsFormElementReadOnly.js.map +1 -0
  94. package/dist/hooks/useLookupNotification.d.ts +23 -2
  95. package/dist/hooks/useLookupNotification.js +2 -1
  96. package/dist/hooks/useLookupNotification.js.map +1 -1
  97. package/dist/hooks/useLookups.d.ts +3 -3
  98. package/dist/hooks/useLookups.js +7 -4
  99. package/dist/hooks/useLookups.js.map +1 -1
  100. package/dist/index.d.ts +2 -0
  101. package/dist/index.js +2 -0
  102. package/dist/index.js.map +1 -1
  103. package/dist/services/form-validation/extensions.d.ts +2 -1
  104. package/dist/services/form-validation/extensions.js +2 -1
  105. package/dist/services/form-validation/extensions.js.map +1 -1
  106. package/dist/services/form-validation/validateSubmission.d.ts +2 -1
  107. package/dist/services/form-validation/validateSubmission.js +14 -9
  108. package/dist/services/form-validation/validateSubmission.js.map +1 -1
  109. package/dist/services/getFlowInstanceNodesWithMeta.d.ts +70 -0
  110. package/dist/services/getFlowInstanceNodesWithMeta.js +247 -0
  111. package/dist/services/getFlowInstanceNodesWithMeta.js.map +1 -0
  112. package/dist/types/form.d.ts +17 -3
  113. package/dist/types/form.js.map +1 -1
  114. package/dist/utils/read-only-form-elements.d.ts +37 -0
  115. package/dist/utils/read-only-form-elements.js +63 -0
  116. package/dist/utils/read-only-form-elements.js.map +1 -0
  117. package/package.json +4 -3
  118. package/dist/services/isElementReadOnlyForAudience.d.ts +0 -20
  119. package/dist/services/isElementReadOnlyForAudience.js +0 -46
  120. package/dist/services/isElementReadOnlyForAudience.js.map +0 -1
  121. package/dist/services/resolveFormElementReadOnly.d.ts +0 -10
  122. package/dist/services/resolveFormElementReadOnly.js +0 -17
  123. package/dist/services/resolveFormElementReadOnly.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
+ import { flushSync } from 'react-dom';
3
4
  import { createTheme as createMuiTheme, ThemeProvider } from '@mui/material';
4
5
  import Tooltip from './components/renderer/Tooltip';
5
6
  import { Prompt, useHistory } from 'react-router-dom';
@@ -44,7 +45,40 @@ import { getElementDisplayNameForAnalyticsEvent, sendGoogleAnalyticsEvent, } fro
44
45
  import { useUserProfileForInjectablesOutsideContext } from './hooks/useUserProfileForInjectables';
45
46
  import { ValidationIconConfigurationContext } from './hooks/useValidationIconConfiguration';
46
47
  import { Clickable } from './components/Clickable';
47
- function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captchaSiteKey, definition, disabled, isPreview, submission, isReadOnly, onCancel, onSubmit, onSaveDraft, setFormSubmission, buttons, validationIcon, primaryColour, attachmentRetentionInDays, isPendingQueueEnabled, handleNavigateAway, isInfoPage: isInfoPageProp, lastElementUpdated, executedLookups, task, taskGroup, taskGroupInstance, onUploadAttachment, captchaType, shouldUseNavigableValidationErrorsNotification = true, navigableValidationErrorsNotificationSettings, replaceInjectablesOverrides, sectionState, scrollToTopOfPage, getCurrentSubmissionDuration, onBeforeUnload, audience, }) {
48
+ import { EditableFormElementIdsContext } from './hooks/useEditableFormElementIds';
49
+ import { useRegisterFormSubmissionAttempt } from './hooks/useFormSubmissionAttempt';
50
+ import { useRegisterFormIsDirty, useRegisterAllowFormNavigation, FormMarkDirtyContextProvider, } from './hooks/useFormIsDirty';
51
+ var AttachmentUploadStatus;
52
+ (function (AttachmentUploadStatus) {
53
+ AttachmentUploadStatus["ATTACHMENTS_UPLOADED"] = "ATTACHMENTS_UPLOADED";
54
+ AttachmentUploadStatus["ATTACHMENTS_UPLOAD_BLOCKING"] = "ATTACHMENTS_UPLOAD_BLOCKING";
55
+ AttachmentUploadStatus["ATTACHMENTS_UPLOAD_NON_BLOCKING"] = "ATTACHMENTS_UPLOAD_NON_BLOCKING";
56
+ })(AttachmentUploadStatus || (AttachmentUploadStatus = {}));
57
+ var SubmissionPreparationStatus;
58
+ (function (SubmissionPreparationStatus) {
59
+ SubmissionPreparationStatus["OFFLINE_SUBMISSION"] = "OFFLINE_SUBMISSION";
60
+ SubmissionPreparationStatus["SUBMISSION_READY"] = "SUBMISSION_READY";
61
+ })(SubmissionPreparationStatus || (SubmissionPreparationStatus = {}));
62
+ const idlePhase = { status: 'idle' };
63
+ function HostPreparationEffect({ continueWithAttachments, prepareSubmission, onResult, }) {
64
+ // prepareSubmission is in the dep array so that an environment change
65
+ // (e.g. going offline) that recreates prepareSubmission will re-trigger
66
+ // preparation with the updated closure. The cleanup function cancels
67
+ // the prior in-flight call before the new one starts.
68
+ React.useEffect(() => {
69
+ let cancelled = false;
70
+ prepareSubmission(continueWithAttachments).then((result) => {
71
+ if (!cancelled) {
72
+ onResult(result);
73
+ }
74
+ });
75
+ return () => {
76
+ cancelled = true;
77
+ };
78
+ }, [continueWithAttachments, onResult, prepareSubmission]);
79
+ return null;
80
+ }
81
+ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captchaSiteKey, definition, disabled, isPreview, submission, isReadOnly, onCancel, onSubmit, onSaveDraft, setFormSubmission, buttons, validationIcon, primaryColour, attachmentRetentionInDays, isPendingQueueEnabled, handleNavigateAway, isInfoPage: isInfoPageProp, lastElementUpdated, executedLookups, task, taskGroup, taskGroupInstance, onUploadAttachment, captchaType, shouldUseNavigableValidationErrorsNotification = true, navigableValidationErrorsNotificationSettings, replaceInjectablesOverrides, sectionState, scrollToTopOfPage, getCurrentSubmissionDuration, onBeforeUnload, audience, editableFormElementIds, }) {
48
82
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
49
83
  const isOffline = useIsOffline();
50
84
  const { isUsingFormsKey } = useAuth();
@@ -142,6 +176,8 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
142
176
  });
143
177
  const [promptOfflineSubmissionAttempt, setPromptOfflineSubmissionAttempt] = React.useState(false);
144
178
  const [promptUploadingAttachments, setPromptUploadingAttachments] = React.useState(false);
179
+ const [hostAttemptPhase, setHostAttemptPhase] = React.useState(idlePhase);
180
+ const [hostAttemptResolver, setHostAttemptResolver] = React.useState(null);
145
181
  const handleBlockedNavigation = React.useCallback((location) => {
146
182
  setUnsavedChangesState((current) => ({
147
183
  ...current,
@@ -225,6 +261,13 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
225
261
  isNavigationAllowed: true,
226
262
  }));
227
263
  }, []);
264
+ const allowNavigationAfterHostAction = React.useCallback(() => {
265
+ // React Router DOM v5's <Prompt> has no imperative "proceed" API, so the
266
+ // guard must rerender as disabled before the host navigates in the same
267
+ // tick. Migrating to the React Router DOM v6 blocker APIs would allow the
268
+ // blocked transition to proceed directly without flushSync().
269
+ flushSync(allowNavigation);
270
+ }, [allowNavigation]);
228
271
  // #endregion Unsaved Changed
229
272
  //
230
273
  //
@@ -246,8 +289,8 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
246
289
  const [isShowingValidationErrorsCard, setIsShowingValidationErrorsCard] = React.useState(false);
247
290
  const { validate } = useFormValidation(pages);
248
291
  const recaptchaType = React.useMemo(() => captchaType !== null && captchaType !== void 0 ? captchaType : 'CHECKBOX', [captchaType]);
249
- const formElementsValidation = React.useMemo(() => !isReadOnly
250
- ? validate(submission, formElementsConditionallyShown, executedLookups !== null && executedLookups !== void 0 ? executedLookups : {}, recaptchaType, isOffline, audience)
292
+ const formElementsValidation = React.useMemo(() => !isReadOnly || editableFormElementIds !== undefined
293
+ ? validate(submission, formElementsConditionallyShown, executedLookups !== null && executedLookups !== void 0 ? executedLookups : {}, recaptchaType, isOffline, audience, editableFormElementIds)
251
294
  : undefined, [
252
295
  isReadOnly,
253
296
  validate,
@@ -257,6 +300,7 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
257
300
  recaptchaType,
258
301
  isOffline,
259
302
  audience,
303
+ editableFormElementIds,
260
304
  ]);
261
305
  React.useEffect(() => {
262
306
  if (!formElementsValidation) {
@@ -312,7 +356,7 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
312
356
  // be taken care of by a pending queue if enabled, otherwise
313
357
  // the user will be prompted to try again or save a draft.
314
358
  if (isOffline) {
315
- return true;
359
+ return AttachmentUploadStatus.ATTACHMENTS_UPLOADED;
316
360
  }
317
361
  const attachmentsAreUploading = attachmentsService.checkIfAttachmentsAreUploading(definition, submission);
318
362
  if (attachmentsAreUploading) {
@@ -325,14 +369,13 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
325
369
  pauseOnHover: true,
326
370
  closeOnClick: true,
327
371
  });
328
- return false;
372
+ return AttachmentUploadStatus.ATTACHMENTS_UPLOAD_BLOCKING;
329
373
  }
330
374
  else {
331
- setPromptUploadingAttachments(true);
332
- return false;
375
+ return AttachmentUploadStatus.ATTACHMENTS_UPLOAD_NON_BLOCKING;
333
376
  }
334
377
  }
335
- return true;
378
+ return AttachmentUploadStatus.ATTACHMENTS_UPLOADED;
336
379
  }, [definition, isOffline, isPendingQueueEnabled, isUsingFormsKey]);
337
380
  const checkBsbsCanBeSubmitted = React.useCallback((submission) => {
338
381
  return !checkBsbsAreInvalid(definition, submission);
@@ -405,15 +448,29 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
405
448
  if (!checkBsbsCanBeSubmitted(submissionData.submission)) {
406
449
  return;
407
450
  }
408
- if (!continueWhilstAttachmentsAreUploading &&
409
- !checkAttachmentsCanBeSubmitted(submissionData.submission)) {
410
- return;
451
+ if (!continueWhilstAttachmentsAreUploading) {
452
+ const attachmentStatus = checkAttachmentsCanBeSubmitted(submissionData.submission);
453
+ switch (attachmentStatus) {
454
+ case AttachmentUploadStatus.ATTACHMENTS_UPLOAD_BLOCKING:
455
+ // The submitter cannot upload attachments in the background and must wait for them to upload before submitting
456
+ return;
457
+ case AttachmentUploadStatus.ATTACHMENTS_UPLOAD_NON_BLOCKING:
458
+ // We can display a prompt allowing the submitter to upload attachments in the background
459
+ return {
460
+ status: attachmentStatus,
461
+ };
462
+ case AttachmentUploadStatus.ATTACHMENTS_UPLOADED:
463
+ break;
464
+ default:
465
+ return;
466
+ }
411
467
  }
412
468
  if (captchaType === 'INVISIBLE') {
413
469
  if (captchasRef.current.length) {
414
470
  if (isOffline) {
415
- setPromptOfflineSubmissionAttempt(true);
416
- return;
471
+ return {
472
+ status: SubmissionPreparationStatus.OFFLINE_SUBMISSION,
473
+ };
417
474
  }
418
475
  const tokenResults = await Promise.allSettled(captchasRef.current.map((captcha) => captcha.executeAsync()));
419
476
  const captchaTokens = [];
@@ -454,10 +511,14 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
454
511
  }
455
512
  if (isOffline && !isPendingQueueEnabled) {
456
513
  console.log('User is offline and form does not support a pending queue');
457
- setPromptOfflineSubmissionAttempt(true);
458
- return;
514
+ return {
515
+ status: SubmissionPreparationStatus.OFFLINE_SUBMISSION,
516
+ };
459
517
  }
460
- return submissionData;
518
+ return {
519
+ status: SubmissionPreparationStatus.SUBMISSION_READY,
520
+ submissionData: submissionData,
521
+ };
461
522
  }, [
462
523
  attachmentRetentionInDays,
463
524
  captchaType,
@@ -472,6 +533,55 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
472
533
  setFormSubmission,
473
534
  shouldUseNavigableValidationErrorsNotification,
474
535
  ]);
536
+ const handleHostPreparationResult = React.useCallback((result) => {
537
+ if (!result) {
538
+ setHostAttemptPhase({ status: 'resolved', result: false });
539
+ return;
540
+ }
541
+ switch (result.status) {
542
+ case AttachmentUploadStatus.ATTACHMENTS_UPLOAD_NON_BLOCKING: {
543
+ setPromptUploadingAttachments(true);
544
+ setHostAttemptPhase({ status: 'prompt-attachments' });
545
+ break;
546
+ }
547
+ case SubmissionPreparationStatus.OFFLINE_SUBMISSION: {
548
+ setPromptOfflineSubmissionAttempt(true);
549
+ setHostAttemptPhase((current) => ({
550
+ status: 'prompt-offline',
551
+ continueWithAttachments: current.status === 'preparing'
552
+ ? current.continueWithAttachments
553
+ : false,
554
+ }));
555
+ break;
556
+ }
557
+ case SubmissionPreparationStatus.SUBMISSION_READY: {
558
+ setHostAttemptPhase({
559
+ status: 'resolved',
560
+ result: allowNavigationAfterHostAction,
561
+ });
562
+ break;
563
+ }
564
+ }
565
+ }, [allowNavigationAfterHostAction]);
566
+ const attemptFormSubmission = React.useCallback(() => {
567
+ setHasAttemptedSubmit(true);
568
+ setIsPreparingToSubmit(true);
569
+ setHostAttemptPhase({ status: 'preparing', continueWithAttachments: false });
570
+ return new Promise((resolve) => {
571
+ setHostAttemptResolver({ resolve });
572
+ });
573
+ }, []);
574
+ React.useEffect(() => {
575
+ if (hostAttemptPhase.status === 'resolved') {
576
+ setIsPreparingToSubmit(false);
577
+ hostAttemptResolver === null || hostAttemptResolver === void 0 ? void 0 : hostAttemptResolver.resolve(hostAttemptPhase.result);
578
+ setHostAttemptResolver(null);
579
+ setHostAttemptPhase(idlePhase);
580
+ }
581
+ }, [hostAttemptPhase, hostAttemptResolver]);
582
+ useRegisterFormSubmissionAttempt(editableFormElementIds === undefined ? undefined : attemptFormSubmission);
583
+ useRegisterFormIsDirty(definition.id, isDirty);
584
+ useRegisterAllowFormNavigation(definition.id, allowNavigationAfterHostAction);
475
585
  const handleSubmit = React.useCallback(async (event, continueWhilstAttachmentsAreUploading) => {
476
586
  event.preventDefault();
477
587
  // Approvers are not shown a submit button, so reaching here means the
@@ -482,39 +592,60 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
482
592
  return;
483
593
  setHasAttemptedSubmit(true);
484
594
  setIsPreparingToSubmit(true);
485
- const submissionData = await prepareSubmission(continueWhilstAttachmentsAreUploading);
486
- if (!submissionData) {
595
+ const submissionResult = await prepareSubmission(continueWhilstAttachmentsAreUploading);
596
+ if (!submissionResult) {
487
597
  setIsPreparingToSubmit(false);
488
598
  return;
489
599
  }
490
- allowNavigation();
491
- // transplant injected options on the definition
492
- const elementsWithInjectedOptions = injectOptionsAcrossAllElements({
493
- contextElements: definition.elements,
494
- elements: definition.elements,
495
- submission: submissionData.submission,
496
- taskContext: taskContextValue,
497
- userProfile: userProfileForInjectables,
498
- });
499
- setIsPreparingToSubmit(false);
500
- resetRecaptchas();
501
- onSubmit({
502
- definition: {
503
- ...definition,
504
- elements: elementsWithInjectedOptions,
505
- },
506
- submission: submissionData.submission,
507
- recaptchas: submissionData.captchaTokens.map((token) => ({
508
- token,
509
- siteKey: captchaSiteKey,
510
- })),
511
- });
512
- sendGoogleAnalyticsEvent('oneblink_form_submit', {
513
- formId: definition.id,
514
- formName: definition.name,
515
- lastElementUpdated: getElementDisplayNameForAnalyticsEvent(lastElementUpdated),
516
- durationUntilSubmission: getCurrentSubmissionDuration === null || getCurrentSubmissionDuration === void 0 ? void 0 : getCurrentSubmissionDuration(),
517
- });
600
+ switch (submissionResult.status) {
601
+ case AttachmentUploadStatus.ATTACHMENTS_UPLOAD_NON_BLOCKING: {
602
+ setPromptUploadingAttachments(true);
603
+ setIsPreparingToSubmit(false);
604
+ return;
605
+ }
606
+ case SubmissionPreparationStatus.OFFLINE_SUBMISSION: {
607
+ setPromptOfflineSubmissionAttempt(true);
608
+ setIsPreparingToSubmit(false);
609
+ return;
610
+ }
611
+ case SubmissionPreparationStatus.SUBMISSION_READY: {
612
+ allowNavigation();
613
+ const submissionData = submissionResult.submissionData;
614
+ // transplant injected options on the definition
615
+ const elementsWithInjectedOptions = injectOptionsAcrossAllElements({
616
+ contextElements: definition.elements,
617
+ elements: definition.elements,
618
+ submission: submissionData.submission,
619
+ taskContext: taskContextValue,
620
+ userProfile: userProfileForInjectables,
621
+ });
622
+ setIsPreparingToSubmit(false);
623
+ resetRecaptchas();
624
+ onSubmit({
625
+ definition: {
626
+ ...definition,
627
+ elements: elementsWithInjectedOptions,
628
+ },
629
+ submission: submissionData.submission,
630
+ recaptchas: submissionData.captchaTokens.map((token) => ({
631
+ token,
632
+ siteKey: captchaSiteKey,
633
+ })),
634
+ });
635
+ sendGoogleAnalyticsEvent('oneblink_form_submit', {
636
+ formId: definition.id,
637
+ formName: definition.name,
638
+ lastElementUpdated: getElementDisplayNameForAnalyticsEvent(lastElementUpdated),
639
+ durationUntilSubmission: getCurrentSubmissionDuration === null || getCurrentSubmissionDuration === void 0 ? void 0 : getCurrentSubmissionDuration(),
640
+ });
641
+ return;
642
+ }
643
+ default: {
644
+ const never = submissionResult;
645
+ setIsPreparingToSubmit(false);
646
+ return never;
647
+ }
648
+ }
518
649
  }, [
519
650
  disabled,
520
651
  isReadOnly,
@@ -541,9 +672,18 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
541
672
  if (!checkBsbAreValidating(submission)) {
542
673
  return;
543
674
  }
544
- if (!continueWhilstAttachmentsAreUploading &&
545
- !checkAttachmentsCanBeSubmitted(submission)) {
546
- return;
675
+ if (!continueWhilstAttachmentsAreUploading) {
676
+ const attachmentCheckResult = checkAttachmentsCanBeSubmitted(submission);
677
+ if (attachmentCheckResult ===
678
+ AttachmentUploadStatus.ATTACHMENTS_UPLOAD_NON_BLOCKING) {
679
+ setPromptUploadingAttachments(true);
680
+ return;
681
+ }
682
+ if (attachmentCheckResult ===
683
+ AttachmentUploadStatus.ATTACHMENTS_UPLOAD_BLOCKING) {
684
+ // The submitter cannot upload attachments in the background and must wait for them to upload before saving draft
685
+ return;
686
+ }
547
687
  }
548
688
  onSaveDraft({
549
689
  definition,
@@ -568,6 +708,13 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
568
708
  ]);
569
709
  const handleContinueWithAttachments = React.useCallback((e) => {
570
710
  setPromptUploadingAttachments(false);
711
+ if (hostAttemptPhase.status === 'prompt-attachments') {
712
+ setHostAttemptPhase({
713
+ status: 'preparing',
714
+ continueWithAttachments: true,
715
+ });
716
+ return;
717
+ }
571
718
  if (hasAttemptedSubmit) {
572
719
  handleSubmit(e, true);
573
720
  }
@@ -576,121 +723,129 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
576
723
  }
577
724
  }, [
578
725
  handleSubmit,
579
- setPromptUploadingAttachments,
726
+ hostAttemptPhase.status,
580
727
  hasAttemptedSubmit,
581
728
  handleSaveDraft,
582
729
  ]);
583
730
  const handleWaitForAttachments = React.useCallback(() => {
584
731
  setPromptUploadingAttachments(false);
585
- }, [setPromptUploadingAttachments]);
732
+ if (hostAttemptPhase.status === 'prompt-attachments') {
733
+ setHostAttemptPhase({ status: 'resolved', result: false });
734
+ }
735
+ }, [hostAttemptPhase.status]);
736
+ const handleCancelOfflineSubmission = React.useCallback(() => {
737
+ setPromptOfflineSubmissionAttempt(false);
738
+ if (hostAttemptPhase.status === 'prompt-offline') {
739
+ setHostAttemptPhase({ status: 'resolved', result: false });
740
+ }
741
+ }, [hostAttemptPhase.status]);
742
+ const handleRetryOfflineSubmission = React.useCallback((e) => {
743
+ setPromptOfflineSubmissionAttempt(false);
744
+ if (hostAttemptPhase.status === 'prompt-offline') {
745
+ setHostAttemptPhase({
746
+ status: 'preparing',
747
+ continueWithAttachments: hostAttemptPhase.continueWithAttachments,
748
+ });
749
+ }
750
+ else {
751
+ handleSubmit(e, false);
752
+ }
753
+ }, [
754
+ handleSubmit,
755
+ hostAttemptPhase,
756
+ ]);
586
757
  // #endregion
587
758
  //
588
759
  //
589
760
  //
590
761
  //
591
- // #region Lookups
592
- const { handlePagesLookupResult } = useLookups(definition.id, setFormSubmission);
593
- // #endregion
762
+ // #region Submission/Definition Changes
763
+ const markFormDirty = React.useCallback(() => {
764
+ setUnsavedChangesState((current) => ({
765
+ ...current,
766
+ isDirty: true,
767
+ }));
768
+ }, []);
594
769
  //
595
770
  //
771
+ // #region Lookups
772
+ const { handlePagesLookupResult } = useLookups(definition.id, setFormSubmission, markFormDirty);
773
+ // #endregion
596
774
  //
597
775
  //
598
- // #region Submission/Definition Changes
599
- const handleChange = React.useCallback((element, { value, executedLookups, deleteSection, sectionState }) => {
776
+ const handleChange = React.useCallback((element, { value, executedLookups, deleteSection, sectionState, isDerivedChange }) => {
600
777
  if (
601
- //This will ensure on a read only form that the summary and calculation elements
602
- //can still be displayed as it needs handleChange so it can render
603
- //due to the dynamic nature of the summary element.
604
- (disabled &&
605
- element.type !== 'summary' &&
606
- element.type !== 'calculation') ||
778
+ // Disabled forms still need derived writers (calculation, summary, …)
779
+ // so those elements can recompute and display from the current model.
780
+ (disabled && !isDerivedChange) ||
607
781
  element.type === 'page') {
608
782
  return;
609
783
  }
610
- switch (element.type) {
611
- case 'section': {
612
- setFormSubmission((currentFormSubmission) => {
613
- const currentSectionState = currentFormSubmission.sectionState || [];
614
- const existingSectionIndex = currentSectionState.findIndex((section) => section.id === element.id);
615
- const sectionIsInState = existingSectionIndex >= 0;
616
- let newSectionState;
617
- if (sectionIsInState && deleteSection) {
618
- newSectionState = currentSectionState.filter((section) => section.id !== element.id);
619
- }
620
- else if (sectionIsInState) {
621
- // Update state of the section
622
- newSectionState = currentSectionState.map((section, index) => index === existingSectionIndex
623
- ? {
624
- ...section,
625
- state: section.state === 'COLLAPSED'
626
- ? 'EXPANDED'
627
- : 'COLLAPSED',
628
- }
629
- : section);
630
- }
631
- else {
632
- // Add the section to state
633
- newSectionState = [
634
- ...currentSectionState,
635
- {
636
- id: element.id,
637
- state: element.isCollapsed ? 'EXPANDED' : 'COLLAPSED',
638
- },
639
- ];
640
- }
641
- return {
642
- ...currentFormSubmission,
643
- sectionState: newSectionState,
644
- };
645
- });
646
- break;
647
- }
648
- // Don't update the last element updated for elements the user cannot set the value of
649
- case 'summary':
650
- case 'calculation': {
651
- setFormSubmission((currentFormSubmission) => ({
652
- ...currentFormSubmission,
653
- submission: {
654
- ...currentFormSubmission.submission,
655
- [element.name]: typeof value === 'function'
656
- ? value(currentFormSubmission.submission[element.name])
657
- : value,
658
- },
659
- }));
660
- break;
661
- }
662
- default: {
663
- setUnsavedChangesState((current) => ({
664
- ...current,
665
- isDirty: true,
666
- }));
667
- setFormSubmission((currentFormSubmission) => {
668
- var _a;
669
- return {
670
- ...currentFormSubmission,
671
- submission: {
672
- ...currentFormSubmission.submission,
673
- [element.name]: typeof value === 'function'
674
- ? value(currentFormSubmission.submission[element.name])
675
- : value,
676
- },
677
- lastElementUpdated: element,
678
- executedLookups: {
679
- ...currentFormSubmission.executedLookups,
680
- [element.name]: typeof executedLookups === 'function'
681
- ? executedLookups((_a = currentFormSubmission.executedLookups) === null || _a === void 0 ? void 0 : _a[element.name])
682
- : executedLookups,
784
+ if (element.type === 'section') {
785
+ setFormSubmission((currentFormSubmission) => {
786
+ const currentSectionState = currentFormSubmission.sectionState || [];
787
+ const existingSectionIndex = currentSectionState.findIndex((section) => section.id === element.id);
788
+ const sectionIsInState = existingSectionIndex >= 0;
789
+ let newSectionState;
790
+ if (sectionIsInState && deleteSection) {
791
+ newSectionState = currentSectionState.filter((section) => section.id !== element.id);
792
+ }
793
+ else if (sectionIsInState) {
794
+ // Update state of the section
795
+ newSectionState = currentSectionState.map((section, index) => index === existingSectionIndex
796
+ ? {
797
+ ...section,
798
+ state: section.state === 'COLLAPSED' ? 'EXPANDED' : 'COLLAPSED',
799
+ }
800
+ : section);
801
+ }
802
+ else {
803
+ // Add the section to state
804
+ newSectionState = [
805
+ ...currentSectionState,
806
+ {
807
+ id: element.id,
808
+ state: element.isCollapsed ? 'EXPANDED' : 'COLLAPSED',
683
809
  },
684
- sectionState: sectionState
685
- ? typeof sectionState === 'function'
686
- ? sectionState(currentFormSubmission.sectionState)
687
- : sectionState
688
- : currentFormSubmission.sectionState,
689
- };
690
- });
691
- }
810
+ ];
811
+ }
812
+ return {
813
+ ...currentFormSubmission,
814
+ sectionState: newSectionState,
815
+ };
816
+ });
817
+ return;
818
+ }
819
+ if (!isDerivedChange) {
820
+ markFormDirty();
692
821
  }
693
- }, [disabled, setFormSubmission]);
822
+ setFormSubmission((currentFormSubmission) => {
823
+ var _a;
824
+ return {
825
+ ...currentFormSubmission,
826
+ submission: {
827
+ ...currentFormSubmission.submission,
828
+ [element.name]: typeof value === 'function'
829
+ ? value(currentFormSubmission.submission[element.name])
830
+ : value,
831
+ },
832
+ lastElementUpdated: isDerivedChange
833
+ ? currentFormSubmission.lastElementUpdated
834
+ : element,
835
+ executedLookups: {
836
+ ...currentFormSubmission.executedLookups,
837
+ [element.name]: typeof executedLookups === 'function'
838
+ ? executedLookups((_a = currentFormSubmission.executedLookups) === null || _a === void 0 ? void 0 : _a[element.name])
839
+ : executedLookups,
840
+ },
841
+ sectionState: sectionState
842
+ ? typeof sectionState === 'function'
843
+ ? sectionState(currentFormSubmission.sectionState)
844
+ : sectionState
845
+ : currentFormSubmission.sectionState,
846
+ };
847
+ }, { isDerivedChange });
848
+ }, [disabled, markFormDirty, setFormSubmission]);
694
849
  // #endregion
695
850
  //
696
851
  //
@@ -740,55 +895,58 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
740
895
  if (conditionalLogicError) {
741
896
  return (_jsx(_Fragment, { children: _jsxs("div", { className: "has-text-centered", children: [_jsx(MaterialIcon, { className: "has-text-warning icon-x-large", children: "error" }), _jsx("h3", { className: "title is-3", children: "Bad Form Configuration" }), _jsx("p", { className: "cypress-conditional-logic-error-message", children: conditionalLogicError.message }), _jsx("p", { className: "has-text-grey", children: localisationService.formatDatetimeLong(new Date()) })] }) }));
742
897
  }
743
- return (_jsx(ThemeProvider, { theme: theme, children: _jsx(LocalizationProvider, { dateAdapter: AdapterDateFns, children: _jsx(ReplaceInjectablesOverridesContext.Provider, { value: replaceInjectablesOverrides, children: _jsx(FormDefinitionContext.Provider, { value: definition, children: _jsx(FormElementOptionsContextProvider, { formIsReadOnly: isReadOnly, children: _jsx(FormElementLookupsContextProvider, { children: _jsx(OneBlinkFormContainerContext.Provider, { value: obFormContainerHTMLElementRef, children: _jsxs("div", { className: clsx('ob-form-container', {
744
- 'is-showing-pages': isShowingMultiplePages,
745
- }), ref: obFormContainerHTMLElementRef, children: [_jsx("form", { name: "obForm", className: `ob-form cypress-ob-form ob-form__page-${currentPageIndex + 1}`, noValidate: true, onSubmit: (e) => handleSubmit(e, false), children: _jsxs("div", { children: [_jsx("div", { ref: scrollToTopOfPageHTMLElementRef }), isShowingMultiplePages && (_jsxs("div", { className: clsx('ob-steps-navigation', {
746
- 'is-active': isStepsHeaderActive,
747
- }), children: [_jsxs(Clickable, { className: clsx('ob-steps-navigation__header', {
748
- 'is-active': isStepsHeaderActive,
749
- }), onClick: toggleStepsNavigation, children: [_jsx("span", { className: "icon is-invisible", children: _jsx(MaterialIcon, { children: "keyboard_arrow_down" }) }), _jsxs("div", { className: "steps-header-active-page", children: [isDisplayingCurrentPageError ? (_jsx("span", { className: "icon", children: _jsx(MaterialIcon, { className: "has-text-danger is-size-4", children: "warning" }) })) : (_jsx("span", { className: "steps-header-active-page-icon", children: currentPageNumber })), _jsx("span", { className: "steps-header-active-page-label cypress-tablet-step-title", children: currentPage ? currentPage.label : '' })] }), _jsx("span", { className: "dropdown icon", children: _jsx(MaterialIcon, { children: "keyboard_arrow_down" }) })] }), _jsx("div", { role: "navigation", className: clsx('ob-steps-navigation__steps', {
750
- 'is-active': isStepsHeaderActive,
751
- }), children: _jsx("div", { className: "steps is-small is-horizontal-tablet cypress-steps", children: visiblePages.map((page, index) => {
752
- const hasErrors = checkDisplayPageError(page);
753
- return (_jsxs(Clickable, { id: `steps-navigation-step-${page.id}`, "aria-label": `${page.label}, page ${index + 1} of ${visiblePages.length}`, "aria-current": currentPage.id === page.id
754
- ? 'step'
755
- : undefined, className: clsx('step-item cypress-step-item', {
756
- 'is-active': currentPage.id === page.id,
757
- 'is-completed': currentPageIndex > index,
758
- 'is-error': hasErrors,
759
- }), onClick: (e) => {
760
- e.stopPropagation();
761
- if (page.id !== currentPage.id) {
762
- setPageId(page.id);
763
- }
764
- }, children: [_jsx("div", { className: "step-marker step-marker-error ob-step-marker cypress-step-marker",
765
- // @ts-expect-error ???
766
- name: `cypress-page-stepper-${index + 1}`, value: index + 1, children: hasErrors ? (_jsx(Tooltip, { title: "Page has errors", children: _jsx("span", { className: "icon tooltip has-tooltip-top cypress-page-error", children: _jsx(MaterialIcon, { className: "has-text-danger is-size-3", children: "warning" }) }) })) : (_jsx("span", { children: index + 1 })) }), _jsx("div", { className: "step-details ob-step-details", children: _jsx("p", { className: "step-title ob-step-title cypress-desktop-step-title", id: `steps-navigation-step-label-${page.id}`, children: page.label }) })] }, page.id));
767
- }) }) })] })), _jsx("div", { className: clsx('ob-steps-navigation__background', {
768
- 'is-active': isStepsHeaderActive,
769
- }), onClick: toggleStepsNavigation }), _jsxs("div", { className: "steps", children: [_jsx("div", { className: clsx('steps-content', {
770
- 'is-single-step': !isShowingMultiplePages,
771
- }), children: _jsx(InjectPagesContext.Provider, { value: handlePagesLookupResult, children: _jsx(GoogleMapsApiKeyContext.Provider, { value: googleMapsApiKey, children: _jsx(AbnLookupAuthenticationGuidContext.Provider, { value: abnLookupAuthenticationGuid, children: _jsx(ValidationIconConfigurationContext.Provider, { value: validationIcon, children: _jsx(CaptchaContext.Provider, { value: captchaContextValue, children: _jsx(AttachmentBlobsProvider, { children: _jsx(FormIsReadOnlyContext.Provider, { value: isReadOnly, children: _jsx(FormAudienceContext.Provider, { value: audience, children: _jsx(TaskContext.Provider, { value: taskContextValue, children: _jsx(OnUploadAttachmentContext.Provider, { value: onUploadAttachment, children: visiblePages.map((pageElement) => (_jsx(PageFormElements, { isActive: pageElement.id ===
772
- currentPage.id, formId: definition.id, formElementsConditionallyShown: formElementsConditionallyShown, formElementsValidation: formElementsValidation, displayValidationMessages: hasAttemptedSubmit ||
773
- isDisplayingCurrentPageError, pageElement: pageElement, onChange: handleChange, model: submission, setFormSubmission: setFormSubmission, sectionState: sectionState }, pageElement.id))) }) }) }) }) }) }) }) }) }) }) }), isShowingMultiplePages && (_jsxs("div", { className: "steps-actions", children: [_jsx("div", { className: "steps-action", children: _jsxs("button", { type: "button", onClick: goToPreviousPage, disabled: isFirstVisiblePage, className: "button is-light cypress-pages-previous", children: [_jsx("span", { className: "icon", children: _jsx(MaterialIcon, { children: "keyboard_arrow_left" }) }), _jsx("span", { children: "Back" })] }) }), _jsx("div", { className: "step-progress-mobile cypress-steps-mobile", children: visiblePages.map((page, index) => (_jsx("div", { className: clsx('step-progress-mobile-dot', {
774
- 'is-active': currentPage.id === page.id,
775
- 'is-completed': currentPageIndex > index,
776
- 'has-background-danger': currentPage.id !== page.id &&
777
- checkDisplayPageError(page),
778
- }) }, page.id))) }), _jsx("div", { className: "steps-action", children: _jsxs("button", { type: "button", onClick: goToNextPage, disabled: isLastVisiblePage, className: "button is-light cypress-pages-next", children: [_jsx("span", { children: "Next" }), _jsx("span", { className: "icon", children: _jsx(MaterialIcon, { children: "keyboard_arrow_right" }) })] }) })] }))] }), !isReadOnly && audience !== 'APPROVER' && (_jsxs("div", { className: "buttons ob-buttons ob-buttons-submit", children: [onSaveDraft && !isInfoPage && (_jsx("button", { type: "button", className: "button ob-button is-primary ob-button-save-draft cypress-save-draft-form", onClick: () => handleSaveDraft(false), disabled: isPreview || disabled, children: _jsx(CustomisableButtonInner, { label: ((_a = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _a === void 0 ? void 0 : _a.label) || 'Save Draft', icon: (_b = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _b === void 0 ? void 0 : _b.icon }) })), _jsx("span", { className: "ob-buttons-submit__spacer" }), !isInfoPage && (_jsx("button", { type: "button", className: "button ob-button is-light ob-button-submit-cancel cypress-cancel-form", onClick: handleCancel, disabled: isPreview || disabled, children: _jsx(CustomisableButtonInner, { label: ((_c = buttons === null || buttons === void 0 ? void 0 : buttons.cancel) === null || _c === void 0 ? void 0 : _c.label) || 'Cancel', icon: (_d = buttons === null || buttons === void 0 ? void 0 : buttons.cancel) === null || _d === void 0 ? void 0 : _d.icon }) })), isLastVisiblePage && (_jsx(Tooltip, { title: submissionConditionallyEnabled
779
- ? ''
780
- : 'Submission disabled: Your inputs have not met the criteria for submission', children: _jsx("span", { children: _jsx("button", { type: "submit", className: clsx('button ob-button is-success ob-button-submit cypress-submit-form-button cypress-submit-form', { 'is-loading': isPreparingToSubmit }, {
781
- 'ob-button-submit-is-disabled': submissionConditionallyEnabled,
782
- }), disabled: isPreview ||
783
- disabled ||
784
- isPreparingToSubmit ||
785
- !submissionConditionallyEnabled, children: _jsx(CustomisableButtonInner, { label: isInfoPage
786
- ? 'Done'
787
- : ((_e = buttons === null || buttons === void 0 ? void 0 : buttons.submit) === null || _e === void 0 ? void 0 : _e.label) || 'Submit', icon: (_f = buttons === null || buttons === void 0 ? void 0 : buttons.submit) === null || _f === void 0 ? void 0 : _f.icon }) }) }) }))] }))] }) }), !isReadOnly && !isPreview && audience !== 'APPROVER' && (_jsxs(React.Fragment, { children: [_jsx(Prompt, { when: isDirty && !isNavigationAllowed, message: handleBlockedNavigation }), _jsx(Modal, { isOpen: hasConfirmedNavigation === false, title: "Unsaved Changes", cardClassName: "cypress-cancel-confirm", titleClassName: "cypress-cancel-confirm-title", bodyClassName: "cypress-cancel-confirm-body", actions: _jsxs(_Fragment, { children: [onSaveDraft && (_jsx("button", { type: "button", className: "button ob-button is-success cypress-cancel-confirm-save-draft", onClick: () => handleSaveDraft(false), children: _jsx(CustomisableButtonInner, { label: ((_g = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _g === void 0 ? void 0 : _g.label) || 'Save Draft', icon: (_h = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _h === void 0 ? void 0 : _h.icon }) })), _jsx("span", { style: { flex: 1 } }), _jsx("button", { type: "button", className: "button ob-button is-light cypress-cancel-confirm-back", onClick: handleKeepGoing, children: _jsx(CustomisableButtonInner, { label: ((_j = buttons === null || buttons === void 0 ? void 0 : buttons.cancelPromptNo) === null || _j === void 0 ? void 0 : _j.label) || 'Back', icon: (_k = buttons === null || buttons === void 0 ? void 0 : buttons.cancelPromptNo) === null || _k === void 0 ? void 0 : _k.icon }) }), _jsx("button", { type: "button", className: "button ob-button is-primary cypress-cancel-confirm-discard", onClick: handleDiscardUnsavedChanges, autoFocus: true, children: _jsx(CustomisableButtonInner, { label: ((_l = buttons === null || buttons === void 0 ? void 0 : buttons.cancelPromptYes) === null || _l === void 0 ? void 0 : _l.label) || 'Discard', icon: (_m = buttons === null || buttons === void 0 ? void 0 : buttons.cancelPromptYes) === null || _m === void 0 ? void 0 : _m.icon }) })] }), children: _jsx("p", { children: "You have unsaved changes, are you sure you want discard them?" }) }), _jsx(Modal, { isOpen: promptUploadingAttachments === true, title: "Attachment upload in progress", cardClassName: "cypress-attachments-wait-continue", titleClassName: "cypress-attachments-confirm-wait-title", bodyClassName: "cypress-attachments-confirm-wait-body", actions: _jsxs(_Fragment, { children: [_jsx("span", { style: { flex: 1 } }), _jsx("button", { type: "button", className: "button ob-button is-light cypress-attachments-confirm-wait", onClick: handleWaitForAttachments, children: "Wait" }), _jsx("button", { type: "button", className: "button ob-button is-primary cypress-attachments-confirm-continue", onClick: handleContinueWithAttachments, autoFocus: true, children: "Continue" })] }), children: _jsx("p", { children: "Your attachments are still uploading, do you want to wait for the uploads to complete or continue using the app? If you click continue the attachments will upload in the background. Do not close the app until the upload has been completed." }) }), _jsxs(Modal, { isOpen: promptOfflineSubmissionAttempt, title: "It looks like you're Offline", className: "ob-modal__offline-submission-attempt", cardClassName: "cypress-submission-offline has-text-centered", titleClassName: "cypress-offline-title", bodyClassName: "cypress-offline-body", actions: _jsxs(_Fragment, { children: [onSaveDraft && (_jsx("button", { type: "button", className: "button ob-button ob-button__offline-submission-attempt-save-draft is-success", onClick: () => handleSaveDraft(false), children: _jsx(CustomisableButtonInner, { label: ((_o = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _o === void 0 ? void 0 : _o.label) || 'Save Draft', icon: (_p = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _p === void 0 ? void 0 : _p.icon }) })), _jsx("span", { style: { flex: 1 } }), _jsx("button", { className: "button ob-button ob-button__offline-submission-attempt-cancel is-light", onClick: () => setPromptOfflineSubmissionAttempt(false), children: "Cancel" }), _jsx("button", { className: "button ob-button ob-button__offline-submission-attempt-try-again is-primary", onClick: (e) => {
788
- setPromptOfflineSubmissionAttempt(false);
789
- handleSubmit(e, false);
790
- }, autoFocus: true, children: "Try Again" })] }), children: [_jsxs("p", { className: "ob-modal__offline-submission-attempt-message", children: ["You cannot submit this form while offline, please try again when connectivity is restored.", onSaveDraft && (_jsxs("span", { className: "ob-modal__offline-submission-attempt-save-draft-message", children: [' ', "Alternatively, click the", ' ', _jsx("b", { children: ((_q = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _q === void 0 ? void 0 : _q.label) || 'Save Draft' }), ' ', "button below to come back to this later."] }))] }), _jsx(MaterialIcon, { className: "has-text-warning icon-x-large ob-modal__offline-submission-attempt-icon", children: "wifi_off" })] })] })), shouldUseNavigableValidationErrorsNotification &&
791
- isShowingValidationErrorsCard && (_jsx(ValidationErrorsCard, { visiblePages: visiblePages, formElementsValidation: formElementsValidation, setPageId: setPageId, currentPage: currentPage, navigationTopOffset: (_r = navigableValidationErrorsNotificationSettings === null || navigableValidationErrorsNotificationSettings === void 0 ? void 0 : navigableValidationErrorsNotificationSettings.navigationTopOffset) !== null && _r !== void 0 ? _r : 'CALCULATE', scrollableContainerId: navigableValidationErrorsNotificationSettings === null || navigableValidationErrorsNotificationSettings === void 0 ? void 0 : navigableValidationErrorsNotificationSettings.scrollableContainerId, validationErrorToastFocusElementRef: validationErrorToastFocusElementRef }))] }) }) }) }) }) }) }) }));
898
+ return (_jsx(FormMarkDirtyContextProvider, { markDirty: markFormDirty, children: _jsx(ThemeProvider, { theme: theme, children: _jsx(LocalizationProvider, { dateAdapter: AdapterDateFns, children: _jsx(ReplaceInjectablesOverridesContext.Provider, { value: replaceInjectablesOverrides, children: _jsx(FormDefinitionContext.Provider, { value: definition, children: _jsx(FormElementOptionsContextProvider, { formIsReadOnly: isReadOnly, children: _jsx(FormElementLookupsContextProvider, { children: _jsx(OneBlinkFormContainerContext.Provider, { value: obFormContainerHTMLElementRef, children: _jsxs("div", { className: clsx('ob-form-container', {
899
+ 'is-showing-pages': isShowingMultiplePages,
900
+ }), ref: obFormContainerHTMLElementRef, children: [_jsx("form", { name: "obForm", className: `ob-form cypress-ob-form ob-form__page-${currentPageIndex + 1}`, noValidate: true, onSubmit: (e) => handleSubmit(e, false), children: _jsxs("div", { children: [_jsx("div", { ref: scrollToTopOfPageHTMLElementRef }), isShowingMultiplePages && (_jsxs("div", { className: clsx('ob-steps-navigation', {
901
+ 'is-active': isStepsHeaderActive,
902
+ }), children: [_jsxs(Clickable, { className: clsx('ob-steps-navigation__header', {
903
+ 'is-active': isStepsHeaderActive,
904
+ }), onClick: toggleStepsNavigation, children: [_jsx("span", { className: "icon is-invisible", children: _jsx(MaterialIcon, { children: "keyboard_arrow_down" }) }), _jsxs("div", { className: "steps-header-active-page", children: [isDisplayingCurrentPageError ? (_jsx("span", { className: "icon", children: _jsx(MaterialIcon, { className: "has-text-danger is-size-4", children: "warning" }) })) : (_jsx("span", { className: "steps-header-active-page-icon", children: currentPageNumber })), _jsx("span", { className: "steps-header-active-page-label cypress-tablet-step-title", children: currentPage ? currentPage.label : '' })] }), _jsx("span", { className: "dropdown icon", children: _jsx(MaterialIcon, { children: "keyboard_arrow_down" }) })] }), _jsx("div", { role: "navigation", className: clsx('ob-steps-navigation__steps', {
905
+ 'is-active': isStepsHeaderActive,
906
+ }), children: _jsx("div", { className: "steps is-small is-horizontal-tablet cypress-steps", children: visiblePages.map((page, index) => {
907
+ const hasErrors = checkDisplayPageError(page);
908
+ return (_jsxs(Clickable, { id: `steps-navigation-step-${page.id}`, "aria-label": `${page.label}, page ${index + 1} of ${visiblePages.length}`, "aria-current": currentPage.id === page.id
909
+ ? 'step'
910
+ : undefined, className: clsx('step-item cypress-step-item', {
911
+ 'is-active': currentPage.id === page.id,
912
+ 'is-completed': currentPageIndex > index,
913
+ 'is-error': hasErrors,
914
+ }), onClick: (e) => {
915
+ e.stopPropagation();
916
+ if (page.id !== currentPage.id) {
917
+ setPageId(page.id);
918
+ }
919
+ }, children: [_jsx("div", { className: "step-marker step-marker-error ob-step-marker cypress-step-marker",
920
+ // @ts-expect-error ???
921
+ name: `cypress-page-stepper-${index + 1}`, value: index + 1, children: hasErrors ? (_jsx(Tooltip, { title: "Page has errors", children: _jsx("span", { className: "icon tooltip has-tooltip-top cypress-page-error", children: _jsx(MaterialIcon, { className: "has-text-danger is-size-3", children: "warning" }) }) })) : (_jsx("span", { children: index + 1 })) }), _jsx("div", { className: "step-details ob-step-details", children: _jsx("p", { className: "step-title ob-step-title cypress-desktop-step-title", id: `steps-navigation-step-label-${page.id}`, children: page.label }) })] }, page.id));
922
+ }) }) })] })), _jsx("div", { className: clsx('ob-steps-navigation__background', {
923
+ 'is-active': isStepsHeaderActive,
924
+ }), onClick: toggleStepsNavigation }), _jsxs("div", { className: "steps", children: [_jsx("div", { className: clsx('steps-content', {
925
+ 'is-single-step': !isShowingMultiplePages,
926
+ }), children: _jsx(InjectPagesContext.Provider, { value: handlePagesLookupResult, children: _jsx(GoogleMapsApiKeyContext.Provider, { value: googleMapsApiKey, children: _jsx(AbnLookupAuthenticationGuidContext.Provider, { value: abnLookupAuthenticationGuid, children: _jsx(ValidationIconConfigurationContext.Provider, { value: validationIcon, children: _jsx(CaptchaContext.Provider, { value: captchaContextValue, children: _jsx(AttachmentBlobsProvider, { children: _jsx(FormIsReadOnlyContext.Provider, { value: isReadOnly, children: _jsx(EditableFormElementIdsContext.Provider, { value: editableFormElementIds, children: _jsx(FormAudienceContext.Provider, { value: audience, children: _jsx(TaskContext.Provider, { value: taskContextValue, children: _jsx(OnUploadAttachmentContext.Provider, { value: onUploadAttachment, children: visiblePages.map((pageElement) => (_jsx(PageFormElements, { isActive: pageElement.id ===
927
+ currentPage.id, formId: definition.id, formElementsConditionallyShown: formElementsConditionallyShown, formElementsValidation: formElementsValidation, displayValidationMessages: hasAttemptedSubmit ||
928
+ isDisplayingCurrentPageError, pageElement: pageElement, onChange: handleChange, model: submission, setFormSubmission: setFormSubmission, sectionState: sectionState }, pageElement.id))) }) }) }) }) }) }) }) }) }) }) }) }), isShowingMultiplePages && (_jsxs("div", { className: "steps-actions", children: [_jsx("div", { className: "steps-action", children: _jsxs("button", { type: "button", onClick: goToPreviousPage, disabled: isFirstVisiblePage, className: "button is-light cypress-pages-previous", children: [_jsx("span", { className: "icon", children: _jsx(MaterialIcon, { children: "keyboard_arrow_left" }) }), _jsx("span", { children: "Back" })] }) }), _jsx("div", { className: "step-progress-mobile cypress-steps-mobile", children: visiblePages.map((page, index) => (_jsx("div", { className: clsx('step-progress-mobile-dot', {
929
+ 'is-active': currentPage.id === page.id,
930
+ 'is-completed': currentPageIndex > index,
931
+ 'has-background-danger': currentPage.id !== page.id &&
932
+ checkDisplayPageError(page),
933
+ }) }, page.id))) }), _jsx("div", { className: "steps-action", children: _jsxs("button", { type: "button", onClick: goToNextPage, disabled: isLastVisiblePage, className: "button is-light cypress-pages-next", children: [_jsx("span", { children: "Next" }), _jsx("span", { className: "icon", children: _jsx(MaterialIcon, { children: "keyboard_arrow_right" }) })] }) })] }))] }), !isReadOnly && audience !== 'APPROVER' && (_jsxs("div", { className: "buttons ob-buttons ob-buttons-submit", children: [onSaveDraft && !isInfoPage && (_jsx("button", { type: "button", className: "button ob-button is-primary ob-button-save-draft cypress-save-draft-form", onClick: () => handleSaveDraft(false), disabled: isPreview || disabled, children: _jsx(CustomisableButtonInner, { label: ((_a = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _a === void 0 ? void 0 : _a.label) || 'Save Draft', icon: (_b = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _b === void 0 ? void 0 : _b.icon }) })), _jsx("span", { className: "ob-buttons-submit__spacer" }), !isInfoPage && (_jsx("button", { type: "button", className: "button ob-button is-light ob-button-submit-cancel cypress-cancel-form", onClick: handleCancel, disabled: isPreview || disabled, children: _jsx(CustomisableButtonInner, { label: ((_c = buttons === null || buttons === void 0 ? void 0 : buttons.cancel) === null || _c === void 0 ? void 0 : _c.label) || 'Cancel', icon: (_d = buttons === null || buttons === void 0 ? void 0 : buttons.cancel) === null || _d === void 0 ? void 0 : _d.icon }) })), isLastVisiblePage && (_jsx(Tooltip, { title: submissionConditionallyEnabled
934
+ ? ''
935
+ : 'Submission disabled: Your inputs have not met the criteria for submission', children: _jsx("span", { children: _jsx("button", { type: "submit", className: clsx('button ob-button is-success ob-button-submit cypress-submit-form-button cypress-submit-form', { 'is-loading': isPreparingToSubmit }, {
936
+ 'ob-button-submit-is-disabled': submissionConditionallyEnabled,
937
+ }), disabled: isPreview ||
938
+ disabled ||
939
+ isPreparingToSubmit ||
940
+ !submissionConditionallyEnabled, children: _jsx(CustomisableButtonInner, { label: isInfoPage
941
+ ? 'Done'
942
+ : ((_e = buttons === null || buttons === void 0 ? void 0 : buttons.submit) === null || _e === void 0 ? void 0 : _e.label) || 'Submit', icon: (_f = buttons === null || buttons === void 0 ? void 0 : buttons.submit) === null || _f === void 0 ? void 0 : _f.icon }) }) }) }))] }))] }) }), !isPreview &&
943
+ (editableFormElementIds !== undefined ||
944
+ !isReadOnly) && (_jsxs(React.Fragment, { children: [_jsx(Prompt, { when: isDirty && !isNavigationAllowed, message: handleBlockedNavigation }), _jsx(Modal, { isOpen: hasConfirmedNavigation === false, title: "Unsaved Changes", cardClassName: "cypress-cancel-confirm", titleClassName: "cypress-cancel-confirm-title", bodyClassName: "cypress-cancel-confirm-body", actions: _jsxs(_Fragment, { children: [onSaveDraft && (_jsx("button", { type: "button", className: "button ob-button is-success cypress-cancel-confirm-save-draft", onClick: () => handleSaveDraft(false), children: _jsx(CustomisableButtonInner, { label: ((_g = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _g === void 0 ? void 0 : _g.label) ||
945
+ 'Save Draft', icon: (_h = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _h === void 0 ? void 0 : _h.icon }) })), _jsx("span", { style: { flex: 1 } }), _jsx("button", { type: "button", className: "button ob-button is-light cypress-cancel-confirm-back", onClick: handleKeepGoing, children: _jsx(CustomisableButtonInner, { label: ((_j = buttons === null || buttons === void 0 ? void 0 : buttons.cancelPromptNo) === null || _j === void 0 ? void 0 : _j.label) || 'Back', icon: (_k = buttons === null || buttons === void 0 ? void 0 : buttons.cancelPromptNo) === null || _k === void 0 ? void 0 : _k.icon }) }), _jsx("button", { type: "button", className: "button ob-button is-primary cypress-cancel-confirm-discard", onClick: handleDiscardUnsavedChanges, autoFocus: true, children: _jsx(CustomisableButtonInner, { label: ((_l = buttons === null || buttons === void 0 ? void 0 : buttons.cancelPromptYes) === null || _l === void 0 ? void 0 : _l.label) ||
946
+ 'Discard', icon: (_m = buttons === null || buttons === void 0 ? void 0 : buttons.cancelPromptYes) === null || _m === void 0 ? void 0 : _m.icon }) })] }), children: _jsx("p", { children: "You have unsaved changes, are you sure you want discard them?" }) }), hostAttemptPhase.status === 'preparing' && (_jsx(HostPreparationEffect, { continueWithAttachments: hostAttemptPhase.continueWithAttachments, prepareSubmission: prepareSubmission, onResult: handleHostPreparationResult })), _jsx(Modal, { isOpen: promptUploadingAttachments === true, title: "Attachment upload in progress", cardClassName: "cypress-attachments-wait-continue", titleClassName: "cypress-attachments-confirm-wait-title", bodyClassName: "cypress-attachments-confirm-wait-body", actions: _jsxs(_Fragment, { children: [_jsx("span", { style: { flex: 1 } }), _jsx("button", { type: "button", className: "button ob-button is-light cypress-attachments-confirm-wait", onClick: handleWaitForAttachments, children: "Wait" }), _jsx("button", { type: "button", className: "button ob-button is-primary cypress-attachments-confirm-continue", onClick: handleContinueWithAttachments, autoFocus: true, children: "Continue" })] }), children: _jsx("p", { children: "Your attachments are still uploading, do you want to wait for the uploads to complete or continue using the app? If you click continue the attachments will upload in the background. Do not close the app until the upload has been completed." }) }), _jsxs(Modal, { isOpen: promptOfflineSubmissionAttempt, title: "It looks like you're Offline", className: "ob-modal__offline-submission-attempt", cardClassName: "cypress-submission-offline has-text-centered", titleClassName: "cypress-offline-title", bodyClassName: "cypress-offline-body", actions: _jsxs(_Fragment, { children: [onSaveDraft && (_jsx("button", { type: "button", className: "button ob-button ob-button__offline-submission-attempt-save-draft is-success", onClick: () => handleSaveDraft(false), children: _jsx(CustomisableButtonInner, { label: ((_o = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _o === void 0 ? void 0 : _o.label) ||
947
+ 'Save Draft', icon: (_p = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _p === void 0 ? void 0 : _p.icon }) })), _jsx("span", { style: { flex: 1 } }), _jsx("button", { className: "button ob-button ob-button__offline-submission-attempt-cancel is-light cypress-offline-submission-cancel", onClick: handleCancelOfflineSubmission, children: "Cancel" }), _jsx("button", { className: "button ob-button ob-button__offline-submission-attempt-try-again is-primary cypress-offline-submission-try-again", onClick: handleRetryOfflineSubmission, autoFocus: true, children: "Try Again" })] }), children: [_jsxs("p", { className: "ob-modal__offline-submission-attempt-message", children: ["You cannot submit this form while offline, please try again when connectivity is restored.", onSaveDraft && (_jsxs("span", { className: "ob-modal__offline-submission-attempt-save-draft-message", children: [' ', "Alternatively, click the", ' ', _jsx("b", { children: ((_q = buttons === null || buttons === void 0 ? void 0 : buttons.saveDraft) === null || _q === void 0 ? void 0 : _q.label) ||
948
+ 'Save Draft' }), ' ', "button below to come back to this later."] }))] }), _jsx(MaterialIcon, { className: "has-text-warning icon-x-large ob-modal__offline-submission-attempt-icon", children: "wifi_off" })] })] })), shouldUseNavigableValidationErrorsNotification &&
949
+ isShowingValidationErrorsCard && (_jsx(ValidationErrorsCard, { visiblePages: visiblePages, formElementsValidation: formElementsValidation, setPageId: setPageId, currentPage: currentPage, navigationTopOffset: (_r = navigableValidationErrorsNotificationSettings === null || navigableValidationErrorsNotificationSettings === void 0 ? void 0 : navigableValidationErrorsNotificationSettings.navigationTopOffset) !== null && _r !== void 0 ? _r : 'CALCULATE', scrollableContainerId: navigableValidationErrorsNotificationSettings === null || navigableValidationErrorsNotificationSettings === void 0 ? void 0 : navigableValidationErrorsNotificationSettings.scrollableContainerId, validationErrorToastFocusElementRef: validationErrorToastFocusElementRef }))] }) }) }) }) }) }) }) }) }));
792
950
  }
793
951
  export default React.memo(OneBlinkFormBase);
794
952
  //# sourceMappingURL=OneBlinkFormBase.js.map