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

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 (119) hide show
  1. package/dist/OneBlinkFormBase.d.ts +23 -3
  2. package/dist/OneBlinkFormBase.js +350 -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 +1 -0
  8. package/dist/apps/approvals-service.js.map +1 -1
  9. package/dist/apps/services/api/submissions.d.ts +3 -2
  10. package/dist/apps/services/api/submissions.js +3 -6
  11. package/dist/apps/services/api/submissions.js.map +1 -1
  12. package/dist/apps/submission-service.d.ts +11 -5
  13. package/dist/apps/submission-service.js +9 -4
  14. package/dist/apps/submission-service.js.map +1 -1
  15. package/dist/components/renderer/LookupButton.d.ts +1 -2
  16. package/dist/components/renderer/LookupButton.js +7 -6
  17. package/dist/components/renderer/LookupButton.js.map +1 -1
  18. package/dist/components/renderer/LookupNotification.js +31 -24
  19. package/dist/components/renderer/LookupNotification.js.map +1 -1
  20. package/dist/components/renderer/OneBlinkFormElements.d.ts +5 -7
  21. package/dist/components/renderer/OneBlinkFormElements.js +10 -35
  22. package/dist/components/renderer/OneBlinkFormElements.js.map +1 -1
  23. package/dist/components/renderer/PageFormElements.js +8 -3
  24. package/dist/components/renderer/PageFormElements.js.map +1 -1
  25. package/dist/components/renderer/ReverseGeocode.js +6 -11
  26. package/dist/components/renderer/ReverseGeocode.js.map +1 -1
  27. package/dist/form-elements/FormElementABN.js +1 -1
  28. package/dist/form-elements/FormElementABN.js.map +1 -1
  29. package/dist/form-elements/FormElementBSB.js +1 -1
  30. package/dist/form-elements/FormElementBSB.js.map +1 -1
  31. package/dist/form-elements/FormElementBarcodeScanner.js +1 -1
  32. package/dist/form-elements/FormElementBarcodeScanner.js.map +1 -1
  33. package/dist/form-elements/FormElementCalculation.js +1 -0
  34. package/dist/form-elements/FormElementCalculation.js.map +1 -1
  35. package/dist/form-elements/FormElementCheckBoxes.js +1 -1
  36. package/dist/form-elements/FormElementCheckBoxes.js.map +1 -1
  37. package/dist/form-elements/FormElementDate.js +1 -1
  38. package/dist/form-elements/FormElementDate.js.map +1 -1
  39. package/dist/form-elements/FormElementDateTime.js +1 -1
  40. package/dist/form-elements/FormElementDateTime.js.map +1 -1
  41. package/dist/form-elements/FormElementEmail.js +1 -1
  42. package/dist/form-elements/FormElementEmail.js.map +1 -1
  43. package/dist/form-elements/FormElementForm.d.ts +8 -9
  44. package/dist/form-elements/FormElementForm.js +4 -3
  45. package/dist/form-elements/FormElementForm.js.map +1 -1
  46. package/dist/form-elements/FormElementLookupButton.js +4 -3
  47. package/dist/form-elements/FormElementLookupButton.js.map +1 -1
  48. package/dist/form-elements/FormElementNumber.js +1 -1
  49. package/dist/form-elements/FormElementNumber.js.map +1 -1
  50. package/dist/form-elements/FormElementRepeatableSet.d.ts +4 -4
  51. package/dist/form-elements/FormElementRepeatableSet.js +6 -4
  52. package/dist/form-elements/FormElementRepeatableSet.js.map +1 -1
  53. package/dist/form-elements/FormElementSection.d.ts +3 -3
  54. package/dist/form-elements/FormElementSection.js +2 -2
  55. package/dist/form-elements/FormElementSection.js.map +1 -1
  56. package/dist/form-elements/FormElementSelect.js +1 -1
  57. package/dist/form-elements/FormElementSelect.js.map +1 -1
  58. package/dist/form-elements/FormElementSummary.js +2 -0
  59. package/dist/form-elements/FormElementSummary.js.map +1 -1
  60. package/dist/form-elements/FormElementTelephone.js +1 -1
  61. package/dist/form-elements/FormElementTelephone.js.map +1 -1
  62. package/dist/form-elements/FormElementText.js +1 -1
  63. package/dist/form-elements/FormElementText.js.map +1 -1
  64. package/dist/form-elements/FormElementTextarea.js +1 -1
  65. package/dist/form-elements/FormElementTextarea.js.map +1 -1
  66. package/dist/form-elements/FormElementTime.js +1 -1
  67. package/dist/form-elements/FormElementTime.js.map +1 -1
  68. package/dist/hooks/useAreLookupsDisallowed.d.ts +2 -0
  69. package/dist/hooks/useAreLookupsDisallowed.js +12 -0
  70. package/dist/hooks/useAreLookupsDisallowed.js.map +1 -0
  71. package/dist/hooks/useEditableFormElementIds.d.ts +3 -0
  72. package/dist/hooks/useEditableFormElementIds.js +6 -0
  73. package/dist/hooks/useEditableFormElementIds.js.map +1 -0
  74. package/dist/hooks/useFormElementOptions.js +2 -0
  75. package/dist/hooks/useFormElementOptions.js.map +1 -1
  76. package/dist/hooks/useFormIsDirty.d.ts +38 -0
  77. package/dist/hooks/useFormIsDirty.js +78 -0
  78. package/dist/hooks/useFormIsDirty.js.map +1 -0
  79. package/dist/hooks/useFormSubmissionAttempt.d.ts +88 -0
  80. package/dist/hooks/useFormSubmissionAttempt.js +103 -0
  81. package/dist/hooks/useFormSubmissionAttempt.js.map +1 -0
  82. package/dist/hooks/useFormSubmissionAutoSaveState.d.ts +2 -9
  83. package/dist/hooks/useFormSubmissionAutoSaveState.js +4 -2
  84. package/dist/hooks/useFormSubmissionAutoSaveState.js.map +1 -1
  85. package/dist/hooks/useFormValidation.d.ts +1 -1
  86. package/dist/hooks/useFormValidation.js +2 -1
  87. package/dist/hooks/useFormValidation.js.map +1 -1
  88. package/dist/hooks/useInjectPages.d.ts +2 -1
  89. package/dist/hooks/useInjectPages.js.map +1 -1
  90. package/dist/hooks/useIsFormElementReadOnly.d.ts +2 -0
  91. package/dist/hooks/useIsFormElementReadOnly.js +13 -0
  92. package/dist/hooks/useIsFormElementReadOnly.js.map +1 -0
  93. package/dist/hooks/useLookupNotification.d.ts +23 -2
  94. package/dist/hooks/useLookupNotification.js +2 -1
  95. package/dist/hooks/useLookupNotification.js.map +1 -1
  96. package/dist/hooks/useLookups.d.ts +3 -3
  97. package/dist/hooks/useLookups.js +7 -4
  98. package/dist/hooks/useLookups.js.map +1 -1
  99. package/dist/index.d.ts +2 -0
  100. package/dist/index.js +2 -0
  101. package/dist/index.js.map +1 -1
  102. package/dist/services/form-validation/extensions.d.ts +2 -1
  103. package/dist/services/form-validation/extensions.js +2 -1
  104. package/dist/services/form-validation/extensions.js.map +1 -1
  105. package/dist/services/form-validation/validateSubmission.d.ts +2 -1
  106. package/dist/services/form-validation/validateSubmission.js +14 -9
  107. package/dist/services/form-validation/validateSubmission.js.map +1 -1
  108. package/dist/types/form.d.ts +17 -3
  109. package/dist/types/form.js.map +1 -1
  110. package/dist/utils/read-only-form-elements.d.ts +37 -0
  111. package/dist/utils/read-only-form-elements.js +63 -0
  112. package/dist/utils/read-only-form-elements.js.map +1 -0
  113. package/package.json +4 -3
  114. package/dist/services/isElementReadOnlyForAudience.d.ts +0 -20
  115. package/dist/services/isElementReadOnlyForAudience.js +0 -46
  116. package/dist/services/isElementReadOnlyForAudience.js.map +0 -1
  117. package/dist/services/resolveFormElementReadOnly.d.ts +0 -10
  118. package/dist/services/resolveFormElementReadOnly.js +0 -17
  119. 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, 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,54 @@ 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);
475
584
  const handleSubmit = React.useCallback(async (event, continueWhilstAttachmentsAreUploading) => {
476
585
  event.preventDefault();
477
586
  // Approvers are not shown a submit button, so reaching here means the
@@ -482,39 +591,60 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
482
591
  return;
483
592
  setHasAttemptedSubmit(true);
484
593
  setIsPreparingToSubmit(true);
485
- const submissionData = await prepareSubmission(continueWhilstAttachmentsAreUploading);
486
- if (!submissionData) {
594
+ const submissionResult = await prepareSubmission(continueWhilstAttachmentsAreUploading);
595
+ if (!submissionResult) {
487
596
  setIsPreparingToSubmit(false);
488
597
  return;
489
598
  }
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
- });
599
+ switch (submissionResult.status) {
600
+ case AttachmentUploadStatus.ATTACHMENTS_UPLOAD_NON_BLOCKING: {
601
+ setPromptUploadingAttachments(true);
602
+ setIsPreparingToSubmit(false);
603
+ return;
604
+ }
605
+ case SubmissionPreparationStatus.OFFLINE_SUBMISSION: {
606
+ setPromptOfflineSubmissionAttempt(true);
607
+ setIsPreparingToSubmit(false);
608
+ return;
609
+ }
610
+ case SubmissionPreparationStatus.SUBMISSION_READY: {
611
+ allowNavigation();
612
+ const submissionData = submissionResult.submissionData;
613
+ // transplant injected options on the definition
614
+ const elementsWithInjectedOptions = injectOptionsAcrossAllElements({
615
+ contextElements: definition.elements,
616
+ elements: definition.elements,
617
+ submission: submissionData.submission,
618
+ taskContext: taskContextValue,
619
+ userProfile: userProfileForInjectables,
620
+ });
621
+ setIsPreparingToSubmit(false);
622
+ resetRecaptchas();
623
+ onSubmit({
624
+ definition: {
625
+ ...definition,
626
+ elements: elementsWithInjectedOptions,
627
+ },
628
+ submission: submissionData.submission,
629
+ recaptchas: submissionData.captchaTokens.map((token) => ({
630
+ token,
631
+ siteKey: captchaSiteKey,
632
+ })),
633
+ });
634
+ sendGoogleAnalyticsEvent('oneblink_form_submit', {
635
+ formId: definition.id,
636
+ formName: definition.name,
637
+ lastElementUpdated: getElementDisplayNameForAnalyticsEvent(lastElementUpdated),
638
+ durationUntilSubmission: getCurrentSubmissionDuration === null || getCurrentSubmissionDuration === void 0 ? void 0 : getCurrentSubmissionDuration(),
639
+ });
640
+ return;
641
+ }
642
+ default: {
643
+ const never = submissionResult;
644
+ setIsPreparingToSubmit(false);
645
+ return never;
646
+ }
647
+ }
518
648
  }, [
519
649
  disabled,
520
650
  isReadOnly,
@@ -541,9 +671,18 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
541
671
  if (!checkBsbAreValidating(submission)) {
542
672
  return;
543
673
  }
544
- if (!continueWhilstAttachmentsAreUploading &&
545
- !checkAttachmentsCanBeSubmitted(submission)) {
546
- return;
674
+ if (!continueWhilstAttachmentsAreUploading) {
675
+ const attachmentCheckResult = checkAttachmentsCanBeSubmitted(submission);
676
+ if (attachmentCheckResult ===
677
+ AttachmentUploadStatus.ATTACHMENTS_UPLOAD_NON_BLOCKING) {
678
+ setPromptUploadingAttachments(true);
679
+ return;
680
+ }
681
+ if (attachmentCheckResult ===
682
+ AttachmentUploadStatus.ATTACHMENTS_UPLOAD_BLOCKING) {
683
+ // The submitter cannot upload attachments in the background and must wait for them to upload before saving draft
684
+ return;
685
+ }
547
686
  }
548
687
  onSaveDraft({
549
688
  definition,
@@ -568,6 +707,13 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
568
707
  ]);
569
708
  const handleContinueWithAttachments = React.useCallback((e) => {
570
709
  setPromptUploadingAttachments(false);
710
+ if (hostAttemptPhase.status === 'prompt-attachments') {
711
+ setHostAttemptPhase({
712
+ status: 'preparing',
713
+ continueWithAttachments: true,
714
+ });
715
+ return;
716
+ }
571
717
  if (hasAttemptedSubmit) {
572
718
  handleSubmit(e, true);
573
719
  }
@@ -576,121 +722,129 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
576
722
  }
577
723
  }, [
578
724
  handleSubmit,
579
- setPromptUploadingAttachments,
725
+ hostAttemptPhase.status,
580
726
  hasAttemptedSubmit,
581
727
  handleSaveDraft,
582
728
  ]);
583
729
  const handleWaitForAttachments = React.useCallback(() => {
584
730
  setPromptUploadingAttachments(false);
585
- }, [setPromptUploadingAttachments]);
731
+ if (hostAttemptPhase.status === 'prompt-attachments') {
732
+ setHostAttemptPhase({ status: 'resolved', result: false });
733
+ }
734
+ }, [hostAttemptPhase.status]);
735
+ const handleCancelOfflineSubmission = React.useCallback(() => {
736
+ setPromptOfflineSubmissionAttempt(false);
737
+ if (hostAttemptPhase.status === 'prompt-offline') {
738
+ setHostAttemptPhase({ status: 'resolved', result: false });
739
+ }
740
+ }, [hostAttemptPhase.status]);
741
+ const handleRetryOfflineSubmission = React.useCallback((e) => {
742
+ setPromptOfflineSubmissionAttempt(false);
743
+ if (hostAttemptPhase.status === 'prompt-offline') {
744
+ setHostAttemptPhase({
745
+ status: 'preparing',
746
+ continueWithAttachments: hostAttemptPhase.continueWithAttachments,
747
+ });
748
+ }
749
+ else {
750
+ handleSubmit(e, false);
751
+ }
752
+ }, [
753
+ handleSubmit,
754
+ hostAttemptPhase,
755
+ ]);
586
756
  // #endregion
587
757
  //
588
758
  //
589
759
  //
590
760
  //
591
- // #region Lookups
592
- const { handlePagesLookupResult } = useLookups(definition.id, setFormSubmission);
593
- // #endregion
761
+ // #region Submission/Definition Changes
762
+ const markFormDirty = React.useCallback(() => {
763
+ setUnsavedChangesState((current) => ({
764
+ ...current,
765
+ isDirty: true,
766
+ }));
767
+ }, []);
594
768
  //
595
769
  //
770
+ // #region Lookups
771
+ const { handlePagesLookupResult } = useLookups(definition.id, setFormSubmission, markFormDirty);
772
+ // #endregion
596
773
  //
597
774
  //
598
- // #region Submission/Definition Changes
599
- const handleChange = React.useCallback((element, { value, executedLookups, deleteSection, sectionState }) => {
775
+ const handleChange = React.useCallback((element, { value, executedLookups, deleteSection, sectionState, isDerivedChange }) => {
600
776
  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') ||
777
+ // Disabled forms still need derived writers (calculation, summary, …)
778
+ // so those elements can recompute and display from the current model.
779
+ (disabled && !isDerivedChange) ||
607
780
  element.type === 'page') {
608
781
  return;
609
782
  }
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,
783
+ if (element.type === 'section') {
784
+ setFormSubmission((currentFormSubmission) => {
785
+ const currentSectionState = currentFormSubmission.sectionState || [];
786
+ const existingSectionIndex = currentSectionState.findIndex((section) => section.id === element.id);
787
+ const sectionIsInState = existingSectionIndex >= 0;
788
+ let newSectionState;
789
+ if (sectionIsInState && deleteSection) {
790
+ newSectionState = currentSectionState.filter((section) => section.id !== element.id);
791
+ }
792
+ else if (sectionIsInState) {
793
+ // Update state of the section
794
+ newSectionState = currentSectionState.map((section, index) => index === existingSectionIndex
795
+ ? {
796
+ ...section,
797
+ state: section.state === 'COLLAPSED' ? 'EXPANDED' : 'COLLAPSED',
798
+ }
799
+ : section);
800
+ }
801
+ else {
802
+ // Add the section to state
803
+ newSectionState = [
804
+ ...currentSectionState,
805
+ {
806
+ id: element.id,
807
+ state: element.isCollapsed ? 'EXPANDED' : 'COLLAPSED',
683
808
  },
684
- sectionState: sectionState
685
- ? typeof sectionState === 'function'
686
- ? sectionState(currentFormSubmission.sectionState)
687
- : sectionState
688
- : currentFormSubmission.sectionState,
689
- };
690
- });
691
- }
809
+ ];
810
+ }
811
+ return {
812
+ ...currentFormSubmission,
813
+ sectionState: newSectionState,
814
+ };
815
+ });
816
+ return;
817
+ }
818
+ if (!isDerivedChange) {
819
+ markFormDirty();
692
820
  }
693
- }, [disabled, setFormSubmission]);
821
+ setFormSubmission((currentFormSubmission) => {
822
+ var _a;
823
+ return {
824
+ ...currentFormSubmission,
825
+ submission: {
826
+ ...currentFormSubmission.submission,
827
+ [element.name]: typeof value === 'function'
828
+ ? value(currentFormSubmission.submission[element.name])
829
+ : value,
830
+ },
831
+ lastElementUpdated: isDerivedChange
832
+ ? currentFormSubmission.lastElementUpdated
833
+ : element,
834
+ executedLookups: {
835
+ ...currentFormSubmission.executedLookups,
836
+ [element.name]: typeof executedLookups === 'function'
837
+ ? executedLookups((_a = currentFormSubmission.executedLookups) === null || _a === void 0 ? void 0 : _a[element.name])
838
+ : executedLookups,
839
+ },
840
+ sectionState: sectionState
841
+ ? typeof sectionState === 'function'
842
+ ? sectionState(currentFormSubmission.sectionState)
843
+ : sectionState
844
+ : currentFormSubmission.sectionState,
845
+ };
846
+ }, { isDerivedChange });
847
+ }, [disabled, markFormDirty, setFormSubmission]);
694
848
  // #endregion
695
849
  //
696
850
  //
@@ -740,55 +894,58 @@ function OneBlinkFormBase({ googleMapsApiKey, abnLookupAuthenticationGuid, captc
740
894
  if (conditionalLogicError) {
741
895
  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
896
  }
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 }))] }) }) }) }) }) }) }) }));
897
+ 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', {
898
+ 'is-showing-pages': isShowingMultiplePages,
899
+ }), 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', {
900
+ 'is-active': isStepsHeaderActive,
901
+ }), children: [_jsxs(Clickable, { className: clsx('ob-steps-navigation__header', {
902
+ 'is-active': isStepsHeaderActive,
903
+ }), 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', {
904
+ 'is-active': isStepsHeaderActive,
905
+ }), children: _jsx("div", { className: "steps is-small is-horizontal-tablet cypress-steps", children: visiblePages.map((page, index) => {
906
+ const hasErrors = checkDisplayPageError(page);
907
+ 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
908
+ ? 'step'
909
+ : undefined, className: clsx('step-item cypress-step-item', {
910
+ 'is-active': currentPage.id === page.id,
911
+ 'is-completed': currentPageIndex > index,
912
+ 'is-error': hasErrors,
913
+ }), onClick: (e) => {
914
+ e.stopPropagation();
915
+ if (page.id !== currentPage.id) {
916
+ setPageId(page.id);
917
+ }
918
+ }, children: [_jsx("div", { className: "step-marker step-marker-error ob-step-marker cypress-step-marker",
919
+ // @ts-expect-error ???
920
+ 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));
921
+ }) }) })] })), _jsx("div", { className: clsx('ob-steps-navigation__background', {
922
+ 'is-active': isStepsHeaderActive,
923
+ }), onClick: toggleStepsNavigation }), _jsxs("div", { className: "steps", children: [_jsx("div", { className: clsx('steps-content', {
924
+ 'is-single-step': !isShowingMultiplePages,
925
+ }), 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 ===
926
+ currentPage.id, formId: definition.id, formElementsConditionallyShown: formElementsConditionallyShown, formElementsValidation: formElementsValidation, displayValidationMessages: hasAttemptedSubmit ||
927
+ 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', {
928
+ 'is-active': currentPage.id === page.id,
929
+ 'is-completed': currentPageIndex > index,
930
+ 'has-background-danger': currentPage.id !== page.id &&
931
+ checkDisplayPageError(page),
932
+ }) }, 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
933
+ ? ''
934
+ : '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 }, {
935
+ 'ob-button-submit-is-disabled': submissionConditionallyEnabled,
936
+ }), disabled: isPreview ||
937
+ disabled ||
938
+ isPreparingToSubmit ||
939
+ !submissionConditionallyEnabled, children: _jsx(CustomisableButtonInner, { label: isInfoPage
940
+ ? 'Done'
941
+ : ((_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 &&
942
+ (editableFormElementIds !== undefined ||
943
+ !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) ||
944
+ '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) ||
945
+ '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) ||
946
+ '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) ||
947
+ '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 &&
948
+ 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
949
  }
793
950
  export default React.memo(OneBlinkFormBase);
794
951
  //# sourceMappingURL=OneBlinkFormBase.js.map