@signiphi/pdf-signer 0.2.0-beta.2 → 0.2.0-beta.21

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 (197) hide show
  1. package/assets/viewer.html +1 -5
  2. package/dist/components/index.js +2746 -901
  3. package/dist/components/index.js.map +1 -1
  4. package/dist/components/index.mjs +2513 -669
  5. package/dist/components/index.mjs.map +1 -1
  6. package/dist/core/index.js +420 -20
  7. package/dist/core/index.js.map +1 -1
  8. package/dist/core/index.mjs +420 -20
  9. package/dist/core/index.mjs.map +1 -1
  10. package/dist/hooks/index.js +506 -211
  11. package/dist/hooks/index.js.map +1 -1
  12. package/dist/hooks/index.mjs +507 -212
  13. package/dist/hooks/index.mjs.map +1 -1
  14. package/dist/index.css +214 -191
  15. package/dist/index.css.map +1 -1
  16. package/dist/index.js +3019 -893
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +2762 -653
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/styles/index.css +202 -172
  21. package/dist/types/index.js.map +1 -1
  22. package/dist/types/index.mjs.map +1 -1
  23. package/dist/utils/index.js +792 -147
  24. package/dist/utils/index.js.map +1 -1
  25. package/dist/utils/index.mjs +777 -148
  26. package/dist/utils/index.mjs.map +1 -1
  27. package/package.json +2 -2
  28. package/scripts/copy-utils.js +14 -3
  29. package/src/styles/index.css +33 -3
  30. package/dist/__tests__/helpers/fixtures.d.ts +0 -43
  31. package/dist/__tests__/helpers/fixtures.d.ts.map +0 -1
  32. package/dist/__tests__/helpers/mocks.d.ts +0 -333
  33. package/dist/__tests__/helpers/mocks.d.ts.map +0 -1
  34. package/dist/__tests__/setup.d.ts +0 -6
  35. package/dist/__tests__/setup.d.ts.map +0 -1
  36. package/dist/components/AcknowledgementModal.d.ts +0 -21
  37. package/dist/components/AcknowledgementModal.d.ts.map +0 -1
  38. package/dist/components/AcknowledgementsSidebar.d.ts +0 -22
  39. package/dist/components/AcknowledgementsSidebar.d.ts.map +0 -1
  40. package/dist/components/AttachmentUpload.d.ts +0 -17
  41. package/dist/components/AttachmentUpload.d.ts.map +0 -1
  42. package/dist/components/EditableFieldsPanel.d.ts +0 -30
  43. package/dist/components/EditableFieldsPanel.d.ts.map +0 -1
  44. package/dist/components/ErrorBoundary.d.ts +0 -67
  45. package/dist/components/ErrorBoundary.d.ts.map +0 -1
  46. package/dist/components/FormFieldsView.d.ts +0 -46
  47. package/dist/components/FormFieldsView.d.ts.map +0 -1
  48. package/dist/components/InitialsModal.d.ts +0 -16
  49. package/dist/components/InitialsModal.d.ts.map +0 -1
  50. package/dist/components/PdfViewerStyled.d.ts +0 -16
  51. package/dist/components/PdfViewerStyled.d.ts.map +0 -1
  52. package/dist/components/PoweredBySigniphi.d.ts +0 -11
  53. package/dist/components/PoweredBySigniphi.d.ts.map +0 -1
  54. package/dist/components/RequiredFieldNavigation.d.ts +0 -18
  55. package/dist/components/RequiredFieldNavigation.d.ts.map +0 -1
  56. package/dist/components/SignatureCanvas.d.ts +0 -12
  57. package/dist/components/SignatureCanvas.d.ts.map +0 -1
  58. package/dist/components/SignatureInitialsBox.d.ts +0 -25
  59. package/dist/components/SignatureInitialsBox.d.ts.map +0 -1
  60. package/dist/components/SignatureModal.d.ts +0 -21
  61. package/dist/components/SignatureModal.d.ts.map +0 -1
  62. package/dist/components/SigningInstructions.d.ts +0 -12
  63. package/dist/components/SigningInstructions.d.ts.map +0 -1
  64. package/dist/components/SubmissionForm.d.ts +0 -52
  65. package/dist/components/SubmissionForm.d.ts.map +0 -1
  66. package/dist/components/UnacknowledgedFieldsModal.d.ts +0 -23
  67. package/dist/components/UnacknowledgedFieldsModal.d.ts.map +0 -1
  68. package/dist/components/ViewToggleToolbar.d.ts +0 -38
  69. package/dist/components/ViewToggleToolbar.d.ts.map +0 -1
  70. package/dist/components/form-fields/CheckboxRenderer.d.ts +0 -10
  71. package/dist/components/form-fields/CheckboxRenderer.d.ts.map +0 -1
  72. package/dist/components/form-fields/DateFieldRenderer.d.ts +0 -14
  73. package/dist/components/form-fields/DateFieldRenderer.d.ts.map +0 -1
  74. package/dist/components/form-fields/DropdownRenderer.d.ts +0 -14
  75. package/dist/components/form-fields/DropdownRenderer.d.ts.map +0 -1
  76. package/dist/components/form-fields/FormFieldRenderer.d.ts +0 -22
  77. package/dist/components/form-fields/FormFieldRenderer.d.ts.map +0 -1
  78. package/dist/components/form-fields/InitialsFieldRenderer.d.ts +0 -16
  79. package/dist/components/form-fields/InitialsFieldRenderer.d.ts.map +0 -1
  80. package/dist/components/form-fields/RadioGroupRenderer.d.ts +0 -10
  81. package/dist/components/form-fields/RadioGroupRenderer.d.ts.map +0 -1
  82. package/dist/components/form-fields/SignatureFieldRenderer.d.ts +0 -16
  83. package/dist/components/form-fields/SignatureFieldRenderer.d.ts.map +0 -1
  84. package/dist/components/form-fields/TextFieldRenderer.d.ts +0 -14
  85. package/dist/components/form-fields/TextFieldRenderer.d.ts.map +0 -1
  86. package/dist/components/form-fields/TextLabelRenderer.d.ts +0 -14
  87. package/dist/components/form-fields/TextLabelRenderer.d.ts.map +0 -1
  88. package/dist/components/form-fields/index.d.ts +0 -14
  89. package/dist/components/form-fields/index.d.ts.map +0 -1
  90. package/dist/components/index.d.ts +0 -17
  91. package/dist/components/index.d.ts.map +0 -1
  92. package/dist/core/PdfViewerCore.d.ts +0 -19
  93. package/dist/core/PdfViewerCore.d.ts.map +0 -1
  94. package/dist/core/SignatureCaptureCore.d.ts +0 -37
  95. package/dist/core/SignatureCaptureCore.d.ts.map +0 -1
  96. package/dist/core/index.d.ts +0 -3
  97. package/dist/core/index.d.ts.map +0 -1
  98. package/dist/hooks/index.d.ts +0 -9
  99. package/dist/hooks/index.d.ts.map +0 -1
  100. package/dist/hooks/useAcknowledgements.d.ts +0 -50
  101. package/dist/hooks/useAcknowledgements.d.ts.map +0 -1
  102. package/dist/hooks/useAttachments.d.ts +0 -25
  103. package/dist/hooks/useAttachments.d.ts.map +0 -1
  104. package/dist/hooks/useFieldFiltering.d.ts +0 -29
  105. package/dist/hooks/useFieldFiltering.d.ts.map +0 -1
  106. package/dist/hooks/useFormFields.d.ts +0 -23
  107. package/dist/hooks/useFormFields.d.ts.map +0 -1
  108. package/dist/hooks/useMultiSignerContext.d.ts +0 -25
  109. package/dist/hooks/useMultiSignerContext.d.ts.map +0 -1
  110. package/dist/hooks/usePdfViewer.d.ts +0 -52
  111. package/dist/hooks/usePdfViewer.d.ts.map +0 -1
  112. package/dist/hooks/useRequiredFieldNavigation.d.ts +0 -16
  113. package/dist/hooks/useRequiredFieldNavigation.d.ts.map +0 -1
  114. package/dist/hooks/useSignatureCapture.d.ts +0 -17
  115. package/dist/hooks/useSignatureCapture.d.ts.map +0 -1
  116. package/dist/hooks/useSignatures.d.ts +0 -29
  117. package/dist/hooks/useSignatures.d.ts.map +0 -1
  118. package/dist/index.d.ts +0 -17
  119. package/dist/index.d.ts.map +0 -1
  120. package/dist/integrations/index.d.ts +0 -6
  121. package/dist/integrations/index.d.ts.map +0 -1
  122. package/dist/integrations/next-config.d.ts +0 -46
  123. package/dist/integrations/next-config.d.ts.map +0 -1
  124. package/dist/integrations/vite-plugin.d.ts +0 -48
  125. package/dist/integrations/vite-plugin.d.ts.map +0 -1
  126. package/dist/lib/index.d.ts +0 -3
  127. package/dist/lib/index.d.ts.map +0 -1
  128. package/dist/lib/ui/accordion.d.ts +0 -8
  129. package/dist/lib/ui/accordion.d.ts.map +0 -1
  130. package/dist/lib/ui/alert.d.ts +0 -9
  131. package/dist/lib/ui/alert.d.ts.map +0 -1
  132. package/dist/lib/ui/button.d.ts +0 -12
  133. package/dist/lib/ui/button.d.ts.map +0 -1
  134. package/dist/lib/ui/calendar.d.ts +0 -10
  135. package/dist/lib/ui/calendar.d.ts.map +0 -1
  136. package/dist/lib/ui/card.d.ts +0 -9
  137. package/dist/lib/ui/card.d.ts.map +0 -1
  138. package/dist/lib/ui/checkbox.d.ts +0 -5
  139. package/dist/lib/ui/checkbox.d.ts.map +0 -1
  140. package/dist/lib/ui/dialog.d.ts +0 -20
  141. package/dist/lib/ui/dialog.d.ts.map +0 -1
  142. package/dist/lib/ui/index.d.ts +0 -13
  143. package/dist/lib/ui/index.d.ts.map +0 -1
  144. package/dist/lib/ui/input.d.ts +0 -6
  145. package/dist/lib/ui/input.d.ts.map +0 -1
  146. package/dist/lib/ui/label.d.ts +0 -6
  147. package/dist/lib/ui/label.d.ts.map +0 -1
  148. package/dist/lib/ui/popover.d.ts +0 -7
  149. package/dist/lib/ui/popover.d.ts.map +0 -1
  150. package/dist/lib/ui/radio-group.d.ts +0 -6
  151. package/dist/lib/ui/radio-group.d.ts.map +0 -1
  152. package/dist/lib/ui/select.d.ts +0 -14
  153. package/dist/lib/ui/select.d.ts.map +0 -1
  154. package/dist/lib/utils.d.ts +0 -7
  155. package/dist/lib/utils.d.ts.map +0 -1
  156. package/dist/types/index.d.ts +0 -278
  157. package/dist/types/index.d.ts.map +0 -1
  158. package/dist/utils/attachment-validators.d.ts +0 -118
  159. package/dist/utils/attachment-validators.d.ts.map +0 -1
  160. package/dist/utils/audit-trail.d.ts +0 -27
  161. package/dist/utils/audit-trail.d.ts.map +0 -1
  162. package/dist/utils/date-validation.d.ts +0 -30
  163. package/dist/utils/date-validation.d.ts.map +0 -1
  164. package/dist/utils/errors.d.ts +0 -106
  165. package/dist/utils/errors.d.ts.map +0 -1
  166. package/dist/utils/field-extraction.d.ts +0 -36
  167. package/dist/utils/field-extraction.d.ts.map +0 -1
  168. package/dist/utils/field-visibility.d.ts +0 -104
  169. package/dist/utils/field-visibility.d.ts.map +0 -1
  170. package/dist/utils/index.d.ts +0 -18
  171. package/dist/utils/index.d.ts.map +0 -1
  172. package/dist/utils/logger.d.ts +0 -16
  173. package/dist/utils/logger.d.ts.map +0 -1
  174. package/dist/utils/pdf-field-type-helpers.d.ts +0 -78
  175. package/dist/utils/pdf-field-type-helpers.d.ts.map +0 -1
  176. package/dist/utils/pdf-helpers.d.ts +0 -38
  177. package/dist/utils/pdf-helpers.d.ts.map +0 -1
  178. package/dist/utils/pdf-lib-loader.d.ts +0 -45
  179. package/dist/utils/pdf-lib-loader.d.ts.map +0 -1
  180. package/dist/utils/pdf-manipulation.d.ts +0 -93
  181. package/dist/utils/pdf-manipulation.d.ts.map +0 -1
  182. package/dist/utils/pdf-metadata.d.ts +0 -41
  183. package/dist/utils/pdf-metadata.d.ts.map +0 -1
  184. package/dist/utils/pdf-validators.d.ts +0 -149
  185. package/dist/utils/pdf-validators.d.ts.map +0 -1
  186. package/dist/utils/pdf-viewer-filter.d.ts +0 -35
  187. package/dist/utils/pdf-viewer-filter.d.ts.map +0 -1
  188. package/dist/utils/pdf-widget-helpers.d.ts +0 -98
  189. package/dist/utils/pdf-widget-helpers.d.ts.map +0 -1
  190. package/dist/utils/pdfjs-config.d.ts +0 -56
  191. package/dist/utils/pdfjs-config.d.ts.map +0 -1
  192. package/dist/utils/pdfjs-version-check.d.ts +0 -28
  193. package/dist/utils/pdfjs-version-check.d.ts.map +0 -1
  194. package/dist/utils/performance-monitor.d.ts +0 -172
  195. package/dist/utils/performance-monitor.d.ts.map +0 -1
  196. package/dist/utils/tracking.d.ts +0 -89
  197. package/dist/utils/tracking.d.ts.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as pdfjsLib from 'pdfjs-dist';
2
- import * as React8 from 'react';
2
+ import * as React9 from 'react';
3
3
  import { forwardRef, useRef, useState, useCallback, useEffect, useImperativeHandle, useMemo } from 'react';
4
4
  import { format, parseISO, isValid, parse } from 'date-fns';
5
5
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
@@ -9,7 +9,7 @@ import { cva } from 'class-variance-authority';
9
9
  import { clsx } from 'clsx';
10
10
  import { twMerge } from 'tailwind-merge';
11
11
  import * as DialogPrimitive from '@radix-ui/react-dialog';
12
- import { X, ChevronDown, ChevronUp, Check, Circle, AlertCircle, Lock, CheckCircle, Pen, Calendar as Calendar$1, ChevronLeft, ChevronRight, Loader2, FileText, ListChecks, ZoomOut, ZoomIn, Upload, Star, Image, File, CheckCircle2, Clock } from 'lucide-react';
12
+ import { X, ChevronDown, ChevronUp, Check, Circle, AlertCircle, Lock, CheckCircle, Pen, Calendar as Calendar$1, ChevronLeft, ChevronRight, Loader2, FileText, ListChecks, ZoomOut, ZoomIn, Upload, Image, File, CheckCircle2, Clock } from 'lucide-react';
13
13
  import * as LabelPrimitive from '@radix-ui/react-label';
14
14
  import { DayPicker, useDayPicker } from 'react-day-picker';
15
15
  import 'react-day-picker/dist/style.css';
@@ -17,6 +17,7 @@ import * as SelectPrimitive from '@radix-ui/react-select';
17
17
  import * as PopoverPrimitive from '@radix-ui/react-popover';
18
18
  import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
19
19
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
20
+ import { createPortal } from 'react-dom';
20
21
 
21
22
  var __defProp = Object.defineProperty;
22
23
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -114,7 +115,7 @@ var SubmissionStatus = /* @__PURE__ */ ((SubmissionStatus2) => {
114
115
  async function checkPdfJsVersion(viewerBasePath = "/pdfjs") {
115
116
  const workerVersion = pdfjsLib.version;
116
117
  try {
117
- const versionUrl = `${viewerBasePath}/.version`;
118
+ const versionUrl = `${viewerBasePath}/version.txt`;
118
119
  const response = await fetch(versionUrl);
119
120
  if (!response.ok) {
120
121
  return {
@@ -223,6 +224,7 @@ function validatePdfBytes(pdfBytes) {
223
224
  }
224
225
  function isAutoGeneratedLabel(label) {
225
226
  if (!label || !label.trim()) return true;
227
+ const trimmed = label.trim();
226
228
  const autoLabels = [
227
229
  "Signature",
228
230
  "Initials",
@@ -233,7 +235,65 @@ function isAutoGeneratedLabel(label) {
233
235
  "Option",
234
236
  "Radio"
235
237
  ];
236
- return autoLabels.includes(label.trim());
238
+ if (autoLabels.includes(trimmed)) return true;
239
+ if (/^\d{10,}$/.test(trimmed)) return true;
240
+ const typeTimestampPattern = new RegExp(
241
+ `^(${autoLabels.join("|")})[\\s_-]?\\d{10,}$`,
242
+ "i"
243
+ );
244
+ if (typeTimestampPattern.test(trimmed)) return true;
245
+ const typeNumberPattern = new RegExp(
246
+ `^(${autoLabels.join("|")})[\\s_-]?\\d{1,3}$`,
247
+ "i"
248
+ );
249
+ if (typeNumberPattern.test(trimmed)) return true;
250
+ const typeWordsJoined = autoLabels.join("|");
251
+ const corruptedPattern = new RegExp(
252
+ `^(${typeWordsJoined})[\\s_-]?\\d{10,}[\\s_-]?(${typeWordsJoined})+`,
253
+ "i"
254
+ );
255
+ if (corruptedPattern.test(trimmed)) return true;
256
+ const repeatedTypePattern = new RegExp(
257
+ `^((${typeWordsJoined})[\\s_-]+)+(${typeWordsJoined})$`,
258
+ "i"
259
+ );
260
+ if (repeatedTypePattern.test(trimmed)) return true;
261
+ return false;
262
+ }
263
+ function hasDrawableLabel(field) {
264
+ if (!field.label || !field.label.trim()) return false;
265
+ if (field.isLabelAutoGenerated) return false;
266
+ if (isAutoGeneratedLabel(field.label)) return false;
267
+ return true;
268
+ }
269
+ function getFieldDisplayName(field) {
270
+ if (field.label && !isAutoGeneratedLabel(field.label)) {
271
+ return field.label;
272
+ }
273
+ const typeNames = {
274
+ "text": "Text field",
275
+ "signature": "Signature",
276
+ "initials": "Initials",
277
+ "date": "Date field",
278
+ "checkbox": "Checkbox",
279
+ "dropdown": "Dropdown",
280
+ "radio": "Radio selection",
281
+ "radiogroup": "Radio selection",
282
+ "text_label": "Text label"
283
+ };
284
+ if (field.type) {
285
+ const typeName = typeNames[field.type.toLowerCase()];
286
+ if (typeName) {
287
+ return typeName;
288
+ }
289
+ }
290
+ if (field.name) {
291
+ let cleaned = field.name.replace(/_?(signature|initials|date)$/i, "").replace(/[_-]\d{10,}$/, "").replace(/[_-]/g, " ").trim();
292
+ if (cleaned && !isAutoGeneratedLabel(cleaned)) {
293
+ return cleaned.replace(/\b\w/g, (l) => l.toUpperCase());
294
+ }
295
+ }
296
+ return "This field";
237
297
  }
238
298
  function validateFieldValues(values) {
239
299
  const errors = [];
@@ -469,6 +529,114 @@ function getSigniphiMetadata(pdfDoc) {
469
529
  }
470
530
  }
471
531
 
532
+ // src/utils/font-loader.ts
533
+ var SIGNATURE_FONTS = [
534
+ { name: "dancing-script", family: "Dancing Script", label: "Elegant" },
535
+ { name: "great-vibes", family: "Great Vibes", label: "Formal" },
536
+ { name: "caveat", family: "Caveat", label: "Casual" },
537
+ { name: "homemade-apple", family: "Homemade Apple", label: "Natural" },
538
+ { name: "sacramento", family: "Sacramento", label: "Flowing" }
539
+ ];
540
+ var DEFAULT_SIGNATURE_FONT = {
541
+ name: "dancing-script",
542
+ family: "Dancing Script",
543
+ label: "Elegant"
544
+ };
545
+ var GOOGLE_FONTS_URL = "https://fonts.googleapis.com/css2?family=Dancing+Script&family=Great+Vibes&family=Caveat&family=Homemade+Apple&family=Sacramento&display=swap";
546
+ var fontsLoaded = false;
547
+ var fontsLoadingPromise = null;
548
+ async function loadSignatureFonts() {
549
+ if (fontsLoaded) {
550
+ return;
551
+ }
552
+ if (fontsLoadingPromise) {
553
+ return fontsLoadingPromise;
554
+ }
555
+ fontsLoadingPromise = new Promise((resolve, reject) => {
556
+ const existingLink = document.querySelector(
557
+ `link[href="${GOOGLE_FONTS_URL}"]`
558
+ );
559
+ if (existingLink) {
560
+ fontsLoaded = true;
561
+ resolve();
562
+ return;
563
+ }
564
+ const link = document.createElement("link");
565
+ link.href = GOOGLE_FONTS_URL;
566
+ link.rel = "stylesheet";
567
+ link.onload = () => {
568
+ fontsLoaded = true;
569
+ resolve();
570
+ };
571
+ link.onerror = () => {
572
+ fontsLoadingPromise = null;
573
+ reject(new Error("Failed to load signature fonts from Google Fonts"));
574
+ };
575
+ document.head.appendChild(link);
576
+ });
577
+ return fontsLoadingPromise;
578
+ }
579
+ function isFontLoaded(fontFamily) {
580
+ if (typeof document === "undefined") {
581
+ return false;
582
+ }
583
+ return document.fonts.check(`48px "${fontFamily}"`);
584
+ }
585
+ async function waitForFont(fontFamily) {
586
+ if (typeof document === "undefined") {
587
+ return false;
588
+ }
589
+ try {
590
+ await document.fonts.load(`48px "${fontFamily}"`);
591
+ return true;
592
+ } catch {
593
+ return false;
594
+ }
595
+ }
596
+ function generateSignatureFromText(options) {
597
+ const {
598
+ text,
599
+ fontFamily,
600
+ width = 450,
601
+ height = 200,
602
+ fontSize = 48,
603
+ color = "#000000",
604
+ backgroundColor = null
605
+ } = options;
606
+ const canvas = document.createElement("canvas");
607
+ canvas.width = width;
608
+ canvas.height = height;
609
+ const ctx = canvas.getContext("2d");
610
+ if (!ctx) {
611
+ throw new Error("Failed to get canvas 2d context");
612
+ }
613
+ if (backgroundColor) {
614
+ ctx.fillStyle = backgroundColor;
615
+ ctx.fillRect(0, 0, width, height);
616
+ } else {
617
+ ctx.clearRect(0, 0, width, height);
618
+ }
619
+ ctx.font = `${fontSize}px "${fontFamily}"`;
620
+ ctx.fillStyle = color;
621
+ ctx.textAlign = "center";
622
+ ctx.textBaseline = "middle";
623
+ let adjustedFontSize = fontSize;
624
+ let textMetrics = ctx.measureText(text);
625
+ const maxWidth = width * 0.9;
626
+ while (textMetrics.width > maxWidth && adjustedFontSize > 16) {
627
+ adjustedFontSize -= 2;
628
+ ctx.font = `${adjustedFontSize}px "${fontFamily}"`;
629
+ textMetrics = ctx.measureText(text);
630
+ }
631
+ ctx.fillText(text, width / 2, height / 2);
632
+ return canvas.toDataURL("image/png");
633
+ }
634
+ async function generateSignatureFromTextAsync(options) {
635
+ await loadSignatureFonts();
636
+ await waitForFont(options.fontFamily);
637
+ return generateSignatureFromText(options);
638
+ }
639
+
472
640
  // src/utils/pdf-lib-loader.ts
473
641
  var pdfLibPromise = null;
474
642
  async function loadPdfLib() {
@@ -483,18 +651,15 @@ function isFieldVisibleToSigner(field, multiSignerContext) {
483
651
  if (!multiSignerContext.isMultiSigner) {
484
652
  return true;
485
653
  }
486
- const { currentSignerEmail, isPrimarySigner, isFinalSigner } = multiSignerContext;
654
+ const { currentSignerEmail } = multiSignerContext;
487
655
  if (!field.assignedSignerEmail) {
488
- return isFinalSigner;
656
+ return true;
489
657
  }
490
658
  if (field.assignedSignerEmail === currentSignerEmail) {
491
659
  return true;
492
660
  }
493
- if (field.assignedSignerEmail.includes("recipients")) {
494
- return isPrimarySigner;
495
- }
496
- if (field.assignedSignerEmail.includes("signers")) {
497
- return isFinalSigner;
661
+ if (field.assignedSignerEmail === "to-recipients" || field.assignedSignerEmail === "additional-signers") {
662
+ return true;
498
663
  }
499
664
  return false;
500
665
  }
@@ -508,18 +673,15 @@ function shouldFlattenField(field, multiSignerContext) {
508
673
  if (!multiSignerContext.isMultiSigner) {
509
674
  return true;
510
675
  }
511
- const { currentSignerEmail, isPrimarySigner, isFinalSigner } = multiSignerContext;
676
+ const { currentSignerEmail } = multiSignerContext;
512
677
  if (!field.assignedSignerEmail) {
513
- return isFinalSigner;
678
+ return true;
514
679
  }
515
680
  if (field.assignedSignerEmail === currentSignerEmail) {
516
681
  return true;
517
682
  }
518
- if (field.assignedSignerEmail.includes("recipients")) {
519
- return isPrimarySigner;
520
- }
521
- if (field.assignedSignerEmail.includes("signers")) {
522
- return isFinalSigner;
683
+ if (field.assignedSignerEmail === "to-recipients" || field.assignedSignerEmail === "additional-signers") {
684
+ return true;
523
685
  }
524
686
  return false;
525
687
  }
@@ -559,18 +721,23 @@ function findPageIndexWithFallback(pages, pageRef) {
559
721
 
560
722
  // src/utils/pdf-field-type-helpers.ts
561
723
  function detectFieldType(field) {
562
- const typeName = field.constructor.name;
563
- if (typeName === "PDFTextField") {
564
- return "text";
565
- } else if (typeName === "PDFCheckBox") {
724
+ const f = field;
725
+ if (typeof f.check === "function" && typeof f.uncheck === "function") {
566
726
  return "checkbox";
567
- } else if (typeName === "PDFDropdown") {
727
+ }
728
+ if (typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function") {
729
+ return "radiogroup";
730
+ }
731
+ if (typeof f.select === "function" && typeof f.getOptions === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function")) {
568
732
  return "dropdown";
569
- } else if (typeName === "PDFOptionList") {
733
+ }
734
+ if (typeof f.getOptions === "function" && typeof f.setOptions === "function" && typeof f.select !== "function") {
570
735
  return "optionlist";
571
- } else if (typeName === "PDFRadioGroup") {
572
- return "radiogroup";
573
- } else if (typeName === "PDFSignature") {
736
+ }
737
+ if (typeof f.getText === "function" && typeof f.setText === "function") {
738
+ return "text";
739
+ }
740
+ if (typeof f.getText !== "function" && typeof f.check !== "function" && typeof f.select !== "function") {
574
741
  return "signature";
575
742
  }
576
743
  return "unknown";
@@ -579,13 +746,20 @@ function extractFieldValue(field, fieldType) {
579
746
  try {
580
747
  switch (fieldType) {
581
748
  case "text":
749
+ case "date":
582
750
  return field.getText?.() || "";
583
751
  case "checkbox":
584
752
  return field.isChecked?.() ? "true" : "false";
585
753
  case "dropdown":
586
- case "optionlist":
754
+ case "optionlist": {
755
+ const selected = field.getSelected?.();
756
+ return Array.isArray(selected) ? selected[0] || "" : String(selected || "");
757
+ }
587
758
  case "radiogroup":
588
- return field.getSelected?.()?.[0] || "";
759
+ case "radio": {
760
+ const radioSelected = field.getSelected?.();
761
+ return radioSelected ? String(radioSelected) : "";
762
+ }
589
763
  default:
590
764
  return "";
591
765
  }
@@ -889,12 +1063,16 @@ async function validatePdfFormFields(pdfBytes, fieldValues, signatures, extracte
889
1063
  const errors = [];
890
1064
  for (const field of pdfFormFields) {
891
1065
  if (field.required) {
1066
+ const extractedField = extractedFields?.find((f) => f.name === field.name);
892
1067
  if (multiSignerContext?.isMultiSigner && extractedFields) {
893
- const extractedField = extractedFields.find((f) => f.name === field.name);
894
1068
  if (!extractedField) {
895
1069
  continue;
896
1070
  }
897
1071
  }
1072
+ const logicalType = extractedField?.type || field.type;
1073
+ if (logicalType === "date" || logicalType === "signature" || logicalType === "initials") {
1074
+ continue;
1075
+ }
898
1076
  let hasValue = false;
899
1077
  const fieldValue = fieldValues[field.name];
900
1078
  const signatureValue = signatures[field.name];
@@ -904,16 +1082,17 @@ async function validatePdfFormFields(pdfBytes, fieldValues, signatures, extracte
904
1082
  hasValue = !!(signatureValue || fieldValue || mainSignature);
905
1083
  } else if (field.name.includes("initials")) {
906
1084
  hasValue = !!(signatureValue || fieldValue || mainInitials);
1085
+ } else if (field.type === "checkbox" || logicalType === "checkbox") {
1086
+ hasValue = fieldValue === "true" || field.value === "true";
907
1087
  } else {
908
1088
  hasValue = !!(fieldValue || field.value);
909
1089
  }
910
1090
  if (!hasValue) {
911
- let friendlyName = field.name.replace(/[_-]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
912
- if (field.name.includes("signature")) {
913
- friendlyName = "Signature";
914
- } else if (field.name.includes("initials")) {
915
- friendlyName = "Initials";
916
- }
1091
+ const friendlyName = getFieldDisplayName({
1092
+ label: extractedField?.label,
1093
+ name: field.name,
1094
+ type: logicalType
1095
+ });
917
1096
  errors.push(`${friendlyName} is required`);
918
1097
  }
919
1098
  }
@@ -943,12 +1122,10 @@ async function validateCurrentPdfState(pdfBytes, signatures, formFieldValues = {
943
1122
  hasValue = currentValue.trim() !== "";
944
1123
  }
945
1124
  if (!hasValue) {
946
- let friendlyName = field.name.replace(/[_-]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
947
- if (field.name.includes("signature")) {
948
- friendlyName = "Signature";
949
- } else if (field.name.includes("initials")) {
950
- friendlyName = "Initials";
951
- }
1125
+ const friendlyName = getFieldDisplayName({
1126
+ name: field.name,
1127
+ type: field.type
1128
+ });
952
1129
  errors.push(`${friendlyName} is required`);
953
1130
  }
954
1131
  }
@@ -959,7 +1136,80 @@ async function validateCurrentPdfState(pdfBytes, signatures, formFieldValues = {
959
1136
  throw error;
960
1137
  }
961
1138
  }
962
- async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {}, currentSignerEmail, extractedFormFields, metadata, auditTrail, multiSignerContext) {
1139
+ function extractFieldFontSize(fieldName, field, extractedFormFields) {
1140
+ const DEFAULT_FONT_SIZE = 10;
1141
+ const MIN_FONT_SIZE = 8;
1142
+ const MAX_FONT_SIZE = 72;
1143
+ const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
1144
+ if (fieldInfo?.fontSize && fieldInfo.fontSize >= MIN_FONT_SIZE && fieldInfo.fontSize <= MAX_FONT_SIZE) {
1145
+ return fieldInfo.fontSize;
1146
+ }
1147
+ const daFontSize = extractFromDAField(field);
1148
+ if (daFontSize !== null) {
1149
+ return daFontSize;
1150
+ }
1151
+ const tuFontSize = extractFromTUField(field);
1152
+ if (tuFontSize !== null) {
1153
+ return tuFontSize;
1154
+ }
1155
+ return DEFAULT_FONT_SIZE;
1156
+ }
1157
+ function extractFromDAField(field) {
1158
+ try {
1159
+ const fieldWithAcro = field;
1160
+ const dict = fieldWithAcro.acroField?.dict;
1161
+ if (!dict) return null;
1162
+ const daKey = dict.context?.obj?.("DA") || "DA";
1163
+ const daEntry = dict.lookup?.(daKey) || dict.get?.(daKey);
1164
+ if (!daEntry) return null;
1165
+ let daValue = "";
1166
+ if (typeof daEntry.decodeText === "function") {
1167
+ daValue = daEntry.decodeText();
1168
+ } else if (typeof daEntry.asString === "function") {
1169
+ daValue = daEntry.asString();
1170
+ } else {
1171
+ daValue = String(daEntry);
1172
+ }
1173
+ const daMatch = daValue.match(/\s+(\d+)\s+Tf/i);
1174
+ if (daMatch?.[1]) {
1175
+ const fontSize = parseInt(daMatch[1], 10);
1176
+ if (fontSize >= 8 && fontSize <= 72) {
1177
+ return fontSize;
1178
+ }
1179
+ }
1180
+ } catch {
1181
+ }
1182
+ return null;
1183
+ }
1184
+ function extractFromTUField(field) {
1185
+ try {
1186
+ const fieldWithAcro = field;
1187
+ const dict = fieldWithAcro.acroField?.dict;
1188
+ if (!dict) return null;
1189
+ const tuKey = dict.context?.obj?.("TU") || "TU";
1190
+ const tuEntry = dict.lookup?.(tuKey) || dict.get?.(tuKey);
1191
+ if (!tuEntry) return null;
1192
+ let tuValue = "";
1193
+ if (typeof tuEntry.decodeText === "function") {
1194
+ tuValue = tuEntry.decodeText();
1195
+ } else if (typeof tuEntry.asString === "function") {
1196
+ tuValue = tuEntry.asString();
1197
+ } else {
1198
+ tuValue = String(tuEntry);
1199
+ }
1200
+ tuValue = tuValue.replace(/^\(|\)$|^<|>$/g, "");
1201
+ const match = tuValue.match(/\|fontSize:(\d+)$/);
1202
+ if (match?.[1]) {
1203
+ const fontSize = parseInt(match[1], 10);
1204
+ if (fontSize >= 8 && fontSize <= 72) {
1205
+ return fontSize;
1206
+ }
1207
+ }
1208
+ } catch {
1209
+ }
1210
+ return null;
1211
+ }
1212
+ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {}, _currentSignerEmail, extractedFormFields, metadata, auditTrail, multiSignerContext) {
963
1213
  try {
964
1214
  const { PDFDocument, rgb, StandardFonts } = await loadPdfLib();
965
1215
  const pdfDoc = await PDFDocument.load(pdfBytes);
@@ -976,39 +1226,39 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
976
1226
  continue;
977
1227
  }
978
1228
  try {
979
- const fieldTypeName = field.constructor.name;
980
- if (fieldTypeName === "PDFTextField" || fieldTypeName === "PDFTextField2") {
981
- const textField = field;
982
- textField.setText?.(fieldValue);
983
- } else if (fieldTypeName === "PDFCheckBox" || fieldTypeName === "PDFCheckBox2") {
984
- const checkBox = field;
1229
+ const f = field;
1230
+ const isTextField = typeof f.getText === "function" && typeof f.setText === "function";
1231
+ const isCheckbox = typeof f.check === "function" && typeof f.uncheck === "function";
1232
+ const isRadio = typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function";
1233
+ const isDropdown = typeof f.select === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function");
1234
+ if (isTextField) {
1235
+ f.setText?.(fieldValue);
1236
+ } else if (isCheckbox) {
985
1237
  if (fieldValue === "true" || fieldValue === "Yes" || fieldValue === "checked" || fieldValue === "On") {
986
- checkBox.check?.();
1238
+ f.check?.();
987
1239
  } else {
988
- checkBox.uncheck?.();
1240
+ f.uncheck?.();
989
1241
  }
990
- } else if (fieldTypeName === "PDFRadioGroup" || fieldTypeName === "PDFRadioGroup2") {
991
- const radioGroup = field;
1242
+ } else if (isRadio) {
992
1243
  if (fieldValue === "true" || fieldValue === "false") {
993
1244
  continue;
994
1245
  }
995
1246
  const idxMatch = fieldValue.match(/__RADIO_OPTION_INDEX_(\d+)__/);
996
1247
  if (idxMatch && idxMatch[1]) {
997
1248
  const selectedIndex = parseInt(idxMatch[1], 10);
998
- const options = radioGroup.getOptions?.() || [];
1249
+ const options = f.getOptions?.() || [];
999
1250
  if (selectedIndex >= 0 && selectedIndex < options.length) {
1000
- radioGroup.select?.(options[selectedIndex]);
1251
+ f.select?.(options[selectedIndex]);
1001
1252
  }
1002
1253
  } else {
1003
- const options = radioGroup.getOptions?.() || [];
1254
+ const options = f.getOptions?.() || [];
1004
1255
  if (options.includes(fieldValue)) {
1005
- radioGroup.select?.(fieldValue);
1256
+ f.select?.(fieldValue);
1006
1257
  } else {
1007
1258
  }
1008
1259
  }
1009
- } else if (fieldTypeName === "PDFDropdown" || fieldTypeName === "PDFDropdown2") {
1010
- const dropdown = field;
1011
- dropdown.select?.(fieldValue);
1260
+ } else if (isDropdown) {
1261
+ f.select?.(fieldValue);
1012
1262
  }
1013
1263
  } catch (fieldError) {
1014
1264
  logger.error(`Error setting field "${fieldName}":`, fieldError);
@@ -1016,11 +1266,6 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
1016
1266
  }
1017
1267
  try {
1018
1268
  form.updateFieldAppearances();
1019
- const PDFName3 = pdfLib.PDFName;
1020
- const acroForm = pdfDoc.catalog.lookup(PDFName3.of("AcroForm"));
1021
- if (acroForm && typeof acroForm === "object" && "set" in acroForm) {
1022
- acroForm.set(PDFName3.of("NeedAppearances"), false);
1023
- }
1024
1269
  } catch (appearanceError) {
1025
1270
  logger.warn("Could not update field appearances:", appearanceError);
1026
1271
  }
@@ -1089,6 +1334,20 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
1089
1334
  const y = fieldPosition.y;
1090
1335
  const width = Math.max(fieldPosition.width, 80);
1091
1336
  const height = Math.max(fieldPosition.height, 30);
1337
+ const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
1338
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1339
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
1340
+ const labelFontSize = Math.min(10, height * 0.4);
1341
+ const labelX = x;
1342
+ const labelY = y + height + 5;
1343
+ page.drawText(fieldInfo.label, {
1344
+ x: labelX,
1345
+ y: labelY,
1346
+ size: labelFontSize,
1347
+ font: labelFont,
1348
+ color: rgb(0, 0, 0)
1349
+ });
1350
+ }
1092
1351
  const signatureDims = signatureImage.scaleToFit(width - 4, height - 4);
1093
1352
  const finalX = x;
1094
1353
  const finalY = y;
@@ -1123,7 +1382,7 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
1123
1382
  console.log("[FLATTEN] After pattern matching in fieldPageMap:", foundInitialsFields);
1124
1383
  if (extractedFormFields && extractedFormFields.length > 0) {
1125
1384
  foundInitialsFields = foundInitialsFields.filter((fieldName) => {
1126
- const fieldInfo = extractedFormFields.find((f) => f.name === fieldName);
1385
+ const fieldInfo = extractedFormFields.find((f) => f.name === fieldName) || extractedFormFields.find((f) => f.name === fieldName.replace(/_initials$/i, "")) || extractedFormFields.find((f) => fieldName.startsWith(f.name));
1127
1386
  if (!fieldInfo) return false;
1128
1387
  const isActualInitialsField = fieldInfo.type === "initials" || fieldName.toLowerCase().includes("initials");
1129
1388
  if (!isActualInitialsField) return false;
@@ -1152,6 +1411,19 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
1152
1411
  const y = fieldPosition.y;
1153
1412
  const height = Math.max(fieldPosition.height, 20);
1154
1413
  const fontSize = Math.min(height * 0.7, 14);
1414
+ const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName) || extractedFormFields?.find((f) => f.name === fieldName.replace(/_initials$/i, "")) || extractedFormFields?.find((f) => fieldName.startsWith(f.name)) || extractedFormFields?.find((f) => f.fieldId && fieldName.includes(f.fieldId)) || extractedFormFields?.find((f) => f.type === "initials" && f.name !== "initials_field_main");
1415
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1416
+ const labelFontSize = Math.min(10, height * 0.4);
1417
+ const labelX = x;
1418
+ const labelY = y + height + 5;
1419
+ page.drawText(fieldInfo.label, {
1420
+ x: labelX,
1421
+ y: labelY,
1422
+ size: labelFontSize,
1423
+ font,
1424
+ color: rgb(0, 0, 0)
1425
+ });
1426
+ }
1155
1427
  const finalX = x + 2;
1156
1428
  const finalY = y + (height - fontSize) / 2;
1157
1429
  console.log("[FLATTEN] \u2705 Drawing initials at:", { x: finalX, y: finalY, fontSize, text: mainInitialsData });
@@ -1194,11 +1466,16 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
1194
1466
  let flattenedCount = 0;
1195
1467
  for (const field of fieldsToFlatten) {
1196
1468
  const fieldName = field.getName();
1197
- const fieldType = field.constructor.name;
1469
+ const f = field;
1470
+ const isCheckboxField = typeof f.check === "function" && typeof f.uncheck === "function";
1471
+ const isRadioField = typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function";
1472
+ const isDropdownField = typeof f.select === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function");
1473
+ const isTextField = typeof f.getText === "function" && typeof f.setText === "function";
1474
+ const isSignatureType = !isCheckboxField && !isRadioField && !isDropdownField && !isTextField;
1198
1475
  try {
1199
1476
  const userEnteredValue = formFieldValues[fieldName];
1200
- const isSignatureField2 = fieldName.toLowerCase().includes("signature") || fieldType.includes("Signature");
1201
- const isInitialsField2 = fieldName.toLowerCase().includes("initials") || fieldType.includes("Initials");
1477
+ const isSignatureField2 = fieldName.toLowerCase().includes("signature") || isSignatureType;
1478
+ const isInitialsField2 = fieldName.toLowerCase().includes("initials");
1202
1479
  if (isSignatureField2 || isInitialsField2) {
1203
1480
  form.removeField(field);
1204
1481
  flattenedCount++;
@@ -1206,15 +1483,29 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
1206
1483
  }
1207
1484
  const fieldWithWidgets = field;
1208
1485
  const widgets = fieldWithWidgets.acroField?.getWidgets?.() || [];
1209
- if (fieldType === "PDFCheckBox" || fieldType === "PDFCheckBox2") {
1486
+ if (isCheckboxField) {
1210
1487
  const stringValue = String(userEnteredValue || "");
1211
1488
  const isChecked = stringValue === "true" || stringValue === "Yes" || stringValue === "On" || stringValue === "checked";
1489
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
1490
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
1212
1491
  if (isChecked) {
1213
1492
  for (const widget of widgets) {
1214
1493
  const result = getWidgetRectangleAndPage(widget, pages);
1215
1494
  if (!result) continue;
1216
1495
  const { rect, page } = result;
1217
- const checkboxSize = Math.min(rect.width, rect.height) * 0.6;
1496
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1497
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1498
+ const labelX = rect.x + rect.width + 5;
1499
+ const labelY = rect.y + (rect.height - labelFontSize) / 2;
1500
+ page.drawText(fieldInfo.label, {
1501
+ x: labelX,
1502
+ y: labelY,
1503
+ size: labelFontSize,
1504
+ font: labelFont,
1505
+ color: rgb(0, 0, 0)
1506
+ });
1507
+ }
1508
+ const checkboxSize = Math.min(rect.width, rect.height);
1218
1509
  const checkboxX = rect.x + (rect.width - checkboxSize) / 2;
1219
1510
  const checkboxY = rect.y + (rect.height - checkboxSize) / 2;
1220
1511
  page.drawRectangle({
@@ -1225,21 +1516,51 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
1225
1516
  borderColor: rgb(0, 0, 0),
1226
1517
  borderWidth: 1
1227
1518
  });
1228
- const checkSize = checkboxSize * 0.6;
1229
- const textWidth = checkSize * 0.6;
1230
- const textHeight = checkSize * 0.8;
1519
+ const checkFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
1520
+ const checkFontSize = checkboxSize * 0.7;
1521
+ const textWidth = checkFont.widthOfTextAtSize("X", checkFontSize);
1522
+ const textHeight = checkFont.heightAtSize(checkFontSize);
1231
1523
  page.drawText("X", {
1232
1524
  x: checkboxX + (checkboxSize - textWidth) / 2,
1233
- y: checkboxY + (checkboxSize - textHeight) / 2 + textHeight * 0.2,
1234
- size: checkSize,
1235
- font: await pdfDoc.embedFont(StandardFonts.HelveticaBold),
1525
+ y: checkboxY + (checkboxSize - textHeight) / 2 + textHeight * 0.15,
1526
+ size: checkFontSize,
1527
+ font: checkFont,
1236
1528
  color: rgb(0, 0, 0)
1237
1529
  });
1238
1530
  }
1531
+ } else {
1532
+ for (const widget of widgets) {
1533
+ const result = getWidgetRectangleAndPage(widget, pages);
1534
+ if (!result) continue;
1535
+ const { rect, page } = result;
1536
+ const checkboxSize = Math.min(rect.width, rect.height);
1537
+ const checkboxX = rect.x + (rect.width - checkboxSize) / 2;
1538
+ const checkboxY = rect.y + (rect.height - checkboxSize) / 2;
1539
+ page.drawRectangle({
1540
+ x: checkboxX,
1541
+ y: checkboxY,
1542
+ width: checkboxSize,
1543
+ height: checkboxSize,
1544
+ borderColor: rgb(0, 0, 0),
1545
+ borderWidth: 1
1546
+ });
1547
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1548
+ const labelFontSize = Math.min(12, rect.height * 0.6);
1549
+ const labelX = rect.x + rect.width + 5;
1550
+ const labelY = rect.y + (rect.height - labelFontSize) / 2;
1551
+ page.drawText(fieldInfo.label, {
1552
+ x: labelX,
1553
+ y: labelY,
1554
+ size: labelFontSize,
1555
+ font: labelFont,
1556
+ color: rgb(0, 0, 0)
1557
+ });
1558
+ }
1559
+ }
1239
1560
  }
1240
- } else if (fieldType === "PDFRadioGroup" || fieldType === "PDFRadioGroup2") {
1241
- const radioGroup = field;
1242
- const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
1561
+ } else if (isRadioField) {
1562
+ const radioGroup = f;
1563
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
1243
1564
  let actualSelectedValue = "";
1244
1565
  try {
1245
1566
  actualSelectedValue = radioGroup.getSelected?.() || "";
@@ -1284,14 +1605,15 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
1284
1605
  const result = getWidgetRectangleAndPage(widget, pages);
1285
1606
  if (!result) continue;
1286
1607
  const { rect, page, pageIndex } = result;
1287
- if (i === 0 && fieldInfo?.label && fieldInfo.label.trim() && !isAutoGeneratedLabel(fieldInfo.label)) {
1608
+ if (i === 0 && fieldInfo && hasDrawableLabel(fieldInfo)) {
1288
1609
  const groupLabelKey = `${pageIndex}-${fieldName}-grouplabel`;
1289
1610
  if (!drawnGroupLabels.has(groupLabelKey)) {
1611
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1290
1612
  const labelY = rect.y + rect.height + 3;
1291
1613
  page.drawText(fieldInfo.label, {
1292
1614
  x: rect.x,
1293
1615
  y: labelY,
1294
- size: 8,
1616
+ size: labelFontSize,
1295
1617
  font: labelFont,
1296
1618
  color: rgb(0, 0, 0)
1297
1619
  });
@@ -1299,22 +1621,21 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
1299
1621
  }
1300
1622
  } else if (i === 0 && isAutoGeneratedLabel(fieldInfo?.label || "")) {
1301
1623
  }
1302
- const radioSize = Math.min(rect.width, rect.height) * 0.5;
1303
- const radioX = rect.x + (rect.width - radioSize) / 2;
1304
- const radioY = rect.y + (rect.height - radioSize) / 2;
1624
+ const radioSize = Math.min(rect.width, rect.height);
1625
+ const centerX = rect.x + rect.width / 2;
1626
+ const centerY = rect.y + rect.height / 2;
1305
1627
  page.drawCircle({
1306
- x: radioX + radioSize / 2,
1307
- y: radioY + radioSize / 2,
1308
- size: radioSize,
1628
+ x: centerX,
1629
+ y: centerY,
1630
+ size: radioSize / 2,
1309
1631
  borderColor: rgb(0, 0, 0),
1310
1632
  borderWidth: 1
1311
1633
  });
1312
1634
  if (i === selectedIndex) {
1313
- const circleSize = radioSize * 0.5;
1314
1635
  page.drawCircle({
1315
- x: radioX + radioSize / 2,
1316
- y: radioY + radioSize / 2,
1317
- size: circleSize,
1636
+ x: centerX,
1637
+ y: centerY,
1638
+ size: radioSize / 4,
1318
1639
  color: rgb(0, 0, 0)
1319
1640
  });
1320
1641
  }
@@ -1324,8 +1645,8 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
1324
1645
  if (optionText && !drawnOptionLabels.has(optionKey)) {
1325
1646
  page.drawText(optionText, {
1326
1647
  x: rect.x + rect.width + 4,
1327
- y: rect.y + (rect.height - 8) / 2,
1328
- size: 8,
1648
+ y: rect.y + (rect.height - 7) / 2,
1649
+ size: 7,
1329
1650
  font: labelFont,
1330
1651
  color: rgb(0, 0, 0)
1331
1652
  });
@@ -1335,17 +1656,31 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
1335
1656
  }
1336
1657
  if (fieldInfo?.options) {
1337
1658
  }
1338
- } else if (fieldType === "PDFDropdown" || fieldType === "PDFDropdown2") {
1659
+ } else if (isDropdownField) {
1339
1660
  const selectedValue = userEnteredValue ? String(userEnteredValue) : "";
1340
- if (selectedValue && selectedValue.trim()) {
1341
- for (const widget of widgets) {
1342
- const result = getWidgetRectangleAndPage(widget, pages);
1343
- if (!result) continue;
1344
- const { rect, page } = result;
1661
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
1662
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
1663
+ for (const widget of widgets) {
1664
+ const result = getWidgetRectangleAndPage(widget, pages);
1665
+ if (!result) continue;
1666
+ const { rect, page } = result;
1667
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1668
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1669
+ const labelX = rect.x;
1670
+ const labelY = rect.y + rect.height + 5;
1671
+ page.drawText(fieldInfo.label, {
1672
+ x: labelX,
1673
+ y: labelY,
1674
+ size: labelFontSize,
1675
+ font: labelFont,
1676
+ color: rgb(0, 0, 0)
1677
+ });
1678
+ }
1679
+ if (selectedValue && selectedValue.trim()) {
1345
1680
  page.drawText(selectedValue, {
1346
1681
  x: rect.x + 2,
1347
1682
  y: rect.y + 2,
1348
- size: 10,
1683
+ size: 14,
1349
1684
  font: await pdfDoc.embedFont(StandardFonts.Helvetica),
1350
1685
  color: rgb(0, 0, 0)
1351
1686
  });
@@ -1353,15 +1688,31 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
1353
1688
  }
1354
1689
  } else {
1355
1690
  const fieldValue = userEnteredValue ? String(userEnteredValue) : "";
1356
- if (fieldValue && fieldValue.trim()) {
1357
- for (const widget of widgets) {
1358
- const result = getWidgetRectangleAndPage(widget, pages);
1359
- if (!result) continue;
1360
- const { rect, page } = result;
1691
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
1692
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
1693
+ const isDateField = fieldName.toLowerCase().includes("date") || fieldName.toLowerCase().includes("_date");
1694
+ const fontSize = isDateField ? 14 : extractFieldFontSize(fieldName, field, extractedFormFields);
1695
+ for (const widget of widgets) {
1696
+ const result = getWidgetRectangleAndPage(widget, pages);
1697
+ if (!result) continue;
1698
+ const { rect, page } = result;
1699
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1700
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1701
+ const labelX = rect.x;
1702
+ const labelY = rect.y + rect.height + 5;
1703
+ page.drawText(fieldInfo.label, {
1704
+ x: labelX,
1705
+ y: labelY,
1706
+ size: labelFontSize,
1707
+ font: labelFont,
1708
+ color: rgb(0, 0, 0)
1709
+ });
1710
+ }
1711
+ if (fieldValue && fieldValue.trim()) {
1361
1712
  page.drawText(fieldValue, {
1362
1713
  x: rect.x + 2,
1363
1714
  y: rect.y + 2,
1364
- size: 10,
1715
+ size: fontSize,
1365
1716
  font: await pdfDoc.embedFont(StandardFonts.Helvetica),
1366
1717
  color: rgb(0, 0, 0)
1367
1718
  });
@@ -1484,24 +1835,45 @@ async function fillFormFieldsWithSignatures(pdfBytes, formFields, fieldValues, s
1484
1835
  switch (field.type) {
1485
1836
  case "text" /* TEXT */: {
1486
1837
  const value = fieldValues[field.id] || "";
1838
+ if (hasDrawableLabel(field)) {
1839
+ const labelFontSize = Math.min(10, field.position.height * 0.4);
1840
+ const labelX = pdfX;
1841
+ const labelY = pdfY + field.position.height + 5;
1842
+ page.drawText(field.label, {
1843
+ x: labelX,
1844
+ y: labelY,
1845
+ size: labelFontSize,
1846
+ font,
1847
+ color: rgb(0, 0, 0)
1848
+ });
1849
+ }
1487
1850
  if (value) {
1488
1851
  const fontSize = field.fontSize && field.fontSize >= 8 && field.fontSize <= 72 ? field.fontSize : Math.min(12, field.position.height * 0.6);
1489
1852
  page.drawText(value, {
1490
1853
  x: pdfX + 2,
1491
- // Small padding
1492
1854
  y: pdfY + (field.position.height - fontSize) / 2,
1493
- // Center vertically
1494
1855
  size: fontSize,
1495
1856
  font,
1496
1857
  color: rgb(0, 0, 0),
1497
1858
  maxWidth: field.position.width - 4
1498
- // Leave padding
1499
1859
  });
1500
1860
  }
1501
1861
  break;
1502
1862
  }
1503
1863
  case "date" /* DATE */: {
1504
1864
  const value = fieldValues[field.id] || "";
1865
+ if (hasDrawableLabel(field)) {
1866
+ const labelFontSize = Math.min(10, field.position.height * 0.4);
1867
+ const labelX = pdfX;
1868
+ const labelY = pdfY + field.position.height + 5;
1869
+ page.drawText(field.label, {
1870
+ x: labelX,
1871
+ y: labelY,
1872
+ size: labelFontSize,
1873
+ font,
1874
+ color: rgb(0, 0, 0)
1875
+ });
1876
+ }
1505
1877
  if (value) {
1506
1878
  const fontSize = field.fontSize && field.fontSize >= 8 && field.fontSize <= 72 ? field.fontSize : Math.min(12, field.position.height * 0.6);
1507
1879
  page.drawText(value, {
@@ -1517,10 +1889,22 @@ async function fillFormFieldsWithSignatures(pdfBytes, formFields, fieldValues, s
1517
1889
  }
1518
1890
  case "checkbox" /* CHECKBOX */: {
1519
1891
  const value = fieldValues[field.id] || "";
1892
+ const checkboxSize = Math.min(field.position.width, field.position.height) * 0.7;
1893
+ const checkboxX = pdfX + (field.position.width - checkboxSize) / 2;
1894
+ const checkboxY = pdfY + (field.position.height - checkboxSize) / 2;
1895
+ page.drawRectangle({
1896
+ x: checkboxX,
1897
+ y: checkboxY,
1898
+ width: checkboxSize,
1899
+ height: checkboxSize,
1900
+ borderColor: rgb(0, 0, 0),
1901
+ borderWidth: 1,
1902
+ color: rgb(1, 1, 1)
1903
+ });
1520
1904
  if (value === "true") {
1521
- const checkSize = Math.min(field.position.width, field.position.height) * 0.6;
1522
- const checkX = pdfX + (field.position.width - checkSize) / 2;
1523
- const checkY = pdfY + (field.position.height - checkSize) / 2;
1905
+ const checkSize = checkboxSize * 0.7;
1906
+ const checkX = checkboxX + (checkboxSize - checkSize) / 2;
1907
+ const checkY = checkboxY + (checkboxSize - checkSize) / 2;
1524
1908
  page.drawText("\u2713", {
1525
1909
  x: checkX,
1526
1910
  y: checkY,
@@ -1579,6 +1963,18 @@ async function fillFormFieldsWithSignatures(pdfBytes, formFields, fieldValues, s
1579
1963
  }
1580
1964
  case "dropdown" /* DROPDOWN */: {
1581
1965
  const value = fieldValues[field.id] || "";
1966
+ if (hasDrawableLabel(field)) {
1967
+ const labelFontSize = Math.min(10, field.position.height * 0.4);
1968
+ const labelX = pdfX;
1969
+ const labelY = pdfY + field.position.height + 5;
1970
+ page.drawText(field.label, {
1971
+ x: labelX,
1972
+ y: labelY,
1973
+ size: labelFontSize,
1974
+ font,
1975
+ color: rgb(0, 0, 0)
1976
+ });
1977
+ }
1582
1978
  if (value) {
1583
1979
  const fontSize = Math.min(12, field.position.height * 0.6);
1584
1980
  page.drawText(value, {
@@ -1646,6 +2042,203 @@ async function getFieldPageNumbers(pdfBytes) {
1646
2042
  }
1647
2043
  }
1648
2044
 
2045
+ // src/utils/pdf-signer-utils.ts
2046
+ function getFieldSignerEmail(field) {
2047
+ return field.assignedSignerEmail || null;
2048
+ }
2049
+ function isFieldAssignedToSigner(field, signerEmail, allSignerEmails = []) {
2050
+ const assignedTo = field.assignedSignerEmail;
2051
+ if (!assignedTo) return false;
2052
+ if (assignedTo === signerEmail) return true;
2053
+ if (assignedTo === "to-recipients" && allSignerEmails.includes(signerEmail)) {
2054
+ return true;
2055
+ }
2056
+ if (assignedTo === "additional-signers" && allSignerEmails.includes(signerEmail)) {
2057
+ return true;
2058
+ }
2059
+ return false;
2060
+ }
2061
+ async function preparePdfForSigner(inputPdfBytes, formFields, options) {
2062
+ const {
2063
+ activeSignerEmail,
2064
+ hideMode = "NoView",
2065
+ allSignerEmails = []
2066
+ } = options;
2067
+ logger.info(`\u{1F510} Preparing PDF for signer: ${activeSignerEmail}`);
2068
+ logger.info(`\u{1F510} Form fields to process: ${formFields.length}`);
2069
+ logger.info(`\u{1F510} Hide mode: ${hideMode}`);
2070
+ logger.info(`\u{1F510} All signer emails:`, allSignerEmails);
2071
+ logger.info(`\u{1F510} Form field assignments:`, formFields.map((f) => ({
2072
+ name: f.name,
2073
+ assignedTo: f.assignedSignerEmail
2074
+ })));
2075
+ const pdfLib = await loadPdfLib();
2076
+ const { PDFDocument, AnnotationFlags } = pdfLib;
2077
+ let pdfDoc;
2078
+ try {
2079
+ logger.info(`\u{1F510} Loading PDF document, bytes length: ${inputPdfBytes.byteLength || inputPdfBytes.length}`);
2080
+ const bytesArray = inputPdfBytes instanceof Uint8Array ? inputPdfBytes : new Uint8Array(inputPdfBytes);
2081
+ const header = new TextDecoder().decode(bytesArray.slice(0, 8));
2082
+ logger.info(`\u{1F510} PDF header: "${header}"`);
2083
+ if (!header.startsWith("%PDF-")) {
2084
+ throw new Error(`Invalid PDF header: "${header}". Expected "%PDF-"`);
2085
+ }
2086
+ pdfDoc = await PDFDocument.load(inputPdfBytes);
2087
+ logger.info(`\u2705 Successfully loaded PDF document`);
2088
+ } catch (loadError) {
2089
+ logger.error(`\u274C Failed to load PDF with pdf-lib:`, loadError);
2090
+ logger.info(`\u{1F50D} PDF bytes details:`, {
2091
+ type: inputPdfBytes.constructor.name,
2092
+ length: inputPdfBytes.byteLength || inputPdfBytes.length,
2093
+ isArrayBuffer: inputPdfBytes instanceof ArrayBuffer,
2094
+ isUint8Array: inputPdfBytes instanceof Uint8Array,
2095
+ first20Bytes: Array.from(new Uint8Array(inputPdfBytes instanceof ArrayBuffer ? inputPdfBytes : inputPdfBytes.buffer).slice(0, 20)).map((b) => b.toString(16).padStart(2, "0")).join(" ")
2096
+ });
2097
+ throw new Error(`PDF parsing failed for signer ${activeSignerEmail}: ${loadError instanceof Error ? loadError.message : "Unknown error"}`);
2098
+ }
2099
+ const form = pdfDoc.getForm();
2100
+ const pdfFields = form.getFields();
2101
+ logger.info(`\u{1F510} PDF contains ${pdfFields.length} form fields`);
2102
+ for (const pdfField of pdfFields) {
2103
+ const pdfFieldName = pdfField.getName();
2104
+ const matchingFormField = formFields.find(
2105
+ (f) => f.name === pdfFieldName || (f.type === "signature" /* SIGNATURE */ || f.type === "initials" /* INITIALS */) && `${f.name}_signature` === pdfFieldName
2106
+ );
2107
+ if (!matchingFormField) {
2108
+ logger.info(`\u{1F510} PDF field "${pdfFieldName}" not found in provided form fields - leaving unchanged`);
2109
+ continue;
2110
+ }
2111
+ logger.info(`\u{1F510} Matched PDF field "${pdfFieldName}" to FormField "${matchingFormField.name}". Assigned to: ${matchingFormField.assignedSignerEmail}`);
2112
+ const fieldSignerEmail = getFieldSignerEmail(matchingFormField);
2113
+ const isAssignedToActiveSigner = isFieldAssignedToSigner(
2114
+ matchingFormField,
2115
+ activeSignerEmail,
2116
+ allSignerEmails
2117
+ );
2118
+ const isAssignedToCompletedSigner = (options.completedSignerEmails || []).some(
2119
+ (completedEmail) => isFieldAssignedToSigner(matchingFormField, completedEmail, allSignerEmails)
2120
+ );
2121
+ const isUnassigned = !fieldSignerEmail;
2122
+ let isVisible = false;
2123
+ let reason = "";
2124
+ if (isAssignedToActiveSigner) {
2125
+ isVisible = true;
2126
+ reason = "Assigned to active signer";
2127
+ } else if (isUnassigned) {
2128
+ isVisible = true;
2129
+ reason = "Unassigned field (visible to all)";
2130
+ } else {
2131
+ isVisible = false;
2132
+ reason = `Assigned to different signer (${fieldSignerEmail})`;
2133
+ }
2134
+ const isLocked = (options.lockCompletedFields ?? true) && isAssignedToCompletedSigner;
2135
+ if (isLocked) {
2136
+ reason += " - Locked (completed)";
2137
+ }
2138
+ logger.info(`\u{1F510} Processing field "${pdfFieldName}": ${reason}`);
2139
+ if (isLocked) {
2140
+ try {
2141
+ const fieldWithExtensions = pdfField;
2142
+ if (typeof fieldWithExtensions.enableReadOnly === "function") {
2143
+ fieldWithExtensions.enableReadOnly();
2144
+ } else if (typeof fieldWithExtensions.setReadOnly === "function") {
2145
+ fieldWithExtensions.setReadOnly(true);
2146
+ }
2147
+ logger.info(`\u{1F512} Locked field "${pdfFieldName}"`);
2148
+ } catch (error) {
2149
+ logger.warn(`\u26A0\uFE0F Could not lock field "${pdfFieldName}":`, error);
2150
+ }
2151
+ }
2152
+ try {
2153
+ const fieldWithExtensions = pdfField;
2154
+ const widgets = fieldWithExtensions.acroField?.getWidgets?.() ?? [];
2155
+ for (const widget of widgets) {
2156
+ if (typeof widget.clearFlag === "function") {
2157
+ widget.clearFlag(AnnotationFlags.Hidden);
2158
+ widget.clearFlag(AnnotationFlags.NoView);
2159
+ }
2160
+ if (!isVisible) {
2161
+ const flag = hideMode === "Hidden" ? AnnotationFlags.Hidden : AnnotationFlags.NoView;
2162
+ if (typeof widget.setFlag === "function") {
2163
+ widget.setFlag(flag);
2164
+ }
2165
+ if (typeof widget.clearFlag === "function") {
2166
+ widget.clearFlag(AnnotationFlags.Print);
2167
+ }
2168
+ logger.info(`\u{1F648} Hidden field "${pdfFieldName}" using ${hideMode} flag`);
2169
+ } else {
2170
+ if (typeof widget.setFlag === "function") {
2171
+ widget.setFlag(AnnotationFlags.Print);
2172
+ }
2173
+ logger.info(`\u{1F441}\uFE0F Made field "${pdfFieldName}" visible`);
2174
+ }
2175
+ }
2176
+ } catch (error) {
2177
+ logger.warn(`\u26A0\uFE0F Could not set visibility for field "${pdfFieldName}":`, error);
2178
+ }
2179
+ }
2180
+ try {
2181
+ const currentSubject = pdfDoc.getSubject() || "";
2182
+ const signerInfo = `Prepared for: ${activeSignerEmail}`;
2183
+ const newSubject = currentSubject ? `${currentSubject} | ${signerInfo}` : signerInfo;
2184
+ pdfDoc.setSubject(newSubject);
2185
+ const currentKeywords = pdfDoc.getKeywords() || [];
2186
+ const updatedKeywords = [...currentKeywords, `ActiveSigner:${activeSignerEmail}`];
2187
+ pdfDoc.setKeywords(updatedKeywords);
2188
+ logger.info(`\u{1F4DD} Added signer metadata to PDF`);
2189
+ } catch (error) {
2190
+ logger.warn(`\u26A0\uFE0F Could not set PDF metadata:`, error);
2191
+ }
2192
+ const modifiedPdfBytes = await pdfDoc.save();
2193
+ logger.info(`\u2705 PDF prepared for ${activeSignerEmail}: ${modifiedPdfBytes.length} bytes`);
2194
+ return new Uint8Array(modifiedPdfBytes);
2195
+ }
2196
+ function areAllSignersComplete(signers, completedSignerEmails) {
2197
+ const requiredSigners = new Set(signers.map((s) => s.email));
2198
+ const completedSigners = new Set(completedSignerEmails);
2199
+ for (const signerEmail of requiredSigners) {
2200
+ if (!completedSigners.has(signerEmail)) {
2201
+ return false;
2202
+ }
2203
+ }
2204
+ return true;
2205
+ }
2206
+ function getNextSigner(signers, completedSignerEmails) {
2207
+ const completedSet = new Set(completedSignerEmails);
2208
+ const sortedSigners = [...signers].sort((a, b) => a.signOrder - b.signOrder);
2209
+ for (const signer of sortedSigners) {
2210
+ if (!completedSet.has(signer.email)) {
2211
+ return signer;
2212
+ }
2213
+ }
2214
+ return null;
2215
+ }
2216
+ function validateFieldAssignments(formFields, signers) {
2217
+ const errors = [];
2218
+ const signerEmailSet = new Set(signers.map((s) => s.email));
2219
+ const assignedEmails = new Set(
2220
+ formFields.map((field) => field.assignedSignerEmail).filter((email) => email)
2221
+ );
2222
+ for (const assignedEmail of assignedEmails) {
2223
+ if (assignedEmail === "to-recipients" || assignedEmail === "additional-signers") {
2224
+ continue;
2225
+ }
2226
+ if (!signerEmailSet.has(assignedEmail)) {
2227
+ errors.push(`Field assigned to unknown signer: ${assignedEmail}`);
2228
+ }
2229
+ }
2230
+ const requiredUnassignedFields = formFields.filter(
2231
+ (field) => field.required && !field.assignedSignerEmail
2232
+ );
2233
+ if (requiredUnassignedFields.length > 0) {
2234
+ errors.push(`${requiredUnassignedFields.length} required fields are not assigned to any signer`);
2235
+ }
2236
+ return {
2237
+ isValid: errors.length === 0,
2238
+ errors
2239
+ };
2240
+ }
2241
+
1649
2242
  // src/utils/field-extraction.ts
1650
2243
  var PDFName2;
1651
2244
  async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
@@ -1660,7 +2253,6 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1660
2253
  const fields = form.getFields();
1661
2254
  const visibleFields = [];
1662
2255
  let hasAnyInitialsFields = false;
1663
- let hasInitialsFieldsForCurrentSigner = false;
1664
2256
  for (const field of fields) {
1665
2257
  const fieldName = field.getName();
1666
2258
  const fieldWithExtensions = field;
@@ -1686,28 +2278,31 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1686
2278
  };
1687
2279
  })();
1688
2280
  let fieldType = "text" /* TEXT */;
1689
- const fieldTypeName = field.constructor.name;
2281
+ const f = field;
2282
+ const isCheckboxField = typeof f.check === "function" && typeof f.uncheck === "function";
2283
+ const isRadioField = typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function";
2284
+ const isDropdownField = typeof f.select === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function");
2285
+ const isTextField = typeof f.getText === "function" && typeof f.setText === "function";
2286
+ const isSignatureType = !isCheckboxField && !isRadioField && !isDropdownField && !isTextField;
1690
2287
  const cleanFieldName = fieldName.replace(/_signature$|_initials$|_date$/i, "");
1691
- const isActualSignatureField = fieldTypeName.includes("Signature") || cleanFieldName.toLowerCase().includes("signature") && !fieldTypeName.includes("CheckBox") && !fieldTypeName.includes("Radio");
1692
- const isActualInitialsField = cleanFieldName.toLowerCase().includes("initials") && !fieldTypeName.includes("CheckBox") && !fieldTypeName.includes("Radio");
2288
+ const isActualSignatureField = isSignatureType || cleanFieldName.toLowerCase().includes("signature") && !isCheckboxField && !isRadioField;
2289
+ const isActualInitialsField = cleanFieldName.toLowerCase().includes("initials") && !isCheckboxField && !isRadioField;
1693
2290
  if (isActualSignatureField) {
1694
2291
  fieldType = "signature" /* SIGNATURE */;
1695
2292
  } else if (isActualInitialsField) {
1696
2293
  fieldType = "initials" /* INITIALS */;
1697
2294
  hasAnyInitialsFields = true;
1698
- if (currentSignerEmail && fieldMetadata.signer === currentSignerEmail) {
1699
- hasInitialsFieldsForCurrentSigner = true;
1700
- }
1701
2295
  } else if (cleanFieldName.toLowerCase().includes("date")) {
1702
2296
  fieldType = "date" /* DATE */;
1703
- } else if (fieldTypeName.includes("CheckBox")) {
2297
+ } else if (isCheckboxField) {
1704
2298
  fieldType = "checkbox" /* CHECKBOX */;
1705
- } else if (fieldTypeName.toLowerCase().includes("dropdown")) {
2299
+ } else if (isDropdownField) {
1706
2300
  fieldType = "dropdown" /* DROPDOWN */;
1707
- } else if (fieldTypeName.includes("Radio")) {
2301
+ } else if (isRadioField) {
1708
2302
  fieldType = "radio" /* RADIO */;
1709
2303
  }
1710
2304
  let displayLabel = fieldMetadata.label || "";
2305
+ let isLabelAutoGenerated = false;
1711
2306
  if (!displayLabel || !displayLabel.trim()) {
1712
2307
  try {
1713
2308
  const tuLabel = extractTULabel(fieldWithExtensions);
@@ -1719,6 +2314,7 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1719
2314
  }
1720
2315
  if (!displayLabel || !displayLabel.trim()) {
1721
2316
  displayLabel = generateFallbackLabel(cleanFieldName, fieldType);
2317
+ isLabelAutoGenerated = true;
1722
2318
  }
1723
2319
  const esignField = {
1724
2320
  id: fieldName,
@@ -1730,6 +2326,8 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1730
2326
  type: fieldType,
1731
2327
  label: displayLabel,
1732
2328
  // Use friendly label for display
2329
+ isLabelAutoGenerated,
2330
+ // True when label was generated from field name (should not be drawn on PDF)
1733
2331
  position: { x: 0, y: 0, width: 100, height: 30, page: 1 },
1734
2332
  // Default position
1735
2333
  required: fieldWithExtensions.isRequired?.() ?? false,
@@ -1750,6 +2348,13 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1750
2348
  logger.warn("Error extracting options for field:", error);
1751
2349
  }
1752
2350
  }
2351
+ try {
2352
+ const currentValue = extractFieldValue(f, esignField.type);
2353
+ if (currentValue) {
2354
+ esignField.defaultValue = currentValue;
2355
+ }
2356
+ } catch {
2357
+ }
1753
2358
  visibleFields.push(esignField);
1754
2359
  }
1755
2360
  }
@@ -1766,11 +2371,8 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1766
2371
  placeholder: "Draw or upload your signature",
1767
2372
  assignedSignerEmail: currentSignerEmail
1768
2373
  });
1769
- const shouldCreateInitialsField = hasInitialsFieldsForCurrentSigner || hasAnyInitialsFields && !currentSignerEmail;
2374
+ const shouldCreateInitialsField = hasAnyInitialsFields;
1770
2375
  if (shouldCreateInitialsField) {
1771
- if (hasInitialsFieldsForCurrentSigner) {
1772
- } else {
1773
- }
1774
2376
  mainFields.push({
1775
2377
  id: "initials_field_main",
1776
2378
  fieldId: "initials_field_main",
@@ -1857,8 +2459,12 @@ function decodeFieldName(fieldName) {
1857
2459
  }
1858
2460
  function generateFallbackLabel(decodedFieldName, fieldType) {
1859
2461
  let displayLabel = decodedFieldName;
1860
- displayLabel = displayLabel.replace(/_signature$/i, "").replace(/_initials$/i, "").replace(/_date$/i, "");
1861
- if (/^(text|signature|initials|date)_\d+$/i.test(displayLabel)) {
2462
+ let prevLabel = "";
2463
+ while (displayLabel !== prevLabel) {
2464
+ prevLabel = displayLabel;
2465
+ displayLabel = displayLabel.replace(/_signature$/i, "").replace(/_initials$/i, "").replace(/_date$/i, "");
2466
+ }
2467
+ if (/^(text|signature|initials|date|checkbox|radio|dropdown)_\d+/i.test(displayLabel)) {
1862
2468
  if (fieldType === "signature" /* SIGNATURE */) return "Signature";
1863
2469
  if (fieldType === "initials" /* INITIALS */) return "Initials";
1864
2470
  if (fieldType === "date" /* DATE */) return "Date";
@@ -1866,9 +2472,9 @@ function generateFallbackLabel(decodedFieldName, fieldType) {
1866
2472
  if (fieldType === "checkbox" /* CHECKBOX */) return "Checkbox";
1867
2473
  if (fieldType === "dropdown" /* DROPDOWN */) return "Dropdown";
1868
2474
  if (fieldType === "radio" /* RADIO */) return "Option";
1869
- } else {
1870
- displayLabel = displayLabel.replace(/_/g, " ").replace(/\s+\d+$/g, "").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
2475
+ if (fieldType === "text_label" /* TEXT_LABEL */) return "Text Label";
1871
2476
  }
2477
+ displayLabel = displayLabel.replace(/_/g, " ").replace(/\s+\d+$/g, "").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1872
2478
  return displayLabel;
1873
2479
  }
1874
2480
  function extractTULabel(field) {
@@ -1895,6 +2501,28 @@ function extractTULabel(field) {
1895
2501
  return "";
1896
2502
  }
1897
2503
  }
2504
+ function findFieldByUuid(fields, uuid) {
2505
+ return fields.find((f) => f.fieldId === uuid);
2506
+ }
2507
+ function resolveField(fields, identifier) {
2508
+ let field = findFieldByUuid(fields, identifier);
2509
+ if (field) {
2510
+ return field;
2511
+ }
2512
+ field = fields.find((f) => f.id === identifier);
2513
+ if (field) {
2514
+ return field;
2515
+ }
2516
+ field = fields.find((f) => f.name === identifier);
2517
+ if (field) {
2518
+ return field;
2519
+ }
2520
+ const cleanIdentifier = identifier.replace(/_signature$|_initials$|_date$/i, "");
2521
+ return fields.find((f) => f.fieldId === cleanIdentifier || f.id === cleanIdentifier || f.name === cleanIdentifier);
2522
+ }
2523
+ function getFieldUuidsForSigner(fields, signerEmail) {
2524
+ return fields.filter((f) => f.assignedSignerEmail === signerEmail && f.fieldId).map((f) => f.fieldId);
2525
+ }
1898
2526
 
1899
2527
  // src/utils/audit-trail.ts
1900
2528
  function captureDeviceMetadata() {
@@ -2066,13 +2694,13 @@ function getErrorMessage(error, defaultMessage = "Unknown error") {
2066
2694
 
2067
2695
  // src/utils/attachment-validators.ts
2068
2696
  var DEFAULT_ATTACHMENT_CONSTRAINTS = {
2069
- maxFileSize: 10 * 1024 * 1024,
2070
- // 10MB
2697
+ maxFileSize: 25 * 1024 * 1024,
2698
+ // 25MB
2071
2699
  maxTotalSize: 50 * 1024 * 1024,
2072
2700
  // 50MB
2073
2701
  maxFiles: 10,
2074
2702
  allowedTypes: ["image/*", "application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
2075
- allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png", ".gif", ".doc", ".docx"]
2703
+ allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".doc", ".docx"]
2076
2704
  };
2077
2705
  function validateFile(file, constraints = DEFAULT_ATTACHMENT_CONSTRAINTS) {
2078
2706
  const errors = [];
@@ -2364,6 +2992,23 @@ function withPerformanceMonitoring(label, fn, monitor = globalPerformanceMonitor
2364
2992
  }
2365
2993
  });
2366
2994
  }
2995
+ function isValidISODate(value) {
2996
+ if (!value || typeof value !== "string") {
2997
+ return false;
2998
+ }
2999
+ const isoPattern = /^\d{4}-\d{2}-\d{2}$/;
3000
+ if (!isoPattern.test(value)) {
3001
+ return false;
3002
+ }
3003
+ try {
3004
+ const date = parseISO(value);
3005
+ return isValid(date) && !isNaN(date.getTime());
3006
+ } catch {
3007
+ return false;
3008
+ }
3009
+ }
3010
+
3011
+ // src/utils/date-validation.ts
2367
3012
  function toIsoDateString(date) {
2368
3013
  const year = date.getFullYear();
2369
3014
  const month = String(date.getMonth() + 1).padStart(2, "0");
@@ -2487,21 +3132,6 @@ function parseAndValidateDate(value) {
2487
3132
  error: `Invalid date format: "${trimmedValue}". Please use a date picker or format like MM/DD/YYYY.`
2488
3133
  };
2489
3134
  }
2490
- function isValidISODate(value) {
2491
- if (!value || typeof value !== "string") {
2492
- return false;
2493
- }
2494
- const isoPattern = /^\d{4}-\d{2}-\d{2}$/;
2495
- if (!isoPattern.test(value)) {
2496
- return false;
2497
- }
2498
- try {
2499
- const date = parseISO(value);
2500
- return isValid(date) && !isNaN(date.getTime());
2501
- } catch {
2502
- return false;
2503
- }
2504
- }
2505
3135
 
2506
3136
  // src/utils/tracking.ts
2507
3137
  var DEFAULT_API_ENDPOINT = "https://api-dev.signiphi.ai";
@@ -2617,9 +3247,82 @@ var PdfViewerCore = forwardRef(
2617
3247
  }
2618
3248
  return PDFViewerApplication;
2619
3249
  }, [getPDFViewerApplication]);
2620
- const loadPdf = useCallback(
2621
- async (pdfUrl) => {
2622
- const iframe = iframeRef.current;
3250
+ const fieldMetadataRef = useRef([]);
3251
+ const setFieldMetadata = useCallback((fields) => {
3252
+ fieldMetadataRef.current = fields;
3253
+ logger.info("[FIELD METADATA] Updated field metadata:", fields.map((f) => ({ name: f.name, fontSize: f.fontSize, type: f.type })));
3254
+ }, []);
3255
+ const extractFieldFontSizes = useCallback(async () => {
3256
+ const fontSizeMap = {};
3257
+ for (const field of fieldMetadataRef.current) {
3258
+ if (field.type === "text" /* TEXT */) {
3259
+ if (field.fontSize && field.fontSize >= 8 && field.fontSize <= 72) {
3260
+ fontSizeMap[field.name] = field.fontSize;
3261
+ }
3262
+ }
3263
+ }
3264
+ if (Object.keys(fontSizeMap).length === 0) {
3265
+ try {
3266
+ const PDFViewerApplication = await waitForInitialization();
3267
+ if (PDFViewerApplication?.pdfDocument?.getFieldObjects) {
3268
+ const fieldObjects = await PDFViewerApplication.pdfDocument.getFieldObjects();
3269
+ for (const [fieldName, fields] of Object.entries(fieldObjects)) {
3270
+ const fieldArray = fields;
3271
+ for (const field of fieldArray) {
3272
+ if (field.alternateText) {
3273
+ const match = field.alternateText.match(/\|fontSize:(\d+)$/);
3274
+ if (match) {
3275
+ fontSizeMap[fieldName] = parseInt(match[1], 10);
3276
+ break;
3277
+ }
3278
+ }
3279
+ }
3280
+ }
3281
+ }
3282
+ } catch (error) {
3283
+ logger.warn("[FONT SIZE] Error extracting from PDF:", error);
3284
+ }
3285
+ }
3286
+ return fontSizeMap;
3287
+ }, [waitForInitialization]);
3288
+ const injectFormFieldFontSizeCSS = useCallback(async (doc) => {
3289
+ const styleId = "signiphi-pdf-font-size-style";
3290
+ const existingStyle = doc.getElementById(styleId);
3291
+ if (existingStyle) {
3292
+ existingStyle.remove();
3293
+ }
3294
+ const fontSizeMap = await extractFieldFontSizes();
3295
+ const cssRules = [];
3296
+ for (const [fieldName, fontSize] of Object.entries(fontSizeMap)) {
3297
+ cssRules.push(`
3298
+ input[name="${fieldName}"],
3299
+ input[name="${fieldName}_date"],
3300
+ select[name="${fieldName}"] {
3301
+ font-size: ${fontSize}px !important;
3302
+ }
3303
+ `);
3304
+ }
3305
+ cssRules.push(`
3306
+ input[data-element-id][name*="date"],
3307
+ select[data-element-id] {
3308
+ font-size: 18px !important;
3309
+ }
3310
+ `);
3311
+ cssRules.push(`
3312
+ input[type="text"][data-element-id]:not([style*="font-size"]),
3313
+ select[data-element-id]:not([style*="font-size"]) {
3314
+ font-size: 12px !important; /* Default fallback */
3315
+ }
3316
+ `);
3317
+ const style = doc.createElement("style");
3318
+ style.id = styleId;
3319
+ style.textContent = `/* Dynamic font sizes from PDF metadata */
3320
+ ${cssRules.join("\n")}`;
3321
+ doc.head.appendChild(style);
3322
+ }, [extractFieldFontSizes]);
3323
+ const loadPdf = useCallback(
3324
+ async (pdfUrl) => {
3325
+ const iframe = iframeRef.current;
2623
3326
  if (!iframe) {
2624
3327
  logger.error("PDF viewer iframe not available");
2625
3328
  return;
@@ -2647,7 +3350,10 @@ var PdfViewerCore = forwardRef(
2647
3350
  try {
2648
3351
  const PDFViewerApplication = getPDFViewerApplication();
2649
3352
  if (PDFViewerApplication && PDFViewerApplication.initializedPromise) {
2650
- PDFViewerApplication.initializedPromise.then(() => {
3353
+ PDFViewerApplication.initializedPromise.then(async () => {
3354
+ if (iframe.contentDocument) {
3355
+ await injectFormFieldFontSizeCSS(iframe.contentDocument);
3356
+ }
2651
3357
  resolve();
2652
3358
  }).catch(reject);
2653
3359
  } else {
@@ -2727,6 +3433,7 @@ var PdfViewerCore = forwardRef(
2727
3433
  }
2728
3434
  }
2729
3435
  logger.info(`[RADIO DETECT] Found ${Object.keys(radioGroups).length} radio groups:`, Object.keys(radioGroups));
3436
+ const processedRadioFieldNames = new Set(Object.keys(radioGroups));
2730
3437
  for (const [fieldName, radioButtons] of Object.entries(radioGroups)) {
2731
3438
  const selectedRadio = radioButtons.find(
2732
3439
  (rb) => rb.data && typeof rb.data === "object" && rb.data.value === true
@@ -2752,7 +3459,7 @@ var PdfViewerCore = forwardRef(
2752
3459
  }
2753
3460
  for (const [id, data] of Object.entries(storedData)) {
2754
3461
  const fieldName = idToNameMap[id];
2755
- if (fieldName && values[fieldName]) {
3462
+ if (fieldName && (fieldName in values || processedRadioFieldNames.has(fieldName))) {
2756
3463
  continue;
2757
3464
  }
2758
3465
  if (fieldName) {
@@ -2769,13 +3476,13 @@ var PdfViewerCore = forwardRef(
2769
3476
  } else if (data !== void 0 && data !== null) {
2770
3477
  extractedValue = String(data);
2771
3478
  }
2772
- if (extractedValue !== void 0 && extractedValue !== "") {
3479
+ if (extractedValue !== void 0) {
2773
3480
  values[fieldName] = extractedValue;
2774
3481
  }
2775
3482
  }
2776
3483
  }
2777
3484
  for (const [name, fields] of Object.entries(fieldObjects)) {
2778
- if (!values[name]) {
3485
+ if (!(name in values)) {
2779
3486
  const fieldArray = fields;
2780
3487
  const field = fieldArray[0];
2781
3488
  if (field && field.value !== void 0 && field.value !== null) {
@@ -2976,6 +3683,8 @@ var PdfViewerCore = forwardRef(
2976
3683
  }
2977
3684
  }, 1e3);
2978
3685
  }, []);
3686
+ const injectRadioLabels = useCallback((_fields) => {
3687
+ }, []);
2979
3688
  const zoomIn = useCallback(async () => {
2980
3689
  try {
2981
3690
  const PDFViewerApplication = await waitForInitialization();
@@ -3046,7 +3755,7 @@ var PdfViewerCore = forwardRef(
3046
3755
  return null;
3047
3756
  }
3048
3757
  }, [waitForInitialization]);
3049
- const attachFieldClickInterceptors = useCallback((onFieldClick) => {
3758
+ const attachFieldClickInterceptors = useCallback(async (onFieldClick) => {
3050
3759
  const iframe = iframeRef.current;
3051
3760
  if (!iframe?.contentDocument) {
3052
3761
  logger.warn("Cannot attach field interceptors: iframe not ready");
@@ -3054,6 +3763,7 @@ var PdfViewerCore = forwardRef(
3054
3763
  }
3055
3764
  try {
3056
3765
  const doc = iframe.contentDocument;
3766
+ await injectFormFieldFontSizeCSS(doc);
3057
3767
  const clickHighlightStyleId = "signiphi-click-highlight-style";
3058
3768
  if (!doc.getElementById(clickHighlightStyleId)) {
3059
3769
  const style = doc.createElement("style");
@@ -3124,7 +3834,7 @@ var PdfViewerCore = forwardRef(
3124
3834
  return;
3125
3835
  }
3126
3836
  logger.info(`Field click intercepted: ${fieldName}`, e.type);
3127
- const shouldProceed = onFieldClick(fieldName);
3837
+ const shouldProceed = onFieldClick(fieldName, target);
3128
3838
  logger.info(`Field ${fieldName} shouldProceed: ${shouldProceed}`);
3129
3839
  if (e.type !== "pointerdown" && e.type !== "mousedown") {
3130
3840
  if (!shouldProceed) {
@@ -3200,6 +3910,57 @@ var PdfViewerCore = forwardRef(
3200
3910
  logger.error("Error attaching field click interceptors:", error);
3201
3911
  }
3202
3912
  }, []);
3913
+ const attachFieldChangeListeners = useCallback(async (onFieldChange) => {
3914
+ try {
3915
+ const iframe = iframeRef.current;
3916
+ if (!iframe?.contentDocument) {
3917
+ logger.warn("Cannot attach field change listeners: iframe not ready");
3918
+ return () => {
3919
+ };
3920
+ }
3921
+ const doc = iframe.contentDocument;
3922
+ const handleChange = (event) => {
3923
+ const target = event.target;
3924
+ if (!target || !["INPUT", "SELECT", "TEXTAREA"].includes(target.tagName)) {
3925
+ return;
3926
+ }
3927
+ const fieldName = target.getAttribute("name") || target.getAttribute("data-element-id") || target.getAttribute("id") || "";
3928
+ if (!fieldName) return;
3929
+ let value = "";
3930
+ if (target.tagName === "INPUT") {
3931
+ const inputElement = target;
3932
+ if (inputElement.type === "checkbox") {
3933
+ value = inputElement.checked ? "true" : "false";
3934
+ } else if (inputElement.type === "radio") {
3935
+ if (inputElement.checked) {
3936
+ value = inputElement.value || "true";
3937
+ } else {
3938
+ return;
3939
+ }
3940
+ } else {
3941
+ value = inputElement.value;
3942
+ }
3943
+ } else if (target.tagName === "SELECT") {
3944
+ value = target.value;
3945
+ } else if (target.tagName === "TEXTAREA") {
3946
+ value = target.value;
3947
+ }
3948
+ onFieldChange(fieldName, value, target);
3949
+ };
3950
+ doc.addEventListener("change", handleChange, true);
3951
+ doc.addEventListener("input", handleChange, true);
3952
+ logger.info("Attached field change listeners to iframe document");
3953
+ return () => {
3954
+ doc.removeEventListener("change", handleChange, true);
3955
+ doc.removeEventListener("input", handleChange, true);
3956
+ logger.info("Removed field change listeners from iframe document");
3957
+ };
3958
+ } catch (error) {
3959
+ logger.error("Error attaching field change listeners:", error);
3960
+ return () => {
3961
+ };
3962
+ }
3963
+ }, []);
3203
3964
  const addFieldIndicator = useCallback(async (fieldName, indicatorType = "completed", allowedFieldIds) => {
3204
3965
  try {
3205
3966
  const iframe = iframeRef.current;
@@ -3415,6 +4176,275 @@ var PdfViewerCore = forwardRef(
3415
4176
  logger.error("Error removing field indicator:", error);
3416
4177
  }
3417
4178
  }, []);
4179
+ const addDateFieldIndicator = useCallback((fieldName) => {
4180
+ try {
4181
+ const iframe = iframeRef.current;
4182
+ if (!iframe?.contentDocument) {
4183
+ logger.warn("Cannot add date field indicator: iframe not ready");
4184
+ return;
4185
+ }
4186
+ const doc = iframe.contentDocument;
4187
+ const formElement = doc.querySelector(
4188
+ `input[name="${fieldName}"], input[data-element-id="${fieldName}"], textarea[name="${fieldName}"], textarea[data-element-id="${fieldName}"]`
4189
+ );
4190
+ if (!formElement) {
4191
+ logger.warn(`Cannot add date indicator: field not found for "${fieldName}"`);
4192
+ return;
4193
+ }
4194
+ const annotationSection = formElement.closest("section");
4195
+ if (!annotationSection) {
4196
+ logger.warn(`Cannot add date indicator: no annotation section for "${fieldName}"`);
4197
+ return;
4198
+ }
4199
+ const existingIndicator = annotationSection.querySelector(".signiphi-date-field-indicator");
4200
+ if (existingIndicator) {
4201
+ existingIndicator.remove();
4202
+ }
4203
+ const indicator = doc.createElement("div");
4204
+ indicator.className = "signiphi-date-field-indicator";
4205
+ indicator.setAttribute("role", "button");
4206
+ indicator.setAttribute("aria-label", "Open calendar picker");
4207
+ indicator.setAttribute("tabindex", "0");
4208
+ indicator.innerHTML = `
4209
+ <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
4210
+ <rect x="2" y="3" width="14" height="13" rx="2" stroke="#6b7280" stroke-width="1.5" fill="white"/>
4211
+ <line x1="2" y1="6.5" x2="16" y2="6.5" stroke="#6b7280" stroke-width="1.5"/>
4212
+ <line x1="5.5" y1="1" x2="5.5" y2="5" stroke="#6b7280" stroke-width="1.5" stroke-linecap="round"/>
4213
+ <line x1="12.5" y1="1" x2="12.5" y2="5" stroke="#6b7280" stroke-width="1.5" stroke-linecap="round"/>
4214
+ <circle cx="5.5" cy="9.5" r="0.8" fill="#6b7280"/>
4215
+ <circle cx="9" cy="9.5" r="0.8" fill="#6b7280"/>
4216
+ <circle cx="12.5" cy="9.5" r="0.8" fill="#6b7280"/>
4217
+ <circle cx="5.5" cy="12.5" r="0.8" fill="#6b7280"/>
4218
+ <circle cx="9" cy="12.5" r="0.8" fill="#6b7280"/>
4219
+ <circle cx="12.5" cy="12.5" r="0.8" fill="#6b7280"/>
4220
+ </svg>
4221
+ `;
4222
+ Object.assign(indicator.style, {
4223
+ position: "absolute",
4224
+ top: "50%",
4225
+ right: "4px",
4226
+ transform: "translateY(-50%)",
4227
+ width: "24px",
4228
+ height: "24px",
4229
+ display: "flex",
4230
+ alignItems: "center",
4231
+ justifyContent: "center",
4232
+ zIndex: "1000",
4233
+ cursor: "pointer",
4234
+ opacity: "0.7",
4235
+ transition: "opacity 0.2s ease-in-out",
4236
+ pointerEvents: "auto",
4237
+ padding: "3px",
4238
+ borderRadius: "4px"
4239
+ });
4240
+ indicator.addEventListener("mouseenter", () => {
4241
+ indicator.style.opacity = "1";
4242
+ indicator.style.backgroundColor = "#f3f4f6";
4243
+ });
4244
+ indicator.addEventListener("mouseleave", () => {
4245
+ indicator.style.opacity = "0.7";
4246
+ indicator.style.backgroundColor = "transparent";
4247
+ });
4248
+ indicator.addEventListener("keydown", (e) => {
4249
+ if (e.key === "Enter" || e.key === " ") {
4250
+ e.preventDefault();
4251
+ indicator.click();
4252
+ }
4253
+ });
4254
+ const currentPosition = window.getComputedStyle(annotationSection).position;
4255
+ if (currentPosition === "static") {
4256
+ annotationSection.style.position = "relative";
4257
+ }
4258
+ annotationSection.appendChild(indicator);
4259
+ logger.info(`Added date field indicator for: ${fieldName}`);
4260
+ } catch (error) {
4261
+ logger.error("Error adding date field indicator:", error);
4262
+ }
4263
+ }, []);
4264
+ const removeDateFieldIndicator = useCallback((fieldName) => {
4265
+ try {
4266
+ const iframe = iframeRef.current;
4267
+ if (!iframe?.contentDocument) return;
4268
+ const doc = iframe.contentDocument;
4269
+ const formElement = doc.querySelector(
4270
+ `input[name="${fieldName}"], input[data-element-id="${fieldName}"], textarea[name="${fieldName}"], textarea[data-element-id="${fieldName}"]`
4271
+ );
4272
+ if (!formElement) return;
4273
+ const annotationSection = formElement.closest("section");
4274
+ if (!annotationSection) return;
4275
+ const indicator = annotationSection.querySelector(".signiphi-date-field-indicator");
4276
+ if (indicator) {
4277
+ indicator.remove();
4278
+ logger.info(`Removed date field indicator from: ${fieldName}`);
4279
+ }
4280
+ } catch (error) {
4281
+ logger.error("Error removing date field indicator:", error);
4282
+ }
4283
+ }, []);
4284
+ const findFieldElements = useCallback((fieldName) => {
4285
+ const iframe = iframeRef.current;
4286
+ if (!iframe?.contentDocument) return [];
4287
+ const doc = iframe.contentDocument;
4288
+ const targetElements = [];
4289
+ if (fieldName === "signature_field_main") {
4290
+ const allInputs = doc.querySelectorAll("input[name], input[data-element-id]");
4291
+ allInputs.forEach((input) => {
4292
+ const name = input.getAttribute("name") || input.getAttribute("data-element-id") || "";
4293
+ if (name.toLowerCase().includes("signature") && !name.toLowerCase().includes("initials")) {
4294
+ targetElements.push(input);
4295
+ }
4296
+ });
4297
+ } else if (fieldName === "initials_field_main") {
4298
+ const allInputs = doc.querySelectorAll("input[name], input[data-element-id]");
4299
+ allInputs.forEach((input) => {
4300
+ const name = input.getAttribute("name") || input.getAttribute("data-element-id") || "";
4301
+ if (name.toLowerCase().includes("initials")) {
4302
+ targetElements.push(input);
4303
+ }
4304
+ });
4305
+ } else {
4306
+ const formElement = doc.querySelector(
4307
+ `input[name="${fieldName}"], input[data-element-id="${fieldName}"]`
4308
+ );
4309
+ if (formElement) {
4310
+ targetElements.push(formElement);
4311
+ }
4312
+ }
4313
+ return targetElements;
4314
+ }, []);
4315
+ const previewSignature = useCallback((fieldName, dataUrl) => {
4316
+ try {
4317
+ const iframe = iframeRef.current;
4318
+ if (!iframe?.contentDocument) {
4319
+ logger.warn("Cannot preview signature: iframe not ready");
4320
+ return;
4321
+ }
4322
+ const doc = iframe.contentDocument;
4323
+ const targetElements = findFieldElements(fieldName);
4324
+ targetElements.forEach((formElement) => {
4325
+ const annotationSection = formElement.closest("section");
4326
+ if (!annotationSection) return;
4327
+ const existing = annotationSection.querySelector(".signiphi-signature-preview");
4328
+ if (existing) existing.remove();
4329
+ if (!dataUrl) return;
4330
+ const preview = doc.createElement("div");
4331
+ preview.className = "signiphi-signature-preview";
4332
+ preview.setAttribute("aria-hidden", "true");
4333
+ const img = doc.createElement("img");
4334
+ img.src = dataUrl;
4335
+ img.alt = "";
4336
+ Object.assign(img.style, {
4337
+ width: "100%",
4338
+ height: "100%",
4339
+ objectFit: "contain",
4340
+ pointerEvents: "none"
4341
+ });
4342
+ preview.appendChild(img);
4343
+ Object.assign(preview.style, {
4344
+ position: "absolute",
4345
+ top: "0",
4346
+ left: "0",
4347
+ width: "100%",
4348
+ height: "100%",
4349
+ display: "flex",
4350
+ alignItems: "center",
4351
+ justifyContent: "center",
4352
+ zIndex: "999",
4353
+ pointerEvents: "none",
4354
+ padding: "2px",
4355
+ boxSizing: "border-box",
4356
+ backgroundColor: "rgba(255, 255, 255, 0.85)",
4357
+ opacity: "0",
4358
+ transition: "opacity 0.3s ease-in-out"
4359
+ });
4360
+ const currentPosition = annotationSection.style.position || getComputedStyle(annotationSection).position;
4361
+ if (currentPosition === "static" || !currentPosition) {
4362
+ annotationSection.style.position = "relative";
4363
+ }
4364
+ annotationSection.appendChild(preview);
4365
+ requestAnimationFrame(() => {
4366
+ preview.style.opacity = "1";
4367
+ });
4368
+ });
4369
+ logger.info(`Previewed signature on ${targetElements.length} field(s) for: ${fieldName}`);
4370
+ } catch (error) {
4371
+ logger.error("Error previewing signature:", error);
4372
+ }
4373
+ }, [findFieldElements]);
4374
+ const previewInitials = useCallback((fieldName, text) => {
4375
+ try {
4376
+ const iframe = iframeRef.current;
4377
+ if (!iframe?.contentDocument) {
4378
+ logger.warn("Cannot preview initials: iframe not ready");
4379
+ return;
4380
+ }
4381
+ const doc = iframe.contentDocument;
4382
+ const targetElements = findFieldElements(fieldName);
4383
+ targetElements.forEach((formElement) => {
4384
+ const annotationSection = formElement.closest("section");
4385
+ if (!annotationSection) return;
4386
+ const existing = annotationSection.querySelector(".signiphi-initials-preview");
4387
+ if (existing) existing.remove();
4388
+ if (!text) return;
4389
+ const preview = doc.createElement("div");
4390
+ preview.className = "signiphi-initials-preview";
4391
+ preview.setAttribute("aria-hidden", "true");
4392
+ preview.textContent = text;
4393
+ Object.assign(preview.style, {
4394
+ position: "absolute",
4395
+ top: "0",
4396
+ left: "0",
4397
+ width: "100%",
4398
+ height: "100%",
4399
+ display: "flex",
4400
+ alignItems: "center",
4401
+ justifyContent: "center",
4402
+ zIndex: "999",
4403
+ pointerEvents: "none",
4404
+ fontFamily: "'Dancing Script', 'Brush Script MT', cursive",
4405
+ fontStyle: "italic",
4406
+ fontSize: "70%",
4407
+ color: "#000000",
4408
+ backgroundColor: "rgba(255, 255, 255, 0.85)",
4409
+ overflow: "hidden",
4410
+ opacity: "0",
4411
+ transition: "opacity 0.3s ease-in-out"
4412
+ });
4413
+ const currentPosition = annotationSection.style.position || getComputedStyle(annotationSection).position;
4414
+ if (currentPosition === "static" || !currentPosition) {
4415
+ annotationSection.style.position = "relative";
4416
+ }
4417
+ annotationSection.appendChild(preview);
4418
+ requestAnimationFrame(() => {
4419
+ preview.style.opacity = "1";
4420
+ });
4421
+ });
4422
+ logger.info(`Previewed initials on ${targetElements.length} field(s) for: ${fieldName}`);
4423
+ } catch (error) {
4424
+ logger.error("Error previewing initials:", error);
4425
+ }
4426
+ }, [findFieldElements]);
4427
+ const clearFieldPreviews = useCallback((fieldName) => {
4428
+ try {
4429
+ const iframe = iframeRef.current;
4430
+ if (!iframe?.contentDocument) return;
4431
+ const doc = iframe.contentDocument;
4432
+ const selectors = ".signiphi-signature-preview, .signiphi-initials-preview";
4433
+ if (fieldName) {
4434
+ const targetElements = findFieldElements(fieldName);
4435
+ targetElements.forEach((el) => {
4436
+ const section = el.closest("section");
4437
+ if (section) {
4438
+ section.querySelectorAll(selectors).forEach((p) => p.remove());
4439
+ }
4440
+ });
4441
+ } else {
4442
+ doc.querySelectorAll(selectors).forEach((el) => el.remove());
4443
+ }
4444
+ } catch (error) {
4445
+ logger.error("Error clearing field previews:", error);
4446
+ }
4447
+ }, [findFieldElements]);
3418
4448
  useEffect(() => {
3419
4449
  if (!versionChecked) {
3420
4450
  const config = getPdfJsConfig();
@@ -3476,22 +4506,14 @@ Or check your configuration.`;
3476
4506
  await initializationPromise.current;
3477
4507
  isLoadingRef.current = false;
3478
4508
  onLoad?.();
3479
- } else {
3480
- const PDFViewerApplication = getPDFViewerApplication();
3481
- if (PDFViewerApplication) {
3482
- isLoadingRef.current = false;
3483
- onLoad?.();
3484
- } else {
3485
- throw new Error("PDFViewerApplication not found after iframe load");
3486
- }
3487
4509
  }
3488
4510
  } catch (error) {
3489
- const errorMessage = error instanceof Error ? error.message : "Unknown error loading PDF viewer";
3490
- logger.error("Error in PDF.js initialization:", error);
4511
+ const errorMessage = error instanceof Error ? error.message : "Failed to load PDF viewer";
4512
+ logger.error("Error in handleIframeLoad:", errorMessage);
3491
4513
  isLoadingRef.current = false;
3492
4514
  onError?.(errorMessage);
3493
4515
  }
3494
- }, 1e3);
4516
+ }, 100);
3495
4517
  } catch (error) {
3496
4518
  const errorMessage = error instanceof Error ? error.message : "Unknown error in iframe load handler";
3497
4519
  logger.error("Error in iframe load handler:", error);
@@ -3509,6 +4531,8 @@ Or check your configuration.`;
3509
4531
  saveDocument,
3510
4532
  getPDFViewerApplication,
3511
4533
  injectPlaceholders,
4534
+ injectRadioLabels,
4535
+ setFieldMetadata,
3512
4536
  zoomIn,
3513
4537
  zoomOut,
3514
4538
  nextPage,
@@ -3516,10 +4540,16 @@ Or check your configuration.`;
3516
4540
  getCurrentPage,
3517
4541
  getTotalPages,
3518
4542
  attachFieldClickInterceptors,
4543
+ attachFieldChangeListeners,
3519
4544
  addFieldIndicator,
3520
- removeFieldIndicator
4545
+ removeFieldIndicator,
4546
+ addDateFieldIndicator,
4547
+ removeDateFieldIndicator,
4548
+ previewSignature,
4549
+ previewInitials,
4550
+ clearFieldPreviews
3521
4551
  }),
3522
- [loadPdf, getFormFieldValues, setFormFieldValues, getAllFieldNames, saveDocument, getPDFViewerApplication, injectPlaceholders, zoomIn, zoomOut, nextPage, previousPage, getCurrentPage, getTotalPages, attachFieldClickInterceptors, addFieldIndicator, removeFieldIndicator]
4552
+ [loadPdf, getFormFieldValues, setFormFieldValues, getAllFieldNames, saveDocument, getPDFViewerApplication, injectPlaceholders, injectRadioLabels, setFieldMetadata, zoomIn, zoomOut, nextPage, previousPage, getCurrentPage, getTotalPages, attachFieldClickInterceptors, attachFieldChangeListeners, addFieldIndicator, removeFieldIndicator, addDateFieldIndicator, removeDateFieldIndicator, previewSignature, previewInitials, clearFieldPreviews]
3523
4553
  );
3524
4554
  return /* @__PURE__ */ jsx(Fragment, { children: children({ iframeRef, handleIframeLoad }) });
3525
4555
  }
@@ -3808,7 +4838,7 @@ var buttonVariants = cva(
3808
4838
  }
3809
4839
  }
3810
4840
  );
3811
- var Button = React8.forwardRef(
4841
+ var Button = React9.forwardRef(
3812
4842
  ({ className, variant, size, asChild = false, ...props }, ref) => {
3813
4843
  const Comp = asChild ? Slot : "button";
3814
4844
  return /* @__PURE__ */ jsx(
@@ -3916,9 +4946,171 @@ var SignatureCanvas = forwardRef(
3916
4946
  }
3917
4947
  );
3918
4948
  SignatureCanvas.displayName = "SignatureCanvas";
4949
+ var Input = React9.forwardRef(
4950
+ ({ className, type, ...props }, ref) => {
4951
+ return /* @__PURE__ */ jsx(
4952
+ "input",
4953
+ {
4954
+ type,
4955
+ className: cn(
4956
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus:outline-none focus:ring-1 disabled:cursor-not-allowed disabled:opacity-50",
4957
+ className
4958
+ ),
4959
+ ref,
4960
+ ...props
4961
+ }
4962
+ );
4963
+ }
4964
+ );
4965
+ Input.displayName = "Input";
4966
+ function SignatureTypeInput({
4967
+ onSave,
4968
+ onCancel,
4969
+ width = 450,
4970
+ height = 200,
4971
+ initialText = "",
4972
+ initialFont,
4973
+ className
4974
+ }) {
4975
+ const resolvedInitialFont = initialFont ?? DEFAULT_SIGNATURE_FONT;
4976
+ const [text, setText] = useState(initialText);
4977
+ const [selectedFont, setSelectedFont] = useState(resolvedInitialFont);
4978
+ const [fontsReady, setFontsReady] = useState(false);
4979
+ const [previewDataUrl, setPreviewDataUrl] = useState(null);
4980
+ const previewCanvasRef = useRef(null);
4981
+ useEffect(() => {
4982
+ loadSignatureFonts().then(() => {
4983
+ setFontsReady(true);
4984
+ }).catch((error) => {
4985
+ console.error("Failed to load signature fonts:", error);
4986
+ setFontsReady(true);
4987
+ });
4988
+ }, []);
4989
+ useEffect(() => {
4990
+ if (!fontsReady || !text.trim()) {
4991
+ setPreviewDataUrl(null);
4992
+ return;
4993
+ }
4994
+ const timeoutId = setTimeout(() => {
4995
+ try {
4996
+ const dataUrl = generateSignatureFromText({
4997
+ text: text.trim(),
4998
+ fontFamily: selectedFont.family,
4999
+ width,
5000
+ height
5001
+ });
5002
+ setPreviewDataUrl(dataUrl);
5003
+ } catch (error) {
5004
+ console.error("Failed to generate signature preview:", error);
5005
+ setPreviewDataUrl(null);
5006
+ }
5007
+ }, 50);
5008
+ return () => clearTimeout(timeoutId);
5009
+ }, [text, selectedFont, fontsReady, width, height]);
5010
+ const handleTextChange = useCallback(
5011
+ (e) => {
5012
+ setText(e.target.value);
5013
+ },
5014
+ []
5015
+ );
5016
+ const handleFontSelect = useCallback((font) => {
5017
+ setSelectedFont(font);
5018
+ }, []);
5019
+ const handleSave = useCallback(() => {
5020
+ if (!text.trim() || !previewDataUrl) {
5021
+ return;
5022
+ }
5023
+ onSave(previewDataUrl);
5024
+ }, [text, previewDataUrl, onSave]);
5025
+ const hasValidSignature = text.trim().length > 0 && previewDataUrl !== null;
5026
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
5027
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
5028
+ /* @__PURE__ */ jsx(
5029
+ "label",
5030
+ {
5031
+ htmlFor: "signature-text",
5032
+ className: "text-sm font-medium text-foreground",
5033
+ children: "Enter your name:"
5034
+ }
5035
+ ),
5036
+ /* @__PURE__ */ jsx(
5037
+ Input,
5038
+ {
5039
+ id: "signature-text",
5040
+ type: "text",
5041
+ value: text,
5042
+ onChange: handleTextChange,
5043
+ placeholder: "Type your name here",
5044
+ maxLength: 50,
5045
+ autoComplete: "off",
5046
+ className: "text-base"
5047
+ }
5048
+ )
5049
+ ] }),
5050
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
5051
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-foreground", children: "Select style:" }),
5052
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: SIGNATURE_FONTS.map((font) => /* @__PURE__ */ jsx(
5053
+ Button,
5054
+ {
5055
+ type: "button",
5056
+ variant: selectedFont.name === font.name ? "default" : "outline",
5057
+ size: "sm",
5058
+ onClick: () => handleFontSelect(font),
5059
+ className: "text-xs md:text-sm",
5060
+ style: {
5061
+ fontFamily: fontsReady ? `"${font.family}", cursive` : void 0
5062
+ },
5063
+ children: font.label
5064
+ },
5065
+ font.name
5066
+ )) })
5067
+ ] }),
5068
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
5069
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-foreground", children: "Preview:" }),
5070
+ /* @__PURE__ */ jsx(
5071
+ "div",
5072
+ {
5073
+ className: "border-2 border-dashed border-border rounded-lg bg-muted/30 flex items-center justify-center overflow-hidden",
5074
+ style: { minHeight: Math.min(height, 150), maxHeight: height },
5075
+ children: !fontsReady ? /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Loading fonts..." }) : !text.trim() ? /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Start typing to see preview" }) : previewDataUrl ? /* @__PURE__ */ jsx(
5076
+ "img",
5077
+ {
5078
+ src: previewDataUrl,
5079
+ alt: "Signature preview",
5080
+ className: "max-w-full max-h-full object-contain"
5081
+ }
5082
+ ) : /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Generating preview..." })
5083
+ }
5084
+ )
5085
+ ] }),
5086
+ /* @__PURE__ */ jsx("canvas", { ref: previewCanvasRef, className: "hidden" }),
5087
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
5088
+ /* @__PURE__ */ jsx(
5089
+ Button,
5090
+ {
5091
+ type: "button",
5092
+ variant: "outline",
5093
+ onClick: onCancel,
5094
+ className: "flex-1",
5095
+ children: "Cancel"
5096
+ }
5097
+ ),
5098
+ /* @__PURE__ */ jsx(
5099
+ Button,
5100
+ {
5101
+ type: "button",
5102
+ onClick: handleSave,
5103
+ disabled: !hasValidSignature,
5104
+ className: "flex-1 font-semibold",
5105
+ children: "Save Signature"
5106
+ }
5107
+ )
5108
+ ] })
5109
+ ] }) });
5110
+ }
3919
5111
  var Dialog = DialogPrimitive.Root;
3920
5112
  var DialogPortal = DialogPrimitive.Portal;
3921
- var DialogOverlay = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { className: "signiphi-pdf-signer", children: /* @__PURE__ */ jsx(
5113
+ var DialogOverlay = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { className: "signiphi-pdf-signer", children: /* @__PURE__ */ jsx(
3922
5114
  DialogPrimitive.Overlay,
3923
5115
  {
3924
5116
  ref,
@@ -3930,7 +5122,7 @@ var DialogOverlay = React8.forwardRef(({ className, ...props }, ref) => /* @__PU
3930
5122
  }
3931
5123
  ) }));
3932
5124
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
3933
- var DialogContent = React8.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
5125
+ var DialogContent = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
3934
5126
  /* @__PURE__ */ jsx(DialogOverlay, {}),
3935
5127
  /* @__PURE__ */ jsx("div", { className: "signiphi-pdf-signer", children: /* @__PURE__ */ jsxs(
3936
5128
  DialogPrimitive.Content,
@@ -3980,7 +5172,7 @@ var DialogFooter = ({
3980
5172
  }
3981
5173
  );
3982
5174
  DialogFooter.displayName = "DialogFooter";
3983
- var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5175
+ var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3984
5176
  DialogPrimitive.Title,
3985
5177
  {
3986
5178
  ref,
@@ -3992,7 +5184,7 @@ var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
3992
5184
  }
3993
5185
  ));
3994
5186
  DialogTitle.displayName = DialogPrimitive.Title.displayName;
3995
- var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5187
+ var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3996
5188
  DialogPrimitive.Description,
3997
5189
  {
3998
5190
  ref,
@@ -4008,6 +5200,7 @@ function SignatureModalCore({
4008
5200
  fieldLabel
4009
5201
  }) {
4010
5202
  const canvasRef = useRef(null);
5203
+ const [activeTab, setActiveTab] = useState("draw");
4011
5204
  const [uploadedImage, setUploadedImage] = useState(null);
4012
5205
  const [uploadError, setUploadError] = useState(null);
4013
5206
  const fileInputRef = useRef(null);
@@ -4065,10 +5258,30 @@ function SignatureModalCore({
4065
5258
  onClose();
4066
5259
  }
4067
5260
  }, [uploadedImage, onSave, onClose]);
5261
+ const handleTabChange = useCallback((tab) => {
5262
+ setActiveTab(tab);
5263
+ }, []);
4068
5264
  const canvasWidth = 450;
4069
5265
  const canvasHeight = 200;
4070
5266
  const mobileCanvasWidth = 320;
4071
5267
  const mobileCanvasHeight = 150;
5268
+ const tabs = [
5269
+ {
5270
+ id: "draw",
5271
+ label: "Draw",
5272
+ icon: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" }) })
5273
+ },
5274
+ {
5275
+ id: "upload",
5276
+ label: "Upload",
5277
+ icon: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" }) })
5278
+ },
5279
+ {
5280
+ id: "type",
5281
+ label: "Type",
5282
+ icon: /* @__PURE__ */ jsx("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" }) })
5283
+ }
5284
+ ];
4072
5285
  return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: /* @__PURE__ */ jsxs(DialogContent, { children: [
4073
5286
  /* @__PURE__ */ jsxs(DialogHeader, { className: "space-y-2 md:space-y-3", children: [
4074
5287
  /* @__PURE__ */ jsx(DialogTitle, { className: "text-lg md:text-xl font-semibold", children: "Sign Document" }),
@@ -4078,110 +5291,163 @@ function SignatureModalCore({
4078
5291
  ] })
4079
5292
  ] }),
4080
5293
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 md:gap-6", children: [
4081
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
4082
- /* @__PURE__ */ jsxs(
4083
- Button,
4084
- {
4085
- type: "button",
4086
- variant: "outline",
4087
- onClick: handleUploadClick,
4088
- className: "flex-1 text-xs md:text-sm h-9 md:h-10",
4089
- children: [
4090
- /* @__PURE__ */ jsx("svg", { className: "w-3.5 h-3.5 md:w-4 md:h-4 mr-1.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" }) }),
4091
- "Upload Image"
4092
- ]
4093
- }
4094
- ),
4095
- /* @__PURE__ */ jsx(
4096
- "input",
5294
+ /* @__PURE__ */ jsx("div", { className: "flex gap-1 p-1 bg-muted rounded-lg", children: tabs.map((tab) => /* @__PURE__ */ jsxs(
5295
+ Button,
5296
+ {
5297
+ type: "button",
5298
+ variant: activeTab === tab.id ? "default" : "ghost",
5299
+ size: "sm",
5300
+ onClick: () => handleTabChange(tab.id),
5301
+ className: `flex-1 gap-1.5 text-xs md:text-sm ${activeTab === tab.id ? "" : "hover:bg-background/50"}`,
5302
+ children: [
5303
+ tab.icon,
5304
+ tab.label
5305
+ ]
5306
+ },
5307
+ tab.id
5308
+ )) }),
5309
+ activeTab === "draw" && /* @__PURE__ */ jsxs(Fragment, { children: [
5310
+ /* @__PURE__ */ jsx("div", { className: "block sm:hidden", children: /* @__PURE__ */ jsx(
5311
+ SignatureCanvas,
4097
5312
  {
4098
- ref: fileInputRef,
4099
- type: "file",
4100
- accept: "image/png,image/jpeg,image/jpg",
4101
- className: "hidden",
4102
- onChange: handleFileChange
5313
+ ref: canvasRef,
5314
+ width: mobileCanvasWidth,
5315
+ height: mobileCanvasHeight,
5316
+ title: "Sign Document",
5317
+ onSignature: handleSave,
5318
+ onCancel: handleCancel,
5319
+ showActions: true
4103
5320
  }
4104
- ),
4105
- uploadedImage && /* @__PURE__ */ jsx(
4106
- Button,
5321
+ ) }),
5322
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsx(
5323
+ SignatureCanvas,
4107
5324
  {
4108
- type: "button",
4109
- variant: "outline",
4110
- onClick: handleClearUpload,
4111
- className: "text-xs md:text-sm h-9 md:h-10",
4112
- children: "Clear"
5325
+ ref: canvasRef,
5326
+ width: canvasWidth,
5327
+ height: canvasHeight,
5328
+ title: "Sign Document",
5329
+ onSignature: handleSave,
5330
+ onCancel: handleCancel,
5331
+ showActions: true
4113
5332
  }
4114
- )
4115
- ] }),
4116
- !uploadedImage && /* @__PURE__ */ jsx("p", { className: "text-xs md:text-sm text-muted-foreground text-center -mt-2 md:-mt-4", children: "Supported formats: PNG, JPEG \u2022 Maximum size: 5MB" }),
4117
- uploadError && /* @__PURE__ */ jsxs("div", { className: "bg-destructive/10 border border-destructive/30 text-destructive text-xs md:text-sm p-2 md:p-3 rounded-lg flex items-start gap-2", children: [
4118
- /* @__PURE__ */ jsx("svg", { className: "w-3.5 h-3.5 md:w-4 md:h-4 mt-0.5 flex-shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
4119
- /* @__PURE__ */ jsx("span", { children: uploadError })
5333
+ ) })
4120
5334
  ] }),
4121
- uploadedImage ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:gap-4", children: [
4122
- /* @__PURE__ */ jsx("div", { className: "border-2 border-primary/20 rounded-lg p-3 md:p-6 flex justify-center bg-muted/20", children: /* @__PURE__ */ jsx(
4123
- "img",
4124
- {
4125
- src: uploadedImage,
4126
- alt: "Uploaded signature preview",
4127
- className: "max-w-full",
4128
- style: {
4129
- maxWidth: canvasWidth,
4130
- maxHeight: canvasHeight,
4131
- objectFit: "contain"
4132
- }
4133
- }
4134
- ) }),
4135
- /* @__PURE__ */ jsx(Button, { onClick: handleSaveUpload, size: "lg", className: "font-semibold text-sm md:text-base h-10 md:h-11", children: "Save Signature" })
4136
- ] }) : /* @__PURE__ */ jsx("div", { className: "block sm:hidden", children: /* @__PURE__ */ jsx(
4137
- SignatureCanvas,
5335
+ activeTab === "upload" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
5336
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
5337
+ /* @__PURE__ */ jsxs(
5338
+ Button,
5339
+ {
5340
+ type: "button",
5341
+ variant: "outline",
5342
+ onClick: handleUploadClick,
5343
+ className: "flex-1 text-xs md:text-sm h-9 md:h-10",
5344
+ children: [
5345
+ /* @__PURE__ */ jsx("svg", { className: "w-3.5 h-3.5 md:w-4 md:h-4 mr-1.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" }) }),
5346
+ "Choose Image"
5347
+ ]
5348
+ }
5349
+ ),
5350
+ /* @__PURE__ */ jsx(
5351
+ "input",
5352
+ {
5353
+ ref: fileInputRef,
5354
+ type: "file",
5355
+ accept: "image/png,image/jpeg,image/jpg",
5356
+ className: "hidden",
5357
+ onChange: handleFileChange
5358
+ }
5359
+ ),
5360
+ uploadedImage && /* @__PURE__ */ jsx(
5361
+ Button,
5362
+ {
5363
+ type: "button",
5364
+ variant: "outline",
5365
+ onClick: handleClearUpload,
5366
+ className: "text-xs md:text-sm h-9 md:h-10",
5367
+ children: "Clear"
5368
+ }
5369
+ )
5370
+ ] }),
5371
+ !uploadedImage && /* @__PURE__ */ jsx("p", { className: "text-xs md:text-sm text-muted-foreground text-center", children: "Supported formats: PNG, JPEG (Max 5MB)" }),
5372
+ uploadError && /* @__PURE__ */ jsxs("div", { className: "bg-destructive/10 border border-destructive/30 text-destructive text-xs md:text-sm p-2 md:p-3 rounded-lg flex items-start gap-2", children: [
5373
+ /* @__PURE__ */ jsx("svg", { className: "w-3.5 h-3.5 md:w-4 md:h-4 mt-0.5 flex-shrink-0", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
5374
+ /* @__PURE__ */ jsx("span", { children: uploadError })
5375
+ ] }),
5376
+ uploadedImage ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:gap-4", children: [
5377
+ /* @__PURE__ */ jsx("div", { className: "border-2 border-primary/20 rounded-lg p-3 md:p-6 flex justify-center bg-muted/20", children: /* @__PURE__ */ jsx(
5378
+ "img",
5379
+ {
5380
+ src: uploadedImage,
5381
+ alt: "Uploaded signature preview",
5382
+ className: "max-w-full",
5383
+ style: {
5384
+ maxWidth: canvasWidth,
5385
+ maxHeight: canvasHeight,
5386
+ objectFit: "contain"
5387
+ }
5388
+ }
5389
+ ) }),
5390
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
5391
+ /* @__PURE__ */ jsx(
5392
+ Button,
5393
+ {
5394
+ type: "button",
5395
+ variant: "outline",
5396
+ onClick: handleCancel,
5397
+ className: "flex-1",
5398
+ children: "Cancel"
5399
+ }
5400
+ ),
5401
+ /* @__PURE__ */ jsx(
5402
+ Button,
5403
+ {
5404
+ onClick: handleSaveUpload,
5405
+ className: "flex-1 font-semibold",
5406
+ children: "Save Signature"
5407
+ }
5408
+ )
5409
+ ] })
5410
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:gap-4", children: [
5411
+ /* @__PURE__ */ jsx(
5412
+ "div",
5413
+ {
5414
+ className: "border-2 border-dashed border-border rounded-lg flex items-center justify-center bg-muted/30",
5415
+ style: { minHeight: mobileCanvasHeight },
5416
+ children: /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Upload an image of your signature" })
5417
+ }
5418
+ ),
5419
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
5420
+ /* @__PURE__ */ jsx(
5421
+ Button,
5422
+ {
5423
+ type: "button",
5424
+ variant: "outline",
5425
+ onClick: handleCancel,
5426
+ className: "flex-1",
5427
+ children: "Cancel"
5428
+ }
5429
+ ),
5430
+ /* @__PURE__ */ jsx(Button, { disabled: true, className: "flex-1 font-semibold", children: "Save Signature" })
5431
+ ] })
5432
+ ] })
5433
+ ] }),
5434
+ activeTab === "type" && /* @__PURE__ */ jsx(
5435
+ SignatureTypeInput,
4138
5436
  {
4139
- ref: canvasRef,
4140
- width: mobileCanvasWidth,
4141
- height: mobileCanvasHeight,
4142
- title: "Sign Document",
4143
- onSignature: handleSave,
5437
+ onSave: handleSave,
4144
5438
  onCancel: handleCancel,
4145
- showActions: true
4146
- }
4147
- ) }),
4148
- !uploadedImage && /* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsx(
4149
- SignatureCanvas,
4150
- {
4151
- ref: canvasRef,
4152
5439
  width: canvasWidth,
4153
- height: canvasHeight,
4154
- title: "Sign Document",
4155
- onSignature: handleSave,
4156
- onCancel: handleCancel,
4157
- showActions: true
5440
+ height: canvasHeight
4158
5441
  }
4159
- ) })
5442
+ )
4160
5443
  ] })
4161
5444
  ] }) });
4162
5445
  }
4163
5446
  var SignatureModal = SignatureModalCore;
4164
- var Input = React8.forwardRef(
4165
- ({ className, type, ...props }, ref) => {
4166
- return /* @__PURE__ */ jsx(
4167
- "input",
4168
- {
4169
- type,
4170
- className: cn(
4171
- "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus:outline-none focus:ring-1 disabled:cursor-not-allowed disabled:opacity-50",
4172
- className
4173
- ),
4174
- ref,
4175
- ...props
4176
- }
4177
- );
4178
- }
4179
- );
4180
- Input.displayName = "Input";
4181
5447
  var labelVariants = cva(
4182
5448
  "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
4183
5449
  );
4184
- var Label = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5450
+ var Label = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4185
5451
  LabelPrimitive.Root,
4186
5452
  {
4187
5453
  ref,
@@ -4262,7 +5528,7 @@ function InitialsModal({
4262
5528
  ] })
4263
5529
  ] }) });
4264
5530
  }
4265
- var Card = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5531
+ var Card = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4266
5532
  "div",
4267
5533
  {
4268
5534
  ref,
@@ -4274,7 +5540,7 @@ var Card = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ j
4274
5540
  }
4275
5541
  ));
4276
5542
  Card.displayName = "Card";
4277
- var CardHeader = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5543
+ var CardHeader = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4278
5544
  "div",
4279
5545
  {
4280
5546
  ref,
@@ -4283,7 +5549,7 @@ var CardHeader = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE_
4283
5549
  }
4284
5550
  ));
4285
5551
  CardHeader.displayName = "CardHeader";
4286
- var CardTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5552
+ var CardTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4287
5553
  "div",
4288
5554
  {
4289
5555
  ref,
@@ -4292,7 +5558,7 @@ var CardTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__
4292
5558
  }
4293
5559
  ));
4294
5560
  CardTitle.displayName = "CardTitle";
4295
- var CardDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5561
+ var CardDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4296
5562
  "div",
4297
5563
  {
4298
5564
  ref,
@@ -4301,9 +5567,9 @@ var CardDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__
4301
5567
  }
4302
5568
  ));
4303
5569
  CardDescription.displayName = "CardDescription";
4304
- var CardContent = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("px-6", className), ...props }));
5570
+ var CardContent = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("px-6", className), ...props }));
4305
5571
  CardContent.displayName = "CardContent";
4306
- var CardFooter = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5572
+ var CardFooter = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4307
5573
  "div",
4308
5574
  {
4309
5575
  ref,
@@ -4466,7 +5732,7 @@ function TextFieldRenderer({
4466
5732
  }
4467
5733
  var Select = SelectPrimitive.Root;
4468
5734
  var SelectValue = SelectPrimitive.Value;
4469
- var SelectTrigger = React8.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
5735
+ var SelectTrigger = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
4470
5736
  SelectPrimitive.Trigger,
4471
5737
  {
4472
5738
  ref,
@@ -4482,7 +5748,7 @@ var SelectTrigger = React8.forwardRef(({ className, children, ...props }, ref) =
4482
5748
  }
4483
5749
  ));
4484
5750
  SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
4485
- var SelectScrollUpButton = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5751
+ var SelectScrollUpButton = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4486
5752
  SelectPrimitive.ScrollUpButton,
4487
5753
  {
4488
5754
  ref,
@@ -4495,7 +5761,7 @@ var SelectScrollUpButton = React8.forwardRef(({ className, ...props }, ref) => /
4495
5761
  }
4496
5762
  ));
4497
5763
  SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
4498
- var SelectScrollDownButton = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5764
+ var SelectScrollDownButton = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4499
5765
  SelectPrimitive.ScrollDownButton,
4500
5766
  {
4501
5767
  ref,
@@ -4508,37 +5774,53 @@ var SelectScrollDownButton = React8.forwardRef(({ className, ...props }, ref) =>
4508
5774
  }
4509
5775
  ));
4510
5776
  SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
4511
- var SelectContent = React8.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
4512
- SelectPrimitive.Content,
4513
- {
4514
- ref,
4515
- className: cn(
4516
- "signiphi-pdf-signer",
4517
- // Add scoping class to portal content
4518
- "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
4519
- position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
4520
- className
4521
- ),
4522
- position,
4523
- ...props,
4524
- children: [
4525
- /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
4526
- /* @__PURE__ */ jsx(
4527
- SelectPrimitive.Viewport,
4528
- {
4529
- className: cn(
4530
- "p-1",
4531
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
4532
- ),
4533
- children
4534
- }
5777
+ var SelectContent = React9.forwardRef(({ className, children, position = "popper", ...props }, ref) => {
5778
+ const mergedRef = React9.useCallback(
5779
+ (node) => {
5780
+ if (node) {
5781
+ node.style.setProperty("z-index", "10001", "important");
5782
+ }
5783
+ if (typeof ref === "function") {
5784
+ ref(node);
5785
+ } else if (ref) {
5786
+ ref.current = node;
5787
+ }
5788
+ },
5789
+ [ref]
5790
+ );
5791
+ return /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
5792
+ SelectPrimitive.Content,
5793
+ {
5794
+ ref: mergedRef,
5795
+ className: cn(
5796
+ "signiphi-pdf-signer",
5797
+ // Add scoping class to portal content
5798
+ "max-h-96 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
5799
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
5800
+ className
4535
5801
  ),
4536
- /* @__PURE__ */ jsx(SelectScrollDownButton, {})
4537
- ]
4538
- }
4539
- ) }));
5802
+ position,
5803
+ style: { backgroundColor: "var(--sps-popover, white)", ...props.style },
5804
+ ...props,
5805
+ children: [
5806
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
5807
+ /* @__PURE__ */ jsx(
5808
+ SelectPrimitive.Viewport,
5809
+ {
5810
+ className: cn(
5811
+ "p-1",
5812
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
5813
+ ),
5814
+ children
5815
+ }
5816
+ ),
5817
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
5818
+ ]
5819
+ }
5820
+ ) });
5821
+ });
4540
5822
  SelectContent.displayName = SelectPrimitive.Content.displayName;
4541
- var SelectLabel = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5823
+ var SelectLabel = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4542
5824
  SelectPrimitive.Label,
4543
5825
  {
4544
5826
  ref,
@@ -4547,7 +5829,7 @@ var SelectLabel = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
4547
5829
  }
4548
5830
  ));
4549
5831
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
4550
- var SelectItem = React8.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
5832
+ var SelectItem = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
4551
5833
  SelectPrimitive.Item,
4552
5834
  {
4553
5835
  ref,
@@ -4563,7 +5845,7 @@ var SelectItem = React8.forwardRef(({ className, children, ...props }, ref) => /
4563
5845
  }
4564
5846
  ));
4565
5847
  SelectItem.displayName = SelectPrimitive.Item.displayName;
4566
- var SelectSeparator = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5848
+ var SelectSeparator = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4567
5849
  SelectPrimitive.Separator,
4568
5850
  {
4569
5851
  ref,
@@ -4694,7 +5976,7 @@ function Calendar({
4694
5976
  Calendar.displayName = "Calendar";
4695
5977
  var Popover = PopoverPrimitive.Root;
4696
5978
  var PopoverTrigger = PopoverPrimitive.Trigger;
4697
- var PopoverContent = React8.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
5979
+ var PopoverContent = React9.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
4698
5980
  PopoverPrimitive.Content,
4699
5981
  {
4700
5982
  ref,
@@ -4706,6 +5988,7 @@ var PopoverContent = React8.forwardRef(({ className, align = "center", sideOffse
4706
5988
  "z-50 w-72 rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
4707
5989
  className
4708
5990
  ),
5991
+ style: { backgroundColor: "var(--sps-popover, white)", ...props.style },
4709
5992
  ...props
4710
5993
  }
4711
5994
  ) }));
@@ -4811,10 +6094,10 @@ function DateFieldRenderer({
4811
6094
  "aria-required": field.required,
4812
6095
  children: hasInvalidDate ? /* @__PURE__ */ jsxs(Fragment, { children: [
4813
6096
  /* @__PURE__ */ jsx(AlertCircle, { className: "mr-2 h-4 w-4 text-destructive" }),
4814
- /* @__PURE__ */ jsx("span", { className: "text-destructive", children: invalidDateValue })
6097
+ /* @__PURE__ */ jsx("span", { className: "text-destructive text-sm", children: invalidDateValue })
4815
6098
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
4816
6099
  /* @__PURE__ */ jsx(Calendar$1, { className: "mr-2 h-4 w-4" }),
4817
- date ? format(date, "PPP") : /* @__PURE__ */ jsx("span", { children: "Pick a date" })
6100
+ date ? /* @__PURE__ */ jsx("span", { className: "text-sm", children: format(date, "PPP") }) : /* @__PURE__ */ jsx("span", { className: "text-sm", children: "Pick a date" })
4818
6101
  ] })
4819
6102
  }
4820
6103
  ) }),
@@ -4847,7 +6130,7 @@ function DateFieldRenderer({
4847
6130
  hasInvalidDate && !displayError && /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground mt-1", children: "Invalid date format. Please use the date picker to select a valid date." })
4848
6131
  ] });
4849
6132
  }
4850
- var Checkbox = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
6133
+ var Checkbox = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4851
6134
  CheckboxPrimitive.Root,
4852
6135
  {
4853
6136
  ref,
@@ -4910,7 +6193,7 @@ function CheckboxRenderer({
4910
6193
  error && /* @__PURE__ */ jsx("div", { className: "text-sm text-destructive mt-1", children: error })
4911
6194
  ] });
4912
6195
  }
4913
- var RadioGroup = React8.forwardRef(({ className, ...props }, ref) => {
6196
+ var RadioGroup = React9.forwardRef(({ className, ...props }, ref) => {
4914
6197
  return /* @__PURE__ */ jsx(
4915
6198
  RadioGroupPrimitive.Root,
4916
6199
  {
@@ -4921,7 +6204,7 @@ var RadioGroup = React8.forwardRef(({ className, ...props }, ref) => {
4921
6204
  );
4922
6205
  });
4923
6206
  RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
4924
- var RadioGroupItem = React8.forwardRef(({ className, ...props }, ref) => {
6207
+ var RadioGroupItem = React9.forwardRef(({ className, ...props }, ref) => {
4925
6208
  return /* @__PURE__ */ jsx(
4926
6209
  RadioGroupPrimitive.Item,
4927
6210
  {
@@ -4944,12 +6227,22 @@ function RadioGroupRenderer({
4944
6227
  className = ""
4945
6228
  }) {
4946
6229
  const options = field.options || [];
6230
+ const [localValue, setLocalValue] = useState(value);
6231
+ useEffect(() => {
6232
+ if (value && value !== localValue) {
6233
+ setLocalValue(value);
6234
+ }
6235
+ }, [value]);
6236
+ const handleChange = (newValue) => {
6237
+ setLocalValue(newValue);
6238
+ onChange(newValue);
6239
+ };
4947
6240
  return /* @__PURE__ */ jsxs("div", { className: cn("w-full", className), children: [
4948
6241
  /* @__PURE__ */ jsx(
4949
6242
  RadioGroup,
4950
6243
  {
4951
- value,
4952
- onValueChange: onChange,
6244
+ value: localValue,
6245
+ onValueChange: handleChange,
4953
6246
  required: field.required,
4954
6247
  className: "",
4955
6248
  children: options.map((option) => {
@@ -4958,7 +6251,7 @@ function RadioGroupRenderer({
4958
6251
  "div",
4959
6252
  {
4960
6253
  className: "flex items-center space-x-3 p-3 -ml-3 -mr-3 rounded-lg hover:bg-muted/50 cursor-pointer transition-colors duration-200",
4961
- onClick: () => onChange(option),
6254
+ onClick: () => handleChange(option),
4962
6255
  children: [
4963
6256
  /* @__PURE__ */ jsx(
4964
6257
  RadioGroupItem,
@@ -5443,11 +6736,21 @@ function AttachmentUpload({
5443
6736
  disabled = false,
5444
6737
  maxFiles = 10,
5445
6738
  formatSize,
5446
- className = ""
6739
+ className = "",
6740
+ constraints,
6741
+ validationErrors,
6742
+ onClearErrors
5447
6743
  }) {
5448
6744
  const [isDragging, setIsDragging] = useState(false);
5449
6745
  const fileInputRef = useRef(null);
5450
6746
  const dragCounterRef = useRef(0);
6747
+ const acceptString = useMemo(() => {
6748
+ if (!constraints) return void 0;
6749
+ const types = constraints.allowedTypes ?? [];
6750
+ const extensions = constraints.allowedExtensions ?? [];
6751
+ const combined = [...types, ...extensions].join(",");
6752
+ return combined || void 0;
6753
+ }, [constraints]);
5451
6754
  const handleFileSelect = useCallback(
5452
6755
  (event) => {
5453
6756
  const files = event.target.files;
@@ -5458,6 +6761,11 @@ function AttachmentUpload({
5458
6761
  },
5459
6762
  [onAdd]
5460
6763
  );
6764
+ const openFilePicker = useCallback(() => {
6765
+ if (!disabled && !isUploading) {
6766
+ fileInputRef.current?.click();
6767
+ }
6768
+ }, [disabled, isUploading]);
5461
6769
  const handleDragEnter = useCallback((event) => {
5462
6770
  event.preventDefault();
5463
6771
  event.stopPropagation();
@@ -5492,6 +6800,36 @@ function AttachmentUpload({
5492
6800
  },
5493
6801
  [disabled, isUploading, onAdd]
5494
6802
  );
6803
+ const handlePaste = useCallback(
6804
+ (event) => {
6805
+ if (disabled || isUploading) return;
6806
+ const items = event.clipboardData?.items;
6807
+ if (!items) return;
6808
+ const files = [];
6809
+ for (let i = 0; i < items.length; i++) {
6810
+ const item = items[i];
6811
+ if (item.kind === "file") {
6812
+ const file = item.getAsFile();
6813
+ if (file) files.push(file);
6814
+ }
6815
+ }
6816
+ if (files.length > 0) {
6817
+ event.preventDefault();
6818
+ onAdd(files);
6819
+ }
6820
+ },
6821
+ [disabled, isUploading, onAdd]
6822
+ );
6823
+ const handleKeyDown = useCallback(
6824
+ (event) => {
6825
+ if (disabled || isUploading) return;
6826
+ if (event.key === "Enter" || event.key === " ") {
6827
+ event.preventDefault();
6828
+ fileInputRef.current?.click();
6829
+ }
6830
+ },
6831
+ [disabled, isUploading]
6832
+ );
5495
6833
  const getFileIcon = (fileType) => {
5496
6834
  if (fileType.startsWith("image/")) {
5497
6835
  return /* @__PURE__ */ jsx(Image, { className: "w-6 h-6" });
@@ -5502,15 +6840,22 @@ function AttachmentUpload({
5502
6840
  return /* @__PURE__ */ jsx(File, { className: "w-6 h-6" });
5503
6841
  };
5504
6842
  const canAddMore = attachments.length < maxFiles;
5505
- return /* @__PURE__ */ jsxs("div", { className: `space-y-3 md:space-y-4 ${className}`, children: [
6843
+ const totalSize = attachments.reduce((sum, att) => sum + att.size, 0);
6844
+ return /* @__PURE__ */ jsxs("div", { className: `space-y-3 md:space-y-4 ${className}`, onPaste: handlePaste, children: [
5506
6845
  canAddMore && /* @__PURE__ */ jsxs(
5507
6846
  "div",
5508
6847
  {
6848
+ onClick: openFilePicker,
6849
+ onKeyDown: handleKeyDown,
5509
6850
  onDragEnter: handleDragEnter,
5510
6851
  onDragLeave: handleDragLeave,
5511
6852
  onDragOver: handleDragOver,
5512
6853
  onDrop: handleDrop,
5513
- className: `relative border-2 border-dashed rounded-lg p-4 md:p-8 text-center transition-colors ${isDragging ? "border-primary bg-primary/10" : disabled ? "border-border bg-muted cursor-not-allowed" : "border-border bg-background hover:border-border/80"}`,
6854
+ role: "button",
6855
+ tabIndex: disabled ? -1 : 0,
6856
+ "aria-label": `Upload files. ${attachments.length} of ${maxFiles} files attached.`,
6857
+ "aria-disabled": disabled || isUploading,
6858
+ className: `relative border-2 border-dashed rounded-lg p-4 md:p-8 text-center transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 ${isDragging ? "border-primary bg-primary/10 ring-2 ring-primary/30 scale-[1.01]" : disabled ? "border-border bg-muted cursor-not-allowed" : "border-border bg-background hover:border-primary/40 hover:bg-primary/5 cursor-pointer"}`,
5514
6859
  children: [
5515
6860
  /* @__PURE__ */ jsx(
5516
6861
  "input",
@@ -5518,6 +6863,7 @@ function AttachmentUpload({
5518
6863
  ref: fileInputRef,
5519
6864
  type: "file",
5520
6865
  multiple: true,
6866
+ accept: acceptString,
5521
6867
  onChange: handleFileSelect,
5522
6868
  disabled: disabled || isUploading,
5523
6869
  className: "hidden",
@@ -5540,20 +6886,11 @@ function AttachmentUpload({
5540
6886
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
5541
6887
  /* @__PURE__ */ jsx("p", { className: "text-xs md:text-sm font-medium text-foreground", children: isDragging ? "Drop files here" : "Upload Attachments" }),
5542
6888
  /* @__PURE__ */ jsxs("p", { className: "text-xs md:text-sm text-muted-foreground px-2", children: [
5543
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Drag and drop or " }),
5544
- /* @__PURE__ */ jsxs(
5545
- "button",
5546
- {
5547
- type: "button",
5548
- onClick: () => fileInputRef.current?.click(),
5549
- disabled: disabled || isUploading,
5550
- className: "text-primary hover:text-primary/80 font-medium focus:outline-none focus:underline disabled:text-muted-foreground disabled:cursor-not-allowed",
5551
- children: [
5552
- /* @__PURE__ */ jsx("span", { className: "sm:hidden", children: "Tap to " }),
5553
- "browse files"
5554
- ]
5555
- }
5556
- )
6889
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Drag and drop, paste, or " }),
6890
+ /* @__PURE__ */ jsxs("span", { className: "text-primary font-medium", children: [
6891
+ /* @__PURE__ */ jsx("span", { className: "sm:hidden", children: "Tap to " }),
6892
+ "browse files"
6893
+ ] })
5557
6894
  ] }),
5558
6895
  /* @__PURE__ */ jsxs("p", { className: "text-xs md:text-sm text-muted-foreground", children: [
5559
6896
  "PDF, Word, Images \u2022 Max ",
@@ -5569,13 +6906,34 @@ function AttachmentUpload({
5569
6906
  ]
5570
6907
  }
5571
6908
  ),
6909
+ validationErrors && validationErrors.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 p-2 md:p-3 bg-destructive/10 border border-destructive/30 rounded-lg text-destructive", children: [
6910
+ /* @__PURE__ */ jsx(AlertCircle, { className: "w-4 h-4 mt-0.5 flex-shrink-0" }),
6911
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx("ul", { className: "list-disc list-inside text-xs md:text-sm space-y-0.5", children: validationErrors.map((error, index) => /* @__PURE__ */ jsx("li", { children: error }, index)) }) }),
6912
+ onClearErrors && /* @__PURE__ */ jsx(
6913
+ "button",
6914
+ {
6915
+ type: "button",
6916
+ onClick: onClearErrors,
6917
+ className: "flex-shrink-0 p-0.5 rounded text-destructive/70 hover:text-destructive transition-colors",
6918
+ "aria-label": "Dismiss errors",
6919
+ children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
6920
+ }
6921
+ )
6922
+ ] }),
5572
6923
  attachments.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
5573
- /* @__PURE__ */ jsxs("p", { className: "text-xs md:text-sm font-medium text-foreground", children: [
5574
- "Attached Files (",
5575
- attachments.length,
5576
- "/",
5577
- maxFiles,
5578
- ")"
6924
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
6925
+ /* @__PURE__ */ jsxs("p", { className: "text-xs md:text-sm font-medium text-foreground", children: [
6926
+ "Attached Files (",
6927
+ attachments.length,
6928
+ "/",
6929
+ maxFiles,
6930
+ ")"
6931
+ ] }),
6932
+ constraints && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
6933
+ formatSize(totalSize),
6934
+ " / ",
6935
+ formatSize(constraints.maxTotalSize)
6936
+ ] })
5579
6937
  ] }),
5580
6938
  /* @__PURE__ */ jsx("div", { className: "space-y-2", children: attachments.map((attachment) => /* @__PURE__ */ jsxs(
5581
6939
  "div",
@@ -5685,39 +7043,37 @@ function RequiredFieldNavigation({
5685
7043
  className = ""
5686
7044
  }) {
5687
7045
  if (totalRequired === 0) return null;
5688
- return /* @__PURE__ */ jsxs("div", { className: `flex items-center gap-2 ${className}`, children: [
5689
- /* @__PURE__ */ jsx(Star, { size: 16, className: "text-amber-500" }),
5690
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Required Fields" }),
5691
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
5692
- /* @__PURE__ */ jsx(
5693
- Button,
5694
- {
5695
- variant: "ghost",
5696
- size: "icon",
5697
- onClick: onPrevious,
5698
- className: "h-8 w-8",
5699
- "aria-label": "Previous required field",
5700
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 16 })
5701
- }
5702
- ),
5703
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground px-2", children: [
5704
- currentIndex + 1,
5705
- " / ",
5706
- totalRequired
5707
- ] }),
5708
- /* @__PURE__ */ jsx(
5709
- Button,
5710
- {
5711
- variant: "ghost",
5712
- size: "icon",
5713
- onClick: onNext,
5714
- className: "h-8 w-8",
5715
- "aria-label": "Next required field",
5716
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 16 })
5717
- }
5718
- )
5719
- ] })
5720
- ] });
7046
+ return /* @__PURE__ */ jsx("div", { className: `flex items-center gap-2 ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
7047
+ /* @__PURE__ */ jsx(
7048
+ Button,
7049
+ {
7050
+ variant: "ghost",
7051
+ size: "icon",
7052
+ onClick: onPrevious,
7053
+ className: "h-8 w-8",
7054
+ "aria-label": "Previous required field",
7055
+ children: /* @__PURE__ */ jsx(ChevronLeft, { size: 16 })
7056
+ }
7057
+ ),
7058
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground px-2", children: [
7059
+ currentIndex < 0 ? 0 : currentIndex + 1,
7060
+ " / ",
7061
+ totalRequired
7062
+ ] }),
7063
+ /* @__PURE__ */ jsxs(
7064
+ Button,
7065
+ {
7066
+ variant: "ghost",
7067
+ onClick: onNext,
7068
+ className: "h-8 px-3 gap-1.5",
7069
+ "aria-label": "Next required field",
7070
+ children: [
7071
+ /* @__PURE__ */ jsx(ChevronRight, { size: 16 }),
7072
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Required Field" })
7073
+ ]
7074
+ }
7075
+ )
7076
+ ] }) });
5721
7077
  }
5722
7078
  function ViewToggleToolbar({
5723
7079
  currentView,
@@ -5906,7 +7262,51 @@ function ViewToggleToolbar({
5906
7262
  }
5907
7263
  )
5908
7264
  ] }),
5909
- showPdfControlsGroup && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 sm:gap-3 flex-shrink-0 w-full sm:w-auto justify-center sm:justify-start", children: [
7265
+ showPdfControlsGroup && isViewerReady && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 sm:gap-1 bg-muted/60 rounded-lg p-0.5 sm:p-1 border border-border/50 shadow-sm", children: [
7266
+ /* @__PURE__ */ jsx(
7267
+ Button,
7268
+ {
7269
+ type: "button",
7270
+ variant: "ghost",
7271
+ size: "icon",
7272
+ onClick: handlePreviousPage,
7273
+ disabled: !isViewerReady || currentPage === null || currentPage <= 1,
7274
+ className: cn(
7275
+ "h-7 w-7 sm:h-8 sm:w-8 rounded-md transition-all duration-200",
7276
+ "hover:bg-background/80 hover:shadow-sm",
7277
+ "disabled:opacity-40 disabled:cursor-not-allowed",
7278
+ "active:scale-95"
7279
+ ),
7280
+ "aria-label": "Previous page",
7281
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-3.5 w-3.5 sm:h-4 sm:w-4 text-foreground/80" })
7282
+ }
7283
+ ),
7284
+ currentPage !== null && totalPages !== null && /* @__PURE__ */ jsxs("div", { className: "px-2 sm:px-3 py-1 sm:py-1.5 min-w-[3rem] sm:min-w-[4rem] text-center text-[10px] sm:text-xs font-medium text-foreground/70 bg-background/50 rounded-md border border-border/30", children: [
7285
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: currentPage }),
7286
+ /* @__PURE__ */ jsx("span", { className: "mx-0.5 sm:mx-1 text-foreground/40", children: "/" }),
7287
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: totalPages })
7288
+ ] }),
7289
+ /* @__PURE__ */ jsx(
7290
+ Button,
7291
+ {
7292
+ type: "button",
7293
+ variant: "ghost",
7294
+ size: "icon",
7295
+ onClick: handleNextPage,
7296
+ disabled: !isViewerReady || currentPage === null || totalPages === null || currentPage >= totalPages,
7297
+ className: cn(
7298
+ "h-7 w-7 sm:h-8 sm:w-8 rounded-md transition-all duration-200",
7299
+ "hover:bg-background/80 hover:shadow-sm",
7300
+ "disabled:opacity-40 disabled:cursor-not-allowed",
7301
+ "active:scale-95"
7302
+ ),
7303
+ "aria-label": "Next page",
7304
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5 sm:h-4 sm:w-4 text-foreground/80" })
7305
+ }
7306
+ )
7307
+ ] }),
7308
+ showPdfControlsGroup && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 sm:gap-3 flex-shrink-0 w-full sm:w-auto justify-center", children: [
7309
+ /* @__PURE__ */ jsx("div", { className: "h-5 sm:h-6 w-px bg-border/60 hidden sm:block" }),
5910
7310
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 sm:gap-1 bg-muted/60 rounded-lg p-0.5 sm:p-1 border border-border/50 shadow-sm", children: [
5911
7311
  /* @__PURE__ */ jsx(
5912
7312
  Button,
@@ -5945,50 +7345,6 @@ function ViewToggleToolbar({
5945
7345
  }
5946
7346
  )
5947
7347
  ] }),
5948
- /* @__PURE__ */ jsx("div", { className: "h-5 sm:h-6 w-px bg-border/60 hidden sm:block" }),
5949
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 sm:gap-1 bg-muted/60 rounded-lg p-0.5 sm:p-1 border border-border/50 shadow-sm", children: [
5950
- /* @__PURE__ */ jsx(
5951
- Button,
5952
- {
5953
- type: "button",
5954
- variant: "ghost",
5955
- size: "icon",
5956
- onClick: handlePreviousPage,
5957
- disabled: !isViewerReady || currentPage === null || currentPage <= 1,
5958
- className: cn(
5959
- "h-7 w-7 sm:h-8 sm:w-8 rounded-md transition-all duration-200",
5960
- "hover:bg-background/80 hover:shadow-sm",
5961
- "disabled:opacity-40 disabled:cursor-not-allowed",
5962
- "active:scale-95"
5963
- ),
5964
- "aria-label": "Previous page",
5965
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-3.5 w-3.5 sm:h-4 sm:w-4 text-foreground/80" })
5966
- }
5967
- ),
5968
- currentPage !== null && totalPages !== null && /* @__PURE__ */ jsxs("div", { className: "px-2 sm:px-3 py-1 sm:py-1.5 min-w-[3rem] sm:min-w-[4rem] text-center text-[10px] sm:text-xs font-medium text-foreground/70 bg-background/50 rounded-md border border-border/30", children: [
5969
- /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: currentPage }),
5970
- /* @__PURE__ */ jsx("span", { className: "mx-0.5 sm:mx-1 text-foreground/40", children: "/" }),
5971
- /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: totalPages })
5972
- ] }),
5973
- /* @__PURE__ */ jsx(
5974
- Button,
5975
- {
5976
- type: "button",
5977
- variant: "ghost",
5978
- size: "icon",
5979
- onClick: handleNextPage,
5980
- disabled: !isViewerReady || currentPage === null || totalPages === null || currentPage >= totalPages,
5981
- className: cn(
5982
- "h-7 w-7 sm:h-8 sm:w-8 rounded-md transition-all duration-200",
5983
- "hover:bg-background/80 hover:shadow-sm",
5984
- "disabled:opacity-40 disabled:cursor-not-allowed",
5985
- "active:scale-95"
5986
- ),
5987
- "aria-label": "Next page",
5988
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5 sm:h-4 sm:w-4 text-foreground/80" })
5989
- }
5990
- )
5991
- ] }),
5992
7348
  requiredFieldNavigation?.hasRequiredFields && /* @__PURE__ */ jsxs(Fragment, { children: [
5993
7349
  /* @__PURE__ */ jsx("div", { className: "h-5 sm:h-6 w-px bg-border/60 hidden sm:block" }),
5994
7350
  /* @__PURE__ */ jsx(
@@ -6063,7 +7419,7 @@ var alertVariants = cva(
6063
7419
  }
6064
7420
  }
6065
7421
  );
6066
- var Alert = React8.forwardRef(({ className, variant, ...props }, ref) => /* @__PURE__ */ jsx(
7422
+ var Alert = React9.forwardRef(({ className, variant, ...props }, ref) => /* @__PURE__ */ jsx(
6067
7423
  "div",
6068
7424
  {
6069
7425
  ref,
@@ -6073,7 +7429,7 @@ var Alert = React8.forwardRef(({ className, variant, ...props }, ref) => /* @__P
6073
7429
  }
6074
7430
  ));
6075
7431
  Alert.displayName = "Alert";
6076
- var AlertTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
7432
+ var AlertTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
6077
7433
  "h5",
6078
7434
  {
6079
7435
  ref,
@@ -6082,7 +7438,7 @@ var AlertTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE_
6082
7438
  }
6083
7439
  ));
6084
7440
  AlertTitle.displayName = "AlertTitle";
6085
- var AlertDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
7441
+ var AlertDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
6086
7442
  "div",
6087
7443
  {
6088
7444
  ref,
@@ -6446,10 +7802,15 @@ function UnacknowledgedFieldsModal({
6446
7802
  onOpenChange,
6447
7803
  unacknowledgedFields,
6448
7804
  onAcknowledge,
6449
- isAcknowledged
7805
+ isAcknowledged,
7806
+ onComplete
6450
7807
  }) {
6451
7808
  const [expandedItem, setExpandedItem] = useState("");
6452
7809
  const [isManuallyClosing, setIsManuallyClosing] = useState(false);
7810
+ const onCompleteRef = useRef(onComplete);
7811
+ useEffect(() => {
7812
+ onCompleteRef.current = onComplete;
7813
+ }, [onComplete]);
6453
7814
  useEffect(() => {
6454
7815
  if (open && unacknowledgedFields.length > 0 && expandedItem === "") {
6455
7816
  const firstField = unacknowledgedFields[0];
@@ -6472,6 +7833,9 @@ function UnacknowledgedFieldsModal({
6472
7833
  (ack) => ack.id === ackId || isAcknowledged(fieldId, ack.id)
6473
7834
  );
6474
7835
  if (allAcknowledged) {
7836
+ setTimeout(() => {
7837
+ onCompleteRef.current?.([fieldId]);
7838
+ }, 150);
6475
7839
  const currentIndex = unacknowledgedFields.findIndex((f) => f.field.id === fieldId);
6476
7840
  const nextUnacknowledged = unacknowledgedFields[currentIndex + 1];
6477
7841
  if (nextUnacknowledged) {
@@ -6490,18 +7854,29 @@ function UnacknowledgedFieldsModal({
6490
7854
  };
6491
7855
  const handleAcknowledgeAll = async () => {
6492
7856
  setIsManuallyClosing(true);
7857
+ const acknowledgedFieldIds = [];
6493
7858
  unacknowledgedFields.forEach(({ field }) => {
6494
7859
  if (field.acknowledgements) {
7860
+ let fieldHadUnacknowledged = false;
6495
7861
  field.acknowledgements.forEach((ack) => {
6496
7862
  if (!isAcknowledged(field.id, ack.id)) {
6497
7863
  onAcknowledge(field.id, ack.id);
7864
+ fieldHadUnacknowledged = true;
6498
7865
  }
6499
7866
  });
7867
+ if (fieldHadUnacknowledged) {
7868
+ acknowledgedFieldIds.push(field.id);
7869
+ }
6500
7870
  }
6501
7871
  });
6502
7872
  await new Promise((resolve) => setTimeout(resolve, 150));
6503
7873
  onOpenChange(false);
6504
7874
  setIsManuallyClosing(false);
7875
+ if (acknowledgedFieldIds.length > 0) {
7876
+ setTimeout(() => {
7877
+ onCompleteRef.current?.(acknowledgedFieldIds);
7878
+ }, 150);
7879
+ }
6505
7880
  };
6506
7881
  return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-2xl max-h-[80vh] flex flex-col", children: [
6507
7882
  /* @__PURE__ */ jsxs(DialogHeader, { children: [
@@ -6786,8 +8161,297 @@ function AcknowledgementsSidebar({
6786
8161
  }) }) })
6787
8162
  ] });
6788
8163
  }
8164
+ function calculatePopupPosition(fieldPos, popupWidth, popupHeight) {
8165
+ const viewportWidth = window.innerWidth;
8166
+ const viewportHeight = window.innerHeight;
8167
+ const gap = 8;
8168
+ const edgePadding = 16;
8169
+ const fieldTop = fieldPos.y;
8170
+ const fieldBottom = fieldPos.y + fieldPos.height;
8171
+ const fieldLeft = fieldPos.x;
8172
+ const fieldRight = fieldPos.x + fieldPos.width;
8173
+ const spaceBelow = viewportHeight - fieldBottom - edgePadding;
8174
+ const spaceAbove = fieldTop - edgePadding;
8175
+ const positionBelow = spaceBelow >= popupHeight || spaceBelow >= spaceAbove;
8176
+ let top;
8177
+ if (positionBelow) {
8178
+ top = fieldBottom + gap;
8179
+ if (top + popupHeight > viewportHeight - edgePadding) {
8180
+ const topAbove = fieldTop - popupHeight - gap;
8181
+ if (topAbove >= edgePadding) {
8182
+ top = topAbove;
8183
+ } else {
8184
+ top = Math.min(fieldBottom + gap, viewportHeight - edgePadding - popupHeight);
8185
+ top = Math.max(top, edgePadding);
8186
+ }
8187
+ }
8188
+ } else {
8189
+ top = fieldTop - popupHeight - gap;
8190
+ if (top < edgePadding) {
8191
+ top = fieldBottom + gap;
8192
+ if (top + popupHeight > viewportHeight - edgePadding) {
8193
+ top = Math.max(edgePadding, viewportHeight - edgePadding - popupHeight);
8194
+ }
8195
+ }
8196
+ }
8197
+ if (top < fieldBottom && top + popupHeight > fieldTop) {
8198
+ top = fieldBottom + gap;
8199
+ if (top + popupHeight > viewportHeight - edgePadding) {
8200
+ top = fieldTop - popupHeight - gap;
8201
+ if (top < edgePadding) {
8202
+ top = edgePadding;
8203
+ }
8204
+ }
8205
+ }
8206
+ let left = fieldLeft;
8207
+ if (left + popupWidth > viewportWidth - edgePadding) {
8208
+ left = fieldRight - popupWidth;
8209
+ }
8210
+ if (left + popupWidth > viewportWidth - edgePadding) {
8211
+ left = viewportWidth - edgePadding - popupWidth;
8212
+ }
8213
+ if (left < edgePadding) {
8214
+ left = edgePadding;
8215
+ }
8216
+ return { top: Math.round(top), left: Math.round(left) };
8217
+ }
8218
+ function DateFieldCalendarPopup({
8219
+ open,
8220
+ onOpenChange,
8221
+ value,
8222
+ onSelect,
8223
+ fieldPosition,
8224
+ fieldLabel
8225
+ }) {
8226
+ const [date, setDate] = useState(void 0);
8227
+ const [position, setPosition] = useState({ top: 0, left: 0 });
8228
+ const popupRef = useRef(null);
8229
+ const frozenPositionRef = useRef(null);
8230
+ const [mounted, setMounted] = useState(false);
8231
+ useEffect(() => {
8232
+ if (value) {
8233
+ const validation = parseAndValidateDate(value);
8234
+ if (validation.isValid && validation.date) {
8235
+ setDate(validation.date);
8236
+ } else {
8237
+ setDate(void 0);
8238
+ }
8239
+ } else {
8240
+ setDate(void 0);
8241
+ }
8242
+ }, [value]);
8243
+ useEffect(() => {
8244
+ if (open && fieldPosition) {
8245
+ if (!frozenPositionRef.current) {
8246
+ frozenPositionRef.current = { ...fieldPosition };
8247
+ requestAnimationFrame(() => {
8248
+ if (popupRef.current && frozenPositionRef.current) {
8249
+ const popupRect = popupRef.current.getBoundingClientRect();
8250
+ const newPosition = calculatePopupPosition(
8251
+ frozenPositionRef.current,
8252
+ popupRect.width || 350,
8253
+ // Default width estimate
8254
+ popupRect.height || 400
8255
+ // Default height estimate
8256
+ );
8257
+ setPosition(newPosition);
8258
+ }
8259
+ });
8260
+ }
8261
+ } else if (!open) {
8262
+ frozenPositionRef.current = null;
8263
+ setPosition({ top: 0, left: 0 });
8264
+ }
8265
+ }, [open, fieldPosition]);
8266
+ useEffect(() => {
8267
+ if (popupRef.current && (position.top !== 0 || position.left !== 0)) {
8268
+ popupRef.current.style.setProperty("top", `${position.top}px`, "important");
8269
+ popupRef.current.style.setProperty("left", `${position.left}px`, "important");
8270
+ popupRef.current.style.setProperty("position", "fixed", "important");
8271
+ popupRef.current.style.setProperty("transform", "none", "important");
8272
+ }
8273
+ }, [position]);
8274
+ useEffect(() => {
8275
+ if (!open) return;
8276
+ const handleClickOutside = (event) => {
8277
+ const target = event.target;
8278
+ if (popupRef.current && popupRef.current.contains(target)) {
8279
+ return;
8280
+ }
8281
+ const selectElement = target.closest?.(
8282
+ "[data-radix-select-content], [data-radix-select-item], [data-radix-select-trigger], [data-radix-select-viewport]"
8283
+ );
8284
+ if (selectElement) {
8285
+ return;
8286
+ }
8287
+ onOpenChange(false);
8288
+ };
8289
+ const handleEscape = (event) => {
8290
+ if (event.key === "Escape") {
8291
+ onOpenChange(false);
8292
+ }
8293
+ };
8294
+ const timeoutId = setTimeout(() => {
8295
+ document.addEventListener("mousedown", handleClickOutside);
8296
+ document.addEventListener("keydown", handleEscape);
8297
+ }, 100);
8298
+ return () => {
8299
+ clearTimeout(timeoutId);
8300
+ document.removeEventListener("mousedown", handleClickOutside);
8301
+ document.removeEventListener("keydown", handleEscape);
8302
+ };
8303
+ }, [open, onOpenChange]);
8304
+ useEffect(() => {
8305
+ if (!open) return;
8306
+ const handleScroll = () => {
8307
+ onOpenChange(false);
8308
+ };
8309
+ const timeoutId = setTimeout(() => {
8310
+ window.addEventListener("scroll", handleScroll, { passive: true });
8311
+ const iframe = document.querySelector("iframe");
8312
+ if (iframe?.contentWindow) {
8313
+ iframe.contentWindow.addEventListener("scroll", handleScroll, { passive: true });
8314
+ const viewerContainer = iframe.contentDocument?.querySelector("#viewerContainer");
8315
+ if (viewerContainer) {
8316
+ viewerContainer.addEventListener("scroll", handleScroll, { passive: true });
8317
+ }
8318
+ }
8319
+ }, 100);
8320
+ return () => {
8321
+ clearTimeout(timeoutId);
8322
+ window.removeEventListener("scroll", handleScroll);
8323
+ const iframe = document.querySelector("iframe");
8324
+ if (iframe?.contentWindow) {
8325
+ iframe.contentWindow.removeEventListener("scroll", handleScroll);
8326
+ const viewerContainer = iframe.contentDocument?.querySelector("#viewerContainer");
8327
+ if (viewerContainer) {
8328
+ viewerContainer.removeEventListener("scroll", handleScroll);
8329
+ }
8330
+ }
8331
+ };
8332
+ }, [open, onOpenChange]);
8333
+ useEffect(() => {
8334
+ setMounted(true);
8335
+ return () => setMounted(false);
8336
+ }, []);
8337
+ const handleDateSelect = (selectedDate) => {
8338
+ if (selectedDate) {
8339
+ setDate(selectedDate);
8340
+ const isoString = format(selectedDate, "yyyy-MM-dd");
8341
+ onSelect(isoString);
8342
+ onOpenChange(false);
8343
+ }
8344
+ };
8345
+ const handleToday = () => {
8346
+ const today = /* @__PURE__ */ new Date();
8347
+ handleDateSelect(today);
8348
+ };
8349
+ const handleClear = () => {
8350
+ setDate(void 0);
8351
+ onSelect("");
8352
+ onOpenChange(false);
8353
+ };
8354
+ const handleClose = () => {
8355
+ onOpenChange(false);
8356
+ };
8357
+ if (!open || !mounted || !fieldPosition) {
8358
+ return null;
8359
+ }
8360
+ const hasValidPosition = position.top !== 0 || position.left !== 0;
8361
+ const content = /* @__PURE__ */ jsx("div", { className: "signiphi-pdf-signer", children: /* @__PURE__ */ jsx(
8362
+ "div",
8363
+ {
8364
+ ref: popupRef,
8365
+ className: "signiphi-pdf-calendar-popup",
8366
+ style: {
8367
+ visibility: hasValidPosition ? "visible" : "hidden",
8368
+ // Use visibility instead of opacity
8369
+ opacity: hasValidPosition ? 1 : 0,
8370
+ // Also keep opacity for smooth fade-in
8371
+ pointerEvents: hasValidPosition ? "auto" : "none"
8372
+ // Disable interaction until positioned
8373
+ },
8374
+ role: "dialog",
8375
+ "aria-label": fieldLabel ? `Select date for ${fieldLabel}` : "Select date",
8376
+ "aria-modal": "true",
8377
+ children: /* @__PURE__ */ jsxs(Card, { className: "p-0 shadow-lg border-2 border-border bg-popover rounded-lg", children: [
8378
+ /* @__PURE__ */ jsx("div", { className: "p-3", children: /* @__PURE__ */ jsx(
8379
+ Calendar,
8380
+ {
8381
+ mode: "single",
8382
+ selected: date,
8383
+ onSelect: handleDateSelect,
8384
+ initialFocus: true
8385
+ }
8386
+ ) }),
8387
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2 p-3 border-t border-border", children: [
8388
+ /* @__PURE__ */ jsx(
8389
+ Button,
8390
+ {
8391
+ variant: "outline",
8392
+ size: "sm",
8393
+ onClick: handleToday,
8394
+ className: "flex-1",
8395
+ children: "Today"
8396
+ }
8397
+ ),
8398
+ /* @__PURE__ */ jsx(
8399
+ Button,
8400
+ {
8401
+ variant: "outline",
8402
+ size: "sm",
8403
+ onClick: handleClear,
8404
+ className: "flex-1",
8405
+ children: "Clear"
8406
+ }
8407
+ ),
8408
+ /* @__PURE__ */ jsx(
8409
+ Button,
8410
+ {
8411
+ variant: "ghost",
8412
+ size: "sm",
8413
+ onClick: handleClose,
8414
+ className: "flex-1",
8415
+ children: "Cancel"
8416
+ }
8417
+ )
8418
+ ] })
8419
+ ] })
8420
+ }
8421
+ ) });
8422
+ return createPortal(content, document.body);
8423
+ }
6789
8424
 
6790
8425
  // src/utils/pdf-viewer-filter.ts
8426
+ function robustlyRemoveField(form, field) {
8427
+ try {
8428
+ const fieldWithAcro = field;
8429
+ if (fieldWithAcro.acroField && typeof fieldWithAcro.acroField.getWidgets === "function") {
8430
+ let widgets = fieldWithAcro.acroField.getWidgets() || [];
8431
+ let safetyCounter = 0;
8432
+ const maxIterations = widgets.length + 5;
8433
+ while (widgets.length > 0 && safetyCounter < maxIterations) {
8434
+ try {
8435
+ const widgetIndex = widgets.length - 1;
8436
+ fieldWithAcro.acroField.removeWidget?.(widgetIndex);
8437
+ widgets = fieldWithAcro.acroField.getWidgets() || [];
8438
+ } catch {
8439
+ break;
8440
+ }
8441
+ safetyCounter++;
8442
+ }
8443
+ }
8444
+ form.removeField(field);
8445
+ return true;
8446
+ } catch (error) {
8447
+ try {
8448
+ form.removeField(field);
8449
+ return true;
8450
+ } catch {
8451
+ return false;
8452
+ }
8453
+ }
8454
+ }
6791
8455
  async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
6792
8456
  try {
6793
8457
  const labelFont = await pdfDoc.embedFont("Helvetica-Bold");
@@ -6797,8 +8461,7 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
6797
8461
  for (const field of fieldsToLabel) {
6798
8462
  const isRadioField = field.type === "radio" /* RADIO */;
6799
8463
  const hasRadioOptions = isRadioField && field.options && field.options.length > 0;
6800
- const hasCustomLabel = field.label && field.label.trim() && !isAutoGeneratedLabel(field.label);
6801
- if (!hasCustomLabel && !hasRadioOptions) {
8464
+ if (!hasDrawableLabel(field) && !hasRadioOptions) {
6802
8465
  continue;
6803
8466
  }
6804
8467
  const pdfField = form.getFieldMaybe(field.name);
@@ -6809,27 +8472,30 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
6809
8472
  const widgets = fieldWithExtensions.acroField?.getWidgets?.() || [];
6810
8473
  if (widgets.length === 0) continue;
6811
8474
  if (isRadioField) {
6812
- if (field.label && field.label.trim() && !isAutoGeneratedLabel(field.label)) {
8475
+ if (hasDrawableLabel(field)) {
6813
8476
  const firstWidget = widgets[0];
6814
- if (!firstWidget) continue;
6815
- const firstRect = firstWidget.getRectangle?.();
6816
- if (firstRect) {
6817
- const pageRef = firstWidget.P?.();
6818
- const pageIndex = findPageIndexWithFallback(pages, pageRef);
6819
- if (pageIndex >= 0) {
6820
- const page = pages[pageIndex];
6821
- if (!page) continue;
6822
- const groupLabelKey = `${pageIndex}-${field.name}-grouplabel`;
6823
- if (!drawnOnce.has(groupLabelKey)) {
6824
- const labelY = firstRect.y + firstRect.height + 5;
6825
- page.drawText(field.label, {
6826
- x: firstRect.x,
6827
- y: labelY,
6828
- size: 10,
6829
- font: labelFont,
6830
- color: rgb(0, 0, 0)
6831
- });
6832
- drawnOnce.add(groupLabelKey);
8477
+ if (firstWidget) {
8478
+ const firstRect = firstWidget.getRectangle?.();
8479
+ if (firstRect) {
8480
+ const pageRef = firstWidget.P?.();
8481
+ const pageIndex = findPageIndexWithFallback(pages, pageRef);
8482
+ if (pageIndex >= 0) {
8483
+ const page = pages[pageIndex];
8484
+ if (page) {
8485
+ const groupLabelKey = `${pageIndex}-${field.name}-grouplabel`;
8486
+ if (!drawnOnce.has(groupLabelKey)) {
8487
+ const radioLabelFontSize = Math.min(10, firstRect.height * 0.4);
8488
+ const labelY = firstRect.y + firstRect.height + 5;
8489
+ page.drawText(field.label, {
8490
+ x: firstRect.x,
8491
+ y: labelY,
8492
+ size: radioLabelFontSize,
8493
+ font: labelFont,
8494
+ color: rgb(0, 0, 0)
8495
+ });
8496
+ drawnOnce.add(groupLabelKey);
8497
+ }
8498
+ }
6833
8499
  }
6834
8500
  }
6835
8501
  }
@@ -6842,17 +8508,16 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
6842
8508
  if (!rect) continue;
6843
8509
  const pageRef = widget.P?.();
6844
8510
  const pageIndex = findPageIndexWithFallback(pages, pageRef);
6845
- if (pageIndex >= 0) {
8511
+ if (pageIndex >= 0 && pageIndex < pages.length) {
6846
8512
  const page = pages[pageIndex];
6847
8513
  if (!page) continue;
6848
- const optionTexts = field.options;
6849
- const optionText = optionTexts[i] || optionTexts[0];
8514
+ const optionText = field.options[i];
6850
8515
  const optionKey = `${pageIndex}-${field.name}-opt-${i}`;
6851
8516
  if (optionText && !drawnOnce.has(optionKey)) {
6852
8517
  page.drawText(optionText, {
6853
- x: rect.x + rect.width + 6,
6854
- y: rect.y + (rect.height - 10) / 2,
6855
- size: 10,
8518
+ x: rect.x + rect.width + 4,
8519
+ y: rect.y + (rect.height - 7) / 2,
8520
+ size: 7,
6856
8521
  font: labelFont,
6857
8522
  color: rgb(0, 0, 0)
6858
8523
  });
@@ -6873,12 +8538,13 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
6873
8538
  if (!page) continue;
6874
8539
  const key = `${pageIndex}-${field.name}`;
6875
8540
  if (!drawnOnce.has(key)) {
6876
- if (!isAutoGeneratedLabel(field.label)) {
8541
+ if (hasDrawableLabel(field)) {
8542
+ const labelFontSize = Math.min(10, rect.height * 0.4);
6877
8543
  if (field.type === "checkbox" /* CHECKBOX */) {
6878
8544
  page.drawText(field.label, {
6879
8545
  x: rect.x + rect.width + 5,
6880
- y: rect.y + rect.height * 0.2,
6881
- size: 10,
8546
+ y: rect.y + (rect.height - labelFontSize) / 2,
8547
+ size: labelFontSize,
6882
8548
  font: labelFont,
6883
8549
  color: rgb(0, 0, 0)
6884
8550
  });
@@ -6887,9 +8553,9 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
6887
8553
  page.drawText(field.label, {
6888
8554
  x: rect.x,
6889
8555
  y: labelY,
6890
- size: 9,
8556
+ size: labelFontSize,
6891
8557
  font: labelFont,
6892
- color: rgb(0.3, 0.3, 0.3)
8558
+ color: rgb(0, 0, 0)
6893
8559
  });
6894
8560
  }
6895
8561
  drawnOnce.add(key);
@@ -6950,7 +8616,7 @@ async function filterPdfForCurrentSigner(pdfBytes, allFields, multiSignerContext
6950
8616
  try {
6951
8617
  const pdfField = form.getFieldMaybe(field.name);
6952
8618
  if (pdfField) {
6953
- form.removeField(pdfField);
8619
+ robustlyRemoveField(form, pdfField);
6954
8620
  removedCount++;
6955
8621
  } else {
6956
8622
  notFoundCount++;
@@ -6984,7 +8650,7 @@ function usePdfViewer(multiSignerContext) {
6984
8650
  const [originalPdfBytes, setOriginalPdfBytes] = useState(null);
6985
8651
  const [_viewerPdfBytes, setViewerPdfBytes] = useState(null);
6986
8652
  const [extractedFields, setExtractedFields] = useState([]);
6987
- const loadPdf = useCallback(async (url) => {
8653
+ const loadPdf = useCallback(async (url, signerEmailForExtraction) => {
6988
8654
  setIsLoading(true);
6989
8655
  setError(null);
6990
8656
  setIsLoaded(false);
@@ -6997,8 +8663,22 @@ function usePdfViewer(multiSignerContext) {
6997
8663
  }
6998
8664
  const bytes = await urlToPdfBytes(url);
6999
8665
  setOriginalPdfBytes(bytes);
7000
- setViewerPdfBytes(bytes);
7001
- await viewerRef.current?.loadPdf(url);
8666
+ const fields = await extractVisibleFormFields(bytes, signerEmailForExtraction);
8667
+ setExtractedFields(fields);
8668
+ const filteredBytes = await filterPdfForCurrentSigner(
8669
+ bytes,
8670
+ fields,
8671
+ multiSignerContext || {
8672
+ isMultiSigner: false,
8673
+ currentSigner: null,
8674
+ currentSignerEmail: "",
8675
+ isPrimarySigner: true,
8676
+ isFinalSigner: true
8677
+ }
8678
+ );
8679
+ setViewerPdfBytes(filteredBytes);
8680
+ const blobUrl = createPdfBlobUrl(filteredBytes);
8681
+ await viewerRef.current?.loadPdf(blobUrl);
7002
8682
  } catch (err) {
7003
8683
  const errorMessage = err instanceof Error ? err.message : "Failed to load PDF";
7004
8684
  logger.error("Error loading PDF:", err);
@@ -7006,7 +8686,7 @@ function usePdfViewer(multiSignerContext) {
7006
8686
  setOriginalPdfBytes(null);
7007
8687
  setViewerPdfBytes(null);
7008
8688
  }
7009
- }, []);
8689
+ }, [multiSignerContext]);
7010
8690
  const handleLoad = useCallback(() => {
7011
8691
  setIsLoading(false);
7012
8692
  setIsLoaded(true);
@@ -7042,52 +8722,15 @@ function usePdfViewer(multiSignerContext) {
7042
8722
  return await viewerRef.current.saveDocument();
7043
8723
  }, []);
7044
8724
  const extractFormFields = useCallback(
7045
- async (currentSignerEmail) => {
7046
- if (!originalPdfBytes) {
7047
- throw new Error("No PDF loaded");
7048
- }
7049
- try {
7050
- const fields = await extractVisibleFormFields(
7051
- originalPdfBytes,
7052
- currentSignerEmail
7053
- );
7054
- setExtractedFields(fields);
7055
- if (multiSignerContext?.isMultiSigner) {
7056
- const filteredBytes = await filterPdfForCurrentSigner(
7057
- originalPdfBytes,
7058
- fields,
7059
- multiSignerContext
7060
- );
7061
- setViewerPdfBytes(filteredBytes);
7062
- const filteredBlobUrl = createPdfBlobUrl(filteredBytes);
7063
- await viewerRef.current?.loadPdf(filteredBlobUrl);
7064
- } else {
7065
- const labeledBytes = await filterPdfForCurrentSigner(
7066
- originalPdfBytes,
7067
- fields,
7068
- multiSignerContext || {
7069
- isMultiSigner: false,
7070
- currentSigner: null,
7071
- currentSignerEmail: "",
7072
- isPrimarySigner: true,
7073
- isFinalSigner: true
7074
- }
7075
- );
7076
- setViewerPdfBytes(labeledBytes);
7077
- const labeledBlobUrl = createPdfBlobUrl(labeledBytes);
7078
- await viewerRef.current?.loadPdf(labeledBlobUrl);
7079
- }
7080
- if (viewerRef.current && fields.length > 0) {
7081
- await new Promise((resolve) => setTimeout(resolve, 100));
7082
- viewerRef.current.injectPlaceholders(fields);
7083
- }
7084
- return fields;
7085
- } catch (err) {
7086
- const errorMessage = err instanceof Error ? err.message : "Failed to extract form fields";
7087
- throw new Error(errorMessage);
8725
+ async (_currentSignerEmail) => {
8726
+ if (viewerRef.current && extractedFields.length > 0) {
8727
+ await new Promise((resolve) => setTimeout(resolve, 100));
8728
+ viewerRef.current.injectPlaceholders(extractedFields);
8729
+ viewerRef.current.injectRadioLabels(extractedFields);
7088
8730
  }
8731
+ return extractedFields;
7089
8732
  },
7090
- [originalPdfBytes, multiSignerContext]
8733
+ [extractedFields]
7091
8734
  );
7092
8735
  const fillPdf = useCallback(
7093
8736
  async (fieldValues, signatures, currentSignerEmail, metadata, auditTrail) => {
@@ -7230,6 +8873,20 @@ function usePdfViewer(multiSignerContext) {
7230
8873
  }
7231
8874
  function useFormFields(fields = []) {
7232
8875
  const [fieldValues, setFieldValues] = useState({});
8876
+ const seededFieldsRef = useRef(/* @__PURE__ */ new Set());
8877
+ useEffect(() => {
8878
+ if (fields.length === 0) return;
8879
+ const defaults = {};
8880
+ for (const field of fields) {
8881
+ if (field.defaultValue && field.defaultValue.trim() && !seededFieldsRef.current.has(field.id)) {
8882
+ defaults[field.id] = field.defaultValue;
8883
+ seededFieldsRef.current.add(field.id);
8884
+ }
8885
+ }
8886
+ if (Object.keys(defaults).length > 0) {
8887
+ setFieldValues((prev) => ({ ...defaults, ...prev }));
8888
+ }
8889
+ }, [fields]);
7233
8890
  const [errors, setErrors] = useState([]);
7234
8891
  const [touched, setTouched] = useState({});
7235
8892
  const updateField = useCallback((fieldId, value) => {
@@ -7269,14 +8926,14 @@ function useFormFields(fields = []) {
7269
8926
  if (!value || value.trim() === "") {
7270
8927
  newErrors.push({
7271
8928
  field: fieldId,
7272
- message: `${field.label || field.name} is required`
8929
+ message: `${getFieldDisplayName(field)} is required`
7273
8930
  });
7274
8931
  }
7275
8932
  }
7276
8933
  if (value && field.maxLength && value.length > field.maxLength) {
7277
8934
  newErrors.push({
7278
8935
  field: fieldId,
7279
- message: `${field.label || field.name} must be at most ${field.maxLength} characters`
8936
+ message: `${getFieldDisplayName(field)} must be at most ${field.maxLength} characters`
7280
8937
  });
7281
8938
  }
7282
8939
  if (value && field.type === "date") {
@@ -7302,13 +8959,13 @@ function useFormFields(fields = []) {
7302
8959
  if (!signatures[field.id]) {
7303
8960
  newErrors.push({
7304
8961
  field: field.id,
7305
- message: `${field.label || field.name} is required`
8962
+ message: `${getFieldDisplayName(field)} is required`
7306
8963
  });
7307
8964
  }
7308
8965
  } else if (!value2 || value2.trim() === "") {
7309
8966
  newErrors.push({
7310
8967
  field: field.id,
7311
- message: `${field.label || field.name} is required`
8968
+ message: `${getFieldDisplayName(field)} is required`
7312
8969
  });
7313
8970
  }
7314
8971
  }
@@ -7316,7 +8973,7 @@ function useFormFields(fields = []) {
7316
8973
  if (value && field.maxLength && value.length > field.maxLength) {
7317
8974
  newErrors.push({
7318
8975
  field: field.id,
7319
- message: `${field.label || field.name} must be at most ${field.maxLength} characters`
8976
+ message: `${getFieldDisplayName(field)} must be at most ${field.maxLength} characters`
7320
8977
  });
7321
8978
  }
7322
8979
  if (value && field.type === "date") {
@@ -7490,7 +9147,7 @@ function useSignatures() {
7490
9147
  for (const field of fields) {
7491
9148
  if (field.required && (field.type === "signature" || field.type === "initials")) {
7492
9149
  if (!signaturesToCheck[field.id]) {
7493
- errors.push(`${field.label || field.name} is required`);
9150
+ errors.push(`${getFieldDisplayName(field)} is required`);
7494
9151
  }
7495
9152
  }
7496
9153
  }
@@ -7608,6 +9265,13 @@ function useAttachments(options = {}) {
7608
9265
  errors.push(...validation.errors);
7609
9266
  continue;
7610
9267
  }
9268
+ const isDuplicate = [...attachments, ...newAttachments].some(
9269
+ (att) => att.name.toLowerCase() === file.name.toLowerCase()
9270
+ );
9271
+ if (isDuplicate) {
9272
+ errors.push(`"${file.name}" is already attached`);
9273
+ continue;
9274
+ }
7611
9275
  const currentTotalSize = [...attachments, ...newAttachments].reduce((sum, att) => sum + att.size, 0);
7612
9276
  if (currentTotalSize + file.size > constraints.maxTotalSize) {
7613
9277
  const maxTotalMB = (constraints.maxTotalSize / 1024 / 1024).toFixed(1);
@@ -7658,6 +9322,9 @@ function useAttachments(options = {}) {
7658
9322
  setAttachments([]);
7659
9323
  setValidationErrors([]);
7660
9324
  }, []);
9325
+ const clearValidationErrors = useCallback(() => {
9326
+ setValidationErrors([]);
9327
+ }, []);
7661
9328
  const getTotalSize = useCallback(() => {
7662
9329
  return attachments.reduce((sum, att) => sum + att.size, 0);
7663
9330
  }, [attachments]);
@@ -7689,6 +9356,7 @@ function useAttachments(options = {}) {
7689
9356
  addFiles,
7690
9357
  removeAttachment,
7691
9358
  clearAttachments,
9359
+ clearValidationErrors,
7692
9360
  // Utilities
7693
9361
  getTotalSize,
7694
9362
  formatSize,
@@ -7721,6 +9389,10 @@ function useAcknowledgements(fields) {
7721
9389
  const [acknowledgedMap, setAcknowledgedMap] = useState(
7722
9390
  /* @__PURE__ */ new Map()
7723
9391
  );
9392
+ const acknowledgedMapRef = useRef(acknowledgedMap);
9393
+ useEffect(() => {
9394
+ acknowledgedMapRef.current = acknowledgedMap;
9395
+ }, [acknowledgedMap]);
7724
9396
  const getFieldsWithAcknowledgements = useCallback((fieldsToFilter) => {
7725
9397
  return fieldsToFilter.filter(
7726
9398
  (field) => field.acknowledgements && field.acknowledgements.length > 0
@@ -7732,26 +9404,27 @@ function useAcknowledgements(fields) {
7732
9404
  const fieldAcks = newMap.get(fieldId) || /* @__PURE__ */ new Set();
7733
9405
  fieldAcks.add(ackId);
7734
9406
  newMap.set(fieldId, fieldAcks);
9407
+ acknowledgedMapRef.current = newMap;
7735
9408
  return newMap;
7736
9409
  });
7737
9410
  }, []);
7738
9411
  const isAcknowledged = useCallback((fieldId, ackId) => {
7739
- const fieldAcks = acknowledgedMap.get(fieldId);
9412
+ const fieldAcks = acknowledgedMapRef.current.get(fieldId);
7740
9413
  if (!fieldAcks) return false;
7741
9414
  if (ackId) {
7742
9415
  return fieldAcks.has(ackId);
7743
9416
  }
7744
9417
  return fieldAcks.size > 0;
7745
- }, [acknowledgedMap]);
9418
+ }, []);
7746
9419
  const isFieldFullyAcknowledged = useCallback((fieldId) => {
7747
9420
  const field = fields.find((f) => f.id === fieldId);
7748
9421
  if (!field || !field.acknowledgements || field.acknowledgements.length === 0) {
7749
9422
  return true;
7750
9423
  }
7751
- const fieldAcks = acknowledgedMap.get(fieldId);
9424
+ const fieldAcks = acknowledgedMapRef.current.get(fieldId);
7752
9425
  if (!fieldAcks) return false;
7753
9426
  return field.acknowledgements.every((ack) => fieldAcks.has(ack.id));
7754
- }, [fields, acknowledgedMap]);
9427
+ }, [fields]);
7755
9428
  const getFieldAcknowledgementProgress = useCallback((fieldId) => {
7756
9429
  const field = fields.find((f) => f.id === fieldId);
7757
9430
  if (!field || !field.acknowledgements || field.acknowledgements.length === 0) {
@@ -7806,7 +9479,7 @@ function useAcknowledgements(fields) {
7806
9479
  };
7807
9480
  }
7808
9481
  function useRequiredFieldNavigation(fields) {
7809
- const [currentRequiredIndex, setCurrentRequiredIndex] = useState(0);
9482
+ const [currentRequiredIndex, setCurrentRequiredIndex] = useState(-1);
7810
9483
  const requiredFields = useMemo(() => {
7811
9484
  return fields.filter((f) => {
7812
9485
  if (f.id === "signature_field_main" || f.id === "initials_field_main") {
@@ -7818,18 +9491,18 @@ function useRequiredFieldNavigation(fields) {
7818
9491
  const hasRequiredFields = requiredFields.length > 0;
7819
9492
  const goToNextRequired = useCallback(() => {
7820
9493
  if (!hasRequiredFields) return null;
7821
- const nextIndex = (currentRequiredIndex + 1) % requiredFields.length;
9494
+ const nextIndex = currentRequiredIndex === -1 ? 0 : (currentRequiredIndex + 1) % requiredFields.length;
7822
9495
  setCurrentRequiredIndex(nextIndex);
7823
9496
  return requiredFields[nextIndex] || null;
7824
9497
  }, [currentRequiredIndex, requiredFields, hasRequiredFields]);
7825
9498
  const goToPreviousRequired = useCallback(() => {
7826
9499
  if (!hasRequiredFields) return null;
7827
- const prevIndex = currentRequiredIndex === 0 ? requiredFields.length - 1 : currentRequiredIndex - 1;
9500
+ const prevIndex = currentRequiredIndex <= 0 ? requiredFields.length - 1 : currentRequiredIndex - 1;
7828
9501
  setCurrentRequiredIndex(prevIndex);
7829
9502
  return requiredFields[prevIndex] || null;
7830
9503
  }, [currentRequiredIndex, requiredFields, hasRequiredFields]);
7831
9504
  const currentRequiredField = useMemo(() => {
7832
- if (!hasRequiredFields) return null;
9505
+ if (!hasRequiredFields || currentRequiredIndex < 0) return null;
7833
9506
  return requiredFields[currentRequiredIndex] || null;
7834
9507
  }, [requiredFields, currentRequiredIndex, hasRequiredFields]);
7835
9508
  return {
@@ -7895,8 +9568,9 @@ function SubmissionForm({
7895
9568
  validateFields,
7896
9569
  getFieldError,
7897
9570
  isFieldTouched,
9571
+ touched,
7898
9572
  clearFields
7899
- } = useFormFields();
9573
+ } = useFormFields(extractedFields);
7900
9574
  const {
7901
9575
  signatures,
7902
9576
  setSignature: setSignatureOriginal,
@@ -7908,12 +9582,16 @@ function SubmissionForm({
7908
9582
  } = useSignatures();
7909
9583
  const signaturesRef = useRef(signatures);
7910
9584
  const fieldValuesRef = useRef(fieldValues);
9585
+ const touchedRef = useRef(touched);
7911
9586
  useEffect(() => {
7912
9587
  signaturesRef.current = signatures;
7913
9588
  }, [signatures]);
7914
9589
  useEffect(() => {
7915
9590
  fieldValuesRef.current = fieldValues;
7916
9591
  }, [fieldValues]);
9592
+ useEffect(() => {
9593
+ touchedRef.current = touched;
9594
+ }, [touched]);
7917
9595
  const formFields = customFormFields || extractedFields;
7918
9596
  const { filteredFields, isFieldVisible } = useFieldFiltering(formFields, multiSignerContext);
7919
9597
  const {
@@ -7934,7 +9612,15 @@ function SubmissionForm({
7934
9612
  } = useRequiredFieldNavigation(filteredFields);
7935
9613
  const [acknowledgementModalOpen, setAcknowledgementModalOpen] = useState(false);
7936
9614
  const [currentAcknowledgementField, setCurrentAcknowledgementField] = useState(null);
9615
+ const [currentAcknowledgementFieldName, setCurrentAcknowledgementFieldName] = useState(null);
9616
+ const currentAcknowledgementElementRef = useRef(null);
9617
+ const acknowledgementTriggerSourceRef = useRef(null);
9618
+ const acknowledgementCleanupTimeoutRef = useRef(null);
7937
9619
  const [unacknowledgedModalOpen, setUnacknowledgedModalOpen] = useState(false);
9620
+ const recentlyAcknowledgedFieldsRef = useRef(/* @__PURE__ */ new Map());
9621
+ const [dateCalendarOpen, setDateCalendarOpen] = useState(false);
9622
+ const [currentDateField, setCurrentDateField] = useState(null);
9623
+ const [dateFieldPosition, setDateFieldPosition] = useState(null);
7938
9624
  const realPdfFieldsByType = useMemo(() => {
7939
9625
  return {
7940
9626
  signature: filteredFields.filter(
@@ -7945,6 +9631,15 @@ function SubmissionForm({
7945
9631
  )
7946
9632
  };
7947
9633
  }, [filteredFields]);
9634
+ const hasInitialsFields = useMemo(() => {
9635
+ return filteredFields.some((f) => f.id === "initials_field_main" || f.type === "initials");
9636
+ }, [filteredFields]);
9637
+ const defaultSigningMessage = useMemo(() => {
9638
+ if (hasInitialsFields) {
9639
+ return "Fill out the form fields directly in the PDF document. Your signature and initials below will be applied to the document automatically.";
9640
+ }
9641
+ return "Fill out the form fields directly in the PDF document. Your signature below will be applied to the document automatically.";
9642
+ }, [hasInitialsFields]);
7948
9643
  const getPdfFieldNamesForIndicators = useCallback((fieldType) => {
7949
9644
  const realPdfFields = realPdfFieldsByType[fieldType];
7950
9645
  console.log(`[SubmissionForm] Total filteredFields count:`, filteredFields.length);
@@ -7981,6 +9676,15 @@ function SubmissionForm({
7981
9676
  return result;
7982
9677
  }, [realPdfFieldsByType, filteredFields, isFieldFullyAcknowledged]);
7983
9678
  const indicatorUpdateTimers = useRef(/* @__PURE__ */ new Map());
9679
+ useEffect(() => {
9680
+ return () => {
9681
+ if (acknowledgementCleanupTimeoutRef.current) {
9682
+ clearTimeout(acknowledgementCleanupTimeoutRef.current);
9683
+ }
9684
+ indicatorUpdateTimers.current.forEach((timer) => clearTimeout(timer));
9685
+ indicatorUpdateTimers.current.clear();
9686
+ };
9687
+ }, []);
7984
9688
  const updateFieldIndicator = useCallback((fieldId) => {
7985
9689
  const existingTimer = indicatorUpdateTimers.current.get(fieldId);
7986
9690
  if (existingTimer) {
@@ -8027,11 +9731,11 @@ function SubmissionForm({
8027
9731
  }
8028
9732
  let hasValue;
8029
9733
  if (field.type === "signature" && fieldId !== "signature_field_main") {
8030
- hasValue = hasSignature("signature_field_main");
9734
+ hasValue = !!signaturesRef.current["signature_field_main"];
8031
9735
  } else if (field.type === "initials" && fieldId !== "initials_field_main") {
8032
- hasValue = !!(fieldValues["initials_field_main"] && String(fieldValues["initials_field_main"]).trim() !== "");
9736
+ hasValue = !!(fieldValuesRef.current["initials_field_main"] && String(fieldValuesRef.current["initials_field_main"]).trim() !== "");
8033
9737
  } else {
8034
- hasValue = field.type === "signature" ? hasSignature(field.id) : !!(fieldValues[field.id] && String(fieldValues[field.id]).trim() !== "");
9738
+ hasValue = field.type === "signature" ? !!signaturesRef.current[field.id] : !!(fieldValuesRef.current[field.id] && String(fieldValuesRef.current[field.id]).trim() !== "");
8035
9739
  }
8036
9740
  const hasAcknowledgements = field.acknowledgements && field.acknowledgements.length > 0;
8037
9741
  if (!hasValue && !hasAcknowledgements) {
@@ -8082,24 +9786,38 @@ function SubmissionForm({
8082
9786
  } catch (error) {
8083
9787
  logger.error(`[FIELD INDICATOR] Failed to add ${indicatorType} indicator for ${field.id}:`, error);
8084
9788
  }
8085
- indicatorUpdateTimers.current.delete(fieldId);
8086
- }, 200);
9789
+ }, 50);
8087
9790
  indicatorUpdateTimers.current.set(fieldId, timer);
8088
- }, [filteredFields, fieldValues, hasSignature, isFieldFullyAcknowledged, viewerRef, getPdfFieldNamesForIndicators]);
9791
+ }, [filteredFields, isFieldFullyAcknowledged, viewerRef, getPdfFieldNamesForIndicators]);
9792
+ const updateFieldIndicatorRef = useRef(updateFieldIndicator);
9793
+ useEffect(() => {
9794
+ updateFieldIndicatorRef.current = updateFieldIndicator;
9795
+ }, [updateFieldIndicator]);
8089
9796
  const acknowledgeItem = useCallback((fieldId, ackId) => {
8090
9797
  acknowledgeItemOriginal(fieldId, ackId);
8091
- updateFieldIndicator(fieldId);
8092
- }, [acknowledgeItemOriginal, updateFieldIndicator]);
9798
+ updateFieldIndicatorRef.current(fieldId);
9799
+ }, [acknowledgeItemOriginal]);
8093
9800
  const setSignature = useCallback((fieldId, dataUrl) => {
8094
9801
  setSignatureOriginal(fieldId, dataUrl);
8095
- updateFieldIndicator(fieldId);
8096
- }, [setSignatureOriginal, updateFieldIndicator]);
9802
+ signaturesRef.current = {
9803
+ ...signaturesRef.current,
9804
+ [fieldId]: dataUrl
9805
+ };
9806
+ updateFieldIndicatorRef.current(fieldId);
9807
+ if (dataUrl.startsWith("data:")) {
9808
+ viewerRef.current?.previewSignature(fieldId, dataUrl);
9809
+ } else {
9810
+ viewerRef.current?.previewInitials(fieldId, dataUrl);
9811
+ }
9812
+ }, [setSignatureOriginal]);
8097
9813
  const {
8098
9814
  attachments,
8099
9815
  isUploading: isUploadingAttachments,
8100
9816
  validationErrors: attachmentErrors,
9817
+ constraints: attachmentConstraints,
8101
9818
  addFiles,
8102
9819
  removeAttachment,
9820
+ clearValidationErrors: clearAttachmentErrors,
8103
9821
  formatSize,
8104
9822
  validateAll: validateAttachments
8105
9823
  } = useAttachments({
@@ -8122,17 +9840,17 @@ function SubmissionForm({
8122
9840
  const formFieldsViewRef = useRef(null);
8123
9841
  useEffect(() => {
8124
9842
  if (pdfUrl) {
8125
- loadPdf(pdfUrl);
9843
+ const signerEmailForExtraction = multiSignerContext.isMultiSigner ? effectiveSignerEmail : void 0;
9844
+ loadPdf(pdfUrl, signerEmailForExtraction);
8126
9845
  }
8127
- }, [pdfUrl, loadPdf]);
9846
+ }, [pdfUrl, loadPdf, multiSignerContext.isMultiSigner, effectiveSignerEmail]);
8128
9847
  useEffect(() => {
8129
9848
  if (isPdfLoaded && !customFormFields) {
8130
- const signerEmailForExtraction = multiSignerContext.isMultiSigner ? effectiveSignerEmail : void 0;
8131
- extractFormFields(signerEmailForExtraction).catch((error) => {
8132
- logger.error("Failed to extract form fields:", error);
9849
+ extractFormFields().catch((error) => {
9850
+ logger.error("Failed to inject placeholders:", error);
8133
9851
  });
8134
9852
  }
8135
- }, [isPdfLoaded, effectiveSignerEmail, extractFormFields, customFormFields, multiSignerContext.isMultiSigner]);
9853
+ }, [isPdfLoaded, extractFormFields, customFormFields]);
8136
9854
  useEffect(() => {
8137
9855
  if (!isPdfLoaded || !viewerRef.current) {
8138
9856
  return;
@@ -8147,7 +9865,7 @@ function SubmissionForm({
8147
9865
  });
8148
9866
  }, 700);
8149
9867
  return () => clearTimeout(timeoutId);
8150
- }, [isPdfLoaded, filteredFields, updateFieldIndicator, viewerRef]);
9868
+ }, [isPdfLoaded, filteredFields, viewerRef]);
8151
9869
  useEffect(() => {
8152
9870
  if (!isPdfLoaded || !viewerRef.current) {
8153
9871
  return;
@@ -8156,68 +9874,49 @@ function SubmissionForm({
8156
9874
  if (signatures["signature_field_main"]) {
8157
9875
  updateFieldIndicator("signature_field_main");
8158
9876
  }
8159
- if (fieldValues["initials_field_main"]) {
9877
+ if (signatures["initials_field_main"] || fieldValues["initials_field_main"]) {
8160
9878
  updateFieldIndicator("initials_field_main");
8161
9879
  }
8162
9880
  }, 250);
8163
9881
  return () => clearTimeout(timeoutId);
8164
- }, [signatures, fieldValues, updateFieldIndicator, isPdfLoaded, viewerRef]);
9882
+ }, [signatures, isPdfLoaded, viewerRef]);
8165
9883
  useEffect(() => {
8166
- if (!isPdfLoaded || !viewerRef.current || viewMode !== "pdf") {
8167
- return;
8168
- }
9884
+ if (!isPdfLoaded || !viewerRef.current) return;
8169
9885
  const iframe = document.querySelector("iframe");
8170
- if (!iframe?.contentDocument) {
8171
- return;
8172
- }
8173
- const doc = iframe.contentDocument;
8174
- const handlePdfFieldChange = async (event) => {
8175
- const target = event.target;
8176
- const fieldName = target.getAttribute("name") || target.getAttribute("data-element-id") || "";
8177
- if (!fieldName) return;
8178
- let field = filteredFields.find((f) => f.fieldId === fieldName);
8179
- if (!field) {
8180
- field = filteredFields.find((f) => f.id === fieldName);
8181
- }
8182
- if (!field) {
8183
- field = filteredFields.find((f) => f.name === fieldName);
8184
- }
8185
- if (field) {
8186
- let value = "";
8187
- if (target.tagName === "INPUT") {
8188
- const inputElement = target;
8189
- if (inputElement.type === "checkbox") {
8190
- value = inputElement.checked ? "true" : "false";
8191
- } else if (inputElement.type === "radio") {
8192
- if (inputElement.checked) {
8193
- value = inputElement.value || "true";
8194
- }
8195
- } else {
8196
- value = inputElement.value;
8197
- }
8198
- } else if (target.tagName === "SELECT") {
8199
- value = target.value;
8200
- } else if (target.tagName === "TEXTAREA") {
8201
- value = target.value;
9886
+ if (!iframe?.contentDocument) return;
9887
+ const reapplyPreviews = () => {
9888
+ const doc = iframe.contentDocument;
9889
+ if (!doc) return;
9890
+ const mainSig = signaturesRef.current["signature_field_main"];
9891
+ if (mainSig) {
9892
+ const existingSigPreviews = doc.querySelectorAll(".signiphi-signature-preview");
9893
+ if (existingSigPreviews.length === 0) {
9894
+ viewerRef.current?.previewSignature("signature_field_main", mainSig);
9895
+ }
9896
+ }
9897
+ const mainInitials = fieldValuesRef.current["initials_field_main"];
9898
+ if (mainInitials) {
9899
+ const existingInitialsPreviews = doc.querySelectorAll(".signiphi-initials-preview");
9900
+ if (existingInitialsPreviews.length === 0) {
9901
+ viewerRef.current?.previewInitials("initials_field_main", mainInitials);
8202
9902
  }
8203
- setFieldValue(field.id, value);
8204
- logger.info(`[PDF FIELD CHANGE] Field ${field.id} changed to: "${value}"`);
8205
- updateFieldIndicator(field.id);
8206
9903
  }
8207
9904
  };
8208
- const formFields2 = doc.querySelectorAll("input, select, textarea");
8209
- formFields2.forEach((field) => {
8210
- field.addEventListener("change", handlePdfFieldChange);
8211
- field.addEventListener("input", handlePdfFieldChange);
9905
+ let debounceTimeout;
9906
+ const debouncedReapply = () => {
9907
+ clearTimeout(debounceTimeout);
9908
+ debounceTimeout = setTimeout(reapplyPreviews, 150);
9909
+ };
9910
+ const observer = new MutationObserver(debouncedReapply);
9911
+ const annotationLayers = iframe.contentDocument.querySelectorAll(".annotationLayer");
9912
+ annotationLayers.forEach((layer) => {
9913
+ observer.observe(layer, { childList: true, subtree: false });
8212
9914
  });
8213
- logger.info("[PDF FIELD CHANGE] Attached change listeners to", formFields2.length, "PDF form fields");
8214
9915
  return () => {
8215
- formFields2.forEach((field) => {
8216
- field.removeEventListener("change", handlePdfFieldChange);
8217
- field.removeEventListener("input", handlePdfFieldChange);
8218
- });
9916
+ observer.disconnect();
9917
+ clearTimeout(debounceTimeout);
8219
9918
  };
8220
- }, [isPdfLoaded, viewMode, filteredFields, setFieldValue, updateFieldIndicator]);
9919
+ }, [isPdfLoaded]);
8221
9920
  useEffect(() => {
8222
9921
  const fieldsWithAcks = filteredFields.filter((f) => f.acknowledgements && f.acknowledgements.length > 0);
8223
9922
  if (fieldsWithAcks.length > 0) {
@@ -8241,24 +9940,33 @@ function SubmissionForm({
8241
9940
  })));
8242
9941
  }, [filteredFields]);
8243
9942
  useEffect(() => {
8244
- if (!isPdfLoaded || !viewerRef.current?.attachFieldClickInterceptors) {
9943
+ if (isPdfLoaded && viewerRef.current?.setFieldMetadata && filteredFields.length > 0) {
9944
+ viewerRef.current.setFieldMetadata(filteredFields);
9945
+ logger.info("[FIELD METADATA] Set field metadata for PDF viewer:", filteredFields.length, "fields");
9946
+ }
9947
+ }, [isPdfLoaded, filteredFields]);
9948
+ useEffect(() => {
9949
+ if (!isPdfLoaded || !viewerRef.current?.attachFieldClickInterceptors || !viewerRef.current?.attachFieldChangeListeners) {
8245
9950
  return;
8246
9951
  }
8247
- const timeoutId = setTimeout(() => {
8248
- viewerRef.current?.attachFieldClickInterceptors((fieldName) => {
9952
+ let changeListenerCleanup = null;
9953
+ const timeoutId = setTimeout(async () => {
9954
+ changeListenerCleanup = await viewerRef.current?.attachFieldChangeListeners((fieldName, value) => {
9955
+ const field = resolveField(filteredFields, fieldName);
9956
+ if (field) {
9957
+ setFieldValue(field.id, value);
9958
+ fieldValuesRef.current = {
9959
+ ...fieldValuesRef.current,
9960
+ [field.id]: value
9961
+ };
9962
+ logger.info(`[PDF FIELD CHANGE] Field ${field.id} changed to: "${value}"`);
9963
+ updateFieldIndicatorRef.current(field.id);
9964
+ }
9965
+ }) || null;
9966
+ await viewerRef.current?.attachFieldClickInterceptors((fieldName, element) => {
8249
9967
  logger.info(`[PDF Click] Checking field: ${fieldName}`);
8250
9968
  logger.info(`[PDF Click] Available fields:`, filteredFields.map((f) => ({ id: f.id, fieldId: f.fieldId, name: f.name, acks: f.acknowledgements?.length || 0 })));
8251
- let field = filteredFields.find((f) => f.fieldId === fieldName);
8252
- if (!field) {
8253
- field = filteredFields.find((f) => f.id === fieldName);
8254
- }
8255
- if (!field) {
8256
- field = filteredFields.find((f) => f.name === fieldName);
8257
- }
8258
- if (!field) {
8259
- const cleanFieldName = fieldName.replace(/_signature$|_initials$|_date$/i, "");
8260
- field = filteredFields.find((f) => f.fieldId === cleanFieldName || f.id === cleanFieldName || f.name === cleanFieldName);
8261
- }
9969
+ const field = resolveField(filteredFields, fieldName);
8262
9970
  if (!field) {
8263
9971
  logger.info(`[PDF Click] Field not found: ${fieldName}`);
8264
9972
  return true;
@@ -8273,6 +9981,7 @@ function SubmissionForm({
8273
9981
  if (!isAcked) {
8274
9982
  logger.info(`[PDF Click] Blocking ${field.type} field and showing acknowledgement modal`);
8275
9983
  setCurrentAcknowledgementField(field);
9984
+ setCurrentAcknowledgementFieldName(fieldName);
8276
9985
  setAcknowledgementModalOpen(true);
8277
9986
  return false;
8278
9987
  }
@@ -8287,6 +9996,10 @@ function SubmissionForm({
8287
9996
  setSignature(field.id, collected);
8288
9997
  } else if (field.type === "initials") {
8289
9998
  setFieldValue(field.id, collected);
9999
+ fieldValuesRef.current = {
10000
+ ...fieldValuesRef.current,
10001
+ [field.id]: collected
10002
+ };
8290
10003
  }
8291
10004
  updateFieldIndicator(field.id);
8292
10005
  }
@@ -8315,15 +10028,27 @@ function SubmissionForm({
8315
10028
  return false;
8316
10029
  }
8317
10030
  let shouldBlock = false;
8318
- if (!field.acknowledgements || field.acknowledgements.length === 0) {
8319
- logger.info(`[PDF Click] Field has no acknowledgements`);
10031
+ const wasRecentlyAcknowledged = recentlyAcknowledgedFieldsRef.current.has(field.id);
10032
+ if (wasRecentlyAcknowledged) {
10033
+ logger.info(`[PDF Click] Field was recently acknowledged, skipping check`);
8320
10034
  shouldBlock = false;
8321
10035
  } else {
8322
- const isAcked = isFieldFullyAcknowledged(field.id);
8323
- logger.info(`[PDF Click] Field is fully acknowledged: ${isAcked}`);
8324
- shouldBlock = !isAcked;
10036
+ if (!field.acknowledgements || field.acknowledgements.length === 0) {
10037
+ logger.info(`[PDF Click] Field has no acknowledgements`);
10038
+ shouldBlock = false;
10039
+ } else {
10040
+ const isAcked = isFieldFullyAcknowledged(field.id);
10041
+ logger.info(`[PDF Click] Field is fully acknowledged: ${isAcked}`);
10042
+ shouldBlock = !isAcked;
10043
+ }
8325
10044
  }
8326
- if (!shouldBlock) {
10045
+ if (shouldBlock) {
10046
+ logger.info(`[PDF Click] Blocking field and showing modal`);
10047
+ setCurrentAcknowledgementField(field);
10048
+ setCurrentAcknowledgementFieldName(fieldName);
10049
+ currentAcknowledgementElementRef.current = element || null;
10050
+ acknowledgementTriggerSourceRef.current = "pdf-click";
10051
+ setAcknowledgementModalOpen(true);
8327
10052
  try {
8328
10053
  const iframe = document.querySelector("iframe");
8329
10054
  if (iframe?.contentDocument) {
@@ -8331,23 +10056,140 @@ function SubmissionForm({
8331
10056
  navigationHighlights.forEach((el) => el.classList.remove("active-required-field"));
8332
10057
  const navigationAnnotationHighlights = iframe.contentDocument.querySelectorAll(".active-required-field-annotation");
8333
10058
  navigationAnnotationHighlights.forEach((el) => el.classList.remove("active-required-field-annotation"));
8334
- logger.info("[PDF Click] Cleared navigation highlights (interaction allowed)");
10059
+ logger.info("[PDF Click] Cleared navigation highlights (blocked)");
8335
10060
  }
8336
10061
  } catch (error) {
8337
10062
  logger.warn("[PDF Click] Failed to clear navigation highlights:", error);
8338
10063
  }
10064
+ return false;
8339
10065
  }
8340
- if (shouldBlock) {
8341
- logger.info(`[PDF Click] Blocking field and showing modal`);
8342
- setCurrentAcknowledgementField(field);
8343
- setAcknowledgementModalOpen(true);
10066
+ if (field.type === "date") {
10067
+ logger.info(`[PDF Click] Opening calendar for date field: ${field.id}`);
10068
+ try {
10069
+ const iframe = document.querySelector("iframe");
10070
+ if (iframe?.contentDocument) {
10071
+ const formElement = iframe.contentDocument.querySelector(
10072
+ `input[name="${fieldName}"], input[data-element-id="${fieldName}"]`
10073
+ );
10074
+ if (formElement) {
10075
+ const rect = formElement.getBoundingClientRect();
10076
+ const iframeRect = iframe.getBoundingClientRect();
10077
+ const position = {
10078
+ x: Math.round(iframeRect.left + rect.left),
10079
+ y: Math.round(iframeRect.top + rect.top),
10080
+ width: Math.round(rect.width),
10081
+ height: Math.round(rect.height)
10082
+ };
10083
+ setCurrentDateField(field);
10084
+ setDateFieldPosition(position);
10085
+ setDateCalendarOpen(true);
10086
+ }
10087
+ }
10088
+ } catch (error) {
10089
+ logger.error("[PDF Click] Failed to get field position:", error);
10090
+ }
10091
+ try {
10092
+ const iframe = document.querySelector("iframe");
10093
+ if (iframe?.contentDocument) {
10094
+ const navigationHighlights = iframe.contentDocument.querySelectorAll(".active-required-field");
10095
+ navigationHighlights.forEach((el) => el.classList.remove("active-required-field"));
10096
+ const navigationAnnotationHighlights = iframe.contentDocument.querySelectorAll(".active-required-field-annotation");
10097
+ navigationAnnotationHighlights.forEach((el) => el.classList.remove("active-required-field-annotation"));
10098
+ logger.info("[PDF Click] Cleared navigation highlights (calendar)");
10099
+ }
10100
+ } catch (error) {
10101
+ logger.warn("[PDF Click] Failed to clear navigation highlights:", error);
10102
+ }
8344
10103
  return false;
8345
10104
  }
10105
+ if (!shouldBlock) {
10106
+ try {
10107
+ const iframe = document.querySelector("iframe");
10108
+ if (iframe?.contentDocument) {
10109
+ const navigationHighlights = iframe.contentDocument.querySelectorAll(".active-required-field");
10110
+ navigationHighlights.forEach((el) => el.classList.remove("active-required-field"));
10111
+ const navigationAnnotationHighlights = iframe.contentDocument.querySelectorAll(".active-required-field-annotation");
10112
+ navigationAnnotationHighlights.forEach((el) => el.classList.remove("active-required-field-annotation"));
10113
+ logger.info("[PDF Click] Cleared navigation highlights (interaction allowed)");
10114
+ }
10115
+ } catch (error) {
10116
+ logger.warn("[PDF Click] Failed to clear navigation highlights:", error);
10117
+ }
10118
+ }
8346
10119
  return true;
8347
10120
  });
8348
10121
  }, 500);
8349
- return () => clearTimeout(timeoutId);
10122
+ return () => {
10123
+ clearTimeout(timeoutId);
10124
+ if (changeListenerCleanup) {
10125
+ changeListenerCleanup();
10126
+ }
10127
+ };
8350
10128
  }, [isPdfLoaded, filteredFields, isFieldFullyAcknowledged, hasCollectedSignature, getCollectedSignature, setSignature, setFieldValue, updateFieldIndicator]);
10129
+ useEffect(() => {
10130
+ if (!isPdfLoaded || !viewerRef.current?.addDateFieldIndicator) {
10131
+ return;
10132
+ }
10133
+ const timeoutId = setTimeout(() => {
10134
+ const dateFields = filteredFields.filter((f) => f.type === "date");
10135
+ logger.info(`[DATE INDICATORS] Adding calendar icons to ${dateFields.length} date fields`);
10136
+ dateFields.forEach((field) => {
10137
+ try {
10138
+ viewerRef.current?.addDateFieldIndicator(field.name);
10139
+ logger.info(`[DATE INDICATORS] Added calendar icon to: ${field.name}`);
10140
+ } catch (error) {
10141
+ logger.error(`[DATE INDICATORS] Failed to add calendar icon to ${field.name}:`, error);
10142
+ }
10143
+ });
10144
+ const iframe = document.querySelector("iframe");
10145
+ if (iframe?.contentDocument) {
10146
+ const calendarIndicators = iframe.contentDocument.querySelectorAll(".signiphi-date-field-indicator");
10147
+ calendarIndicators.forEach((indicator) => {
10148
+ indicator.addEventListener("click", (event) => {
10149
+ event.stopPropagation();
10150
+ event.preventDefault();
10151
+ const annotationSection = indicator.closest("section");
10152
+ if (annotationSection) {
10153
+ const formElement = annotationSection.querySelector("input, textarea");
10154
+ if (formElement) {
10155
+ const fieldName = formElement.getAttribute("name") || formElement.getAttribute("data-element-id") || "";
10156
+ let field = filteredFields.find((f) => f.fieldId === fieldName || f.id === fieldName || f.name === fieldName);
10157
+ if (!field) {
10158
+ const cleanFieldName = fieldName.replace(/_date$/i, "");
10159
+ field = filteredFields.find((f) => f.fieldId === cleanFieldName || f.id === cleanFieldName || f.name === cleanFieldName);
10160
+ }
10161
+ if (field && field.type === "date") {
10162
+ logger.info(`[DATE INDICATOR CLICK] Opening calendar for: ${field.id}`);
10163
+ const hasAcknowledgements = field.acknowledgements && field.acknowledgements.length > 0;
10164
+ if (hasAcknowledgements && !isFieldFullyAcknowledged(field.id)) {
10165
+ logger.info(`[DATE INDICATOR CLICK] Showing acknowledgement modal first`);
10166
+ setCurrentAcknowledgementField(field);
10167
+ setCurrentAcknowledgementFieldName(fieldName);
10168
+ setAcknowledgementModalOpen(true);
10169
+ return;
10170
+ }
10171
+ const rect = formElement.getBoundingClientRect();
10172
+ const iframeRect = iframe.getBoundingClientRect();
10173
+ const position = {
10174
+ x: Math.round(iframeRect.left + rect.left),
10175
+ y: Math.round(iframeRect.top + rect.top),
10176
+ width: Math.round(rect.width),
10177
+ height: Math.round(rect.height)
10178
+ };
10179
+ logger.info("[DATE INDICATOR CLICK] Opening calendar at position:", position);
10180
+ setCurrentDateField(field);
10181
+ setDateFieldPosition(position);
10182
+ setDateCalendarOpen(true);
10183
+ }
10184
+ }
10185
+ }
10186
+ });
10187
+ });
10188
+ logger.info(`[DATE INDICATORS] Attached click handlers to ${calendarIndicators.length} calendar icons`);
10189
+ }
10190
+ }, 600);
10191
+ return () => clearTimeout(timeoutId);
10192
+ }, [isPdfLoaded, filteredFields, isFieldFullyAcknowledged]);
8351
10193
  const prevViewModeRef = useRef(viewMode);
8352
10194
  useEffect(() => {
8353
10195
  const syncFieldsOnViewChange = async () => {
@@ -8387,6 +10229,9 @@ function SubmissionForm({
8387
10229
  }
8388
10230
  }
8389
10231
  }
10232
+ if (field.type === "radio" && (normalizedPdfValue === "true" || normalizedPdfValue === "false")) {
10233
+ normalizedPdfValue = void 0;
10234
+ }
8390
10235
  if (field.type === "date" && pdfValue) {
8391
10236
  const validation = parseAndValidateDate(pdfValue);
8392
10237
  if (!validation.isValid) {
@@ -8400,8 +10245,12 @@ function SubmissionForm({
8400
10245
  normalizedPdfValue = validation.isoString;
8401
10246
  }
8402
10247
  }
8403
- if (normalizedPdfValue && normalizedPdfValue !== currentReactValue) {
10248
+ const isUnresolvedRadioIndex = field.type === "radio" && normalizedPdfValue?.includes("__RADIO_OPTION_INDEX_");
10249
+ const pdfFieldReported = field.id in pdfValues;
10250
+ if (normalizedPdfValue && normalizedPdfValue !== currentReactValue && !isUnresolvedRadioIndex) {
8404
10251
  setFieldValue(field.id, normalizedPdfValue);
10252
+ } else if (pdfFieldReported && !normalizedPdfValue && currentReactValue) {
10253
+ setFieldValue(field.id, "");
8405
10254
  }
8406
10255
  }
8407
10256
  if (invalidDates.length > 0) {
@@ -8468,14 +10317,76 @@ function SubmissionForm({
8468
10317
  }
8469
10318
  }
8470
10319
  updateFieldIndicator(fieldId);
10320
+ if (field && field.type === "initials") {
10321
+ viewerRef.current?.previewInitials("initials_field_main", value || null);
10322
+ }
8471
10323
  },
8472
10324
  [setFieldValue, filteredFields, updateFieldIndicator]
8473
10325
  );
8474
- const handleAcknowledgementRequired = useCallback((field) => {
10326
+ const handleAcknowledgementRequired = useCallback((field, element, fieldName) => {
8475
10327
  if (readOnly) return;
8476
10328
  setCurrentAcknowledgementField(field);
10329
+ if (element) {
10330
+ currentAcknowledgementElementRef.current = element;
10331
+ logger.info(`[ACK REQUIRED] Stored element reference for field ${field.id}`, {
10332
+ elementTag: element.tagName,
10333
+ dataElementId: element.getAttribute("data-element-id")
10334
+ });
10335
+ }
10336
+ if (fieldName) {
10337
+ setCurrentAcknowledgementFieldName(fieldName);
10338
+ logger.info(`[ACK REQUIRED] Stored fieldName: ${fieldName} for field ${field.id}`);
10339
+ }
8477
10340
  setAcknowledgementModalOpen(true);
8478
10341
  }, [readOnly]);
10342
+ const handleAcknowledgementModalOpenChange = useCallback((open) => {
10343
+ setAcknowledgementModalOpen(open);
10344
+ if (acknowledgementCleanupTimeoutRef.current) {
10345
+ clearTimeout(acknowledgementCleanupTimeoutRef.current);
10346
+ acknowledgementCleanupTimeoutRef.current = null;
10347
+ }
10348
+ if (!open) {
10349
+ acknowledgementCleanupTimeoutRef.current = setTimeout(() => {
10350
+ if (acknowledgementTriggerSourceRef.current !== null) {
10351
+ acknowledgementTriggerSourceRef.current = null;
10352
+ currentAcknowledgementElementRef.current = null;
10353
+ logger.info("[ACK MODAL] Cleaned up refs after modal dismiss (not completed)");
10354
+ }
10355
+ acknowledgementCleanupTimeoutRef.current = null;
10356
+ }, 500);
10357
+ }
10358
+ }, []);
10359
+ const handleDateSelect = useCallback(async (dateValue) => {
10360
+ if (readOnly || !currentDateField) return;
10361
+ logger.info(`[DATE SELECT] Selected date for field ${currentDateField.id}:`, dateValue);
10362
+ setFieldValue(currentDateField.id, dateValue);
10363
+ try {
10364
+ await setFormFieldValues({ [currentDateField.id]: dateValue });
10365
+ const pdfApp = viewerRef.current?.getPDFViewerApplication?.();
10366
+ const pdfDocument = pdfApp?.pdfDocument;
10367
+ if (pdfDocument?.annotationStorage && typeof pdfDocument.getFieldObjects === "function") {
10368
+ const fieldObjects = await pdfDocument.getFieldObjects() || {};
10369
+ const widgets = fieldObjects[currentDateField.id];
10370
+ if (widgets?.[0]?.id) {
10371
+ pdfDocument.annotationStorage.setValue(widgets[0].id, { value: dateValue });
10372
+ }
10373
+ }
10374
+ logger.info(`[DATE SELECT] Updated PDF field ${currentDateField.id}`);
10375
+ } catch (error) {
10376
+ logger.error(`[DATE SELECT] Failed to update PDF field:`, error);
10377
+ }
10378
+ if (dateValue) {
10379
+ const validation = parseAndValidateDate(dateValue);
10380
+ if (validation.isValid) {
10381
+ setValidationErrors(
10382
+ (prev) => prev.filter(
10383
+ (error) => !error.includes("date field(s) contain invalid values") || !error.includes(currentDateField.label || currentDateField.id)
10384
+ )
10385
+ );
10386
+ }
10387
+ }
10388
+ updateFieldIndicator(currentDateField.id);
10389
+ }, [readOnly, currentDateField, setFieldValue, setFormFieldValues, updateFieldIndicator]);
8479
10390
  const handleSignatureClick = useCallback((field) => {
8480
10391
  if (readOnly) return;
8481
10392
  logger.info(`[SIGNATURE CLICK] Field:`, {
@@ -8491,6 +10402,10 @@ function SubmissionForm({
8491
10402
  setSignature(field.id, collected);
8492
10403
  } else if (field.type === "initials") {
8493
10404
  setFieldValue(field.id, collected);
10405
+ fieldValuesRef.current = {
10406
+ ...fieldValuesRef.current,
10407
+ [field.id]: collected
10408
+ };
8494
10409
  }
8495
10410
  logger.info(`[SIGNATURE CLICK] Auto-placed ${fieldType} for field ${field.id}`);
8496
10411
  updateFieldIndicator(field.id);
@@ -8532,6 +10447,7 @@ function SubmissionForm({
8532
10447
  }
8533
10448
  }
8534
10449
  let highlightedFieldElement = null;
10450
+ let pdfFieldName = field.name;
8535
10451
  try {
8536
10452
  const iframe = document.querySelector("iframe");
8537
10453
  if (iframe?.contentDocument) {
@@ -8592,7 +10508,6 @@ function SubmissionForm({
8592
10508
  iframe.contentDocument.head.appendChild(style);
8593
10509
  logger.info("[REQUIRED NAV] Added CSS styles");
8594
10510
  }
8595
- let pdfFieldName = field.name;
8596
10511
  const pdfApp = viewerRef.current?.getPDFViewerApplication?.();
8597
10512
  if (pdfApp?.pdfDocument?.getData) {
8598
10513
  try {
@@ -8713,7 +10628,8 @@ function SubmissionForm({
8713
10628
  if (field.acknowledgements && field.acknowledgements.length > 0) {
8714
10629
  if (!isFieldFullyAcknowledged(field.id)) {
8715
10630
  logger.info(`[REQUIRED NAV] Field has unacknowledged items, opening acknowledgement modal`);
8716
- handleAcknowledgementRequired(field);
10631
+ acknowledgementTriggerSourceRef.current = "navigation";
10632
+ handleAcknowledgementRequired(field, highlightedFieldElement || void 0, pdfFieldName);
8717
10633
  return;
8718
10634
  }
8719
10635
  }
@@ -8767,13 +10683,25 @@ function SubmissionForm({
8767
10683
  setFieldValue(currentInitialsField.id, initialsText);
8768
10684
  collectSignature("initials", initialsText);
8769
10685
  setFieldValue("initials_field_main", initialsText);
10686
+ fieldValuesRef.current = {
10687
+ ...fieldValuesRef.current,
10688
+ [currentInitialsField.id]: initialsText,
10689
+ "initials_field_main": initialsText
10690
+ };
10691
+ const fieldIdToUpdate = currentInitialsField.id;
8770
10692
  setCurrentInitialsField(null);
8771
10693
  setInitialsModalOpen(false);
10694
+ updateFieldIndicator(fieldIdToUpdate);
10695
+ viewerRef.current?.previewInitials("initials_field_main", initialsText || null);
8772
10696
  },
8773
- [currentInitialsField, setFieldValue, collectSignature]
10697
+ [currentInitialsField, setFieldValue, collectSignature, updateFieldIndicator]
8774
10698
  );
8775
10699
  const handleAcknowledgementComplete = useCallback(async (fieldId) => {
8776
10700
  logger.info(`[ACK COMPLETE] Field ${fieldId} fully acknowledged, continuing interaction`);
10701
+ recentlyAcknowledgedFieldsRef.current.set(fieldId, Date.now());
10702
+ setTimeout(() => {
10703
+ recentlyAcknowledgedFieldsRef.current.delete(fieldId);
10704
+ }, 3e3);
8777
10705
  const field = filteredFields.find((f) => f.id === fieldId);
8778
10706
  if (!field) {
8779
10707
  logger.warn(`[ACK COMPLETE] Field ${fieldId} not found`);
@@ -8801,30 +10729,165 @@ function SubmissionForm({
8801
10729
  setInitialsModalOpen(true);
8802
10730
  }
8803
10731
  }
10732
+ } else if (field.type === "date") {
10733
+ logger.info(`[ACK COMPLETE] Date field, opening calendar`);
10734
+ try {
10735
+ await new Promise((resolve) => setTimeout(resolve, 200));
10736
+ const fieldName = currentAcknowledgementFieldName || field.fieldId || field.name;
10737
+ logger.info(`[ACK COMPLETE] Looking for field with name: ${fieldName}`);
10738
+ const iframe = document.querySelector("iframe");
10739
+ if (iframe?.contentDocument) {
10740
+ const formElement = iframe.contentDocument.querySelector(
10741
+ `input[name="${fieldName}"], input[data-element-id="${fieldName}"]`
10742
+ );
10743
+ if (formElement) {
10744
+ const rect = formElement.getBoundingClientRect();
10745
+ const iframeRect = iframe.getBoundingClientRect();
10746
+ const position = {
10747
+ x: Math.round(iframeRect.left + rect.left),
10748
+ y: Math.round(iframeRect.top + rect.top),
10749
+ width: Math.round(rect.width),
10750
+ height: Math.round(rect.height)
10751
+ };
10752
+ setCurrentDateField(field);
10753
+ setDateFieldPosition(position);
10754
+ setDateCalendarOpen(true);
10755
+ logger.info(`[ACK COMPLETE] Opened calendar for date field in PDF view`);
10756
+ } else {
10757
+ logger.warn(`[ACK COMPLETE] Date field element not found in PDF with name: ${fieldName}`);
10758
+ }
10759
+ }
10760
+ } catch (error) {
10761
+ logger.error("[ACK COMPLETE] Failed to open calendar:", error);
10762
+ }
8804
10763
  } else {
8805
- logger.info(`[ACK COMPLETE] Regular field, attempting to focus in PDF`);
10764
+ const triggerSource = acknowledgementTriggerSourceRef.current;
10765
+ logger.info(`[ACK COMPLETE] Regular field (${field.type}), trigger: ${triggerSource}`);
10766
+ if (triggerSource === "navigation" && (field.type === "checkbox" || field.type === "radio")) {
10767
+ logger.info(`[ACK COMPLETE] Skipping auto-interaction for ${field.type} via navigation (unclear intent)`);
10768
+ acknowledgementTriggerSourceRef.current = null;
10769
+ currentAcknowledgementElementRef.current = null;
10770
+ return;
10771
+ }
8806
10772
  try {
8807
- await new Promise((resolve) => setTimeout(resolve, 100));
10773
+ await new Promise((resolve) => setTimeout(resolve, 200));
10774
+ const fieldName = currentAcknowledgementFieldName || field.fieldId || field.name;
10775
+ logger.info(`[ACK COMPLETE] Looking for field with name: ${fieldName}`);
8808
10776
  const iframe = document.querySelector("iframe");
8809
10777
  if (iframe?.contentDocument) {
8810
- const fieldElement = iframe.contentDocument.querySelector(`[data-element-id="${field.id}"]`);
8811
- if (fieldElement) {
8812
- fieldElement.focus();
8813
- fieldElement.scrollIntoView({ behavior: "smooth", block: "center" });
8814
- logger.info(`[ACK COMPLETE] Focused field in PDF`);
10778
+ if (field.type === "dropdown") {
10779
+ const storedElement = currentAcknowledgementElementRef.current;
10780
+ let selectElement = null;
10781
+ if (storedElement && storedElement.tagName === "SELECT") {
10782
+ selectElement = storedElement;
10783
+ logger.info(`[ACK COMPLETE] Using stored dropdown element`);
10784
+ } else {
10785
+ selectElement = iframe.contentDocument.querySelector(
10786
+ `select[name="${fieldName}"], select[data-element-id="${fieldName}"]`
10787
+ );
10788
+ logger.info(`[ACK COMPLETE] Querying for dropdown element`);
10789
+ }
10790
+ if (selectElement) {
10791
+ selectElement.scrollIntoView({ behavior: "smooth", block: "center" });
10792
+ await new Promise((resolve) => setTimeout(resolve, 200));
10793
+ selectElement.focus();
10794
+ await new Promise((resolve) => setTimeout(resolve, 100));
10795
+ try {
10796
+ if ("showPicker" in selectElement) {
10797
+ selectElement.showPicker();
10798
+ logger.info(`[ACK COMPLETE] Opened dropdown using showPicker() API`);
10799
+ } else {
10800
+ logger.info(`[ACK COMPLETE] showPicker() not supported, dropdown is focused`);
10801
+ }
10802
+ } catch (error) {
10803
+ logger.warn(`[ACK COMPLETE] showPicker() failed (may require user gesture):`, error);
10804
+ }
10805
+ } else {
10806
+ logger.warn(`[ACK COMPLETE] Dropdown element not found with name: ${fieldName}`);
10807
+ }
10808
+ } else if (field.type === "radio") {
10809
+ const storedElement = currentAcknowledgementElementRef.current;
10810
+ if (storedElement && storedElement.getAttribute("type") === "radio") {
10811
+ storedElement.scrollIntoView({ behavior: "smooth", block: "center" });
10812
+ await new Promise((resolve) => setTimeout(resolve, 200));
10813
+ storedElement.click();
10814
+ logger.info(`[ACK COMPLETE] Clicked specific radio option with value: ${storedElement.value}`);
10815
+ } else {
10816
+ const radioElement = iframe.contentDocument.querySelector(
10817
+ `input[type="radio"][name="${fieldName}"], input[type="radio"][data-element-id="${fieldName}"]`
10818
+ );
10819
+ if (radioElement) {
10820
+ radioElement.scrollIntoView({ behavior: "smooth", block: "center" });
10821
+ await new Promise((resolve) => setTimeout(resolve, 200));
10822
+ radioElement.focus();
10823
+ logger.info(`[ACK COMPLETE] Focused radio button group (fallback)`);
10824
+ } else {
10825
+ logger.warn(`[ACK COMPLETE] Radio element not found with name: ${fieldName}`);
10826
+ }
10827
+ }
10828
+ } else {
10829
+ const fieldElement = iframe.contentDocument.querySelector(
10830
+ `input[name="${fieldName}"], input[data-element-id="${fieldName}"], textarea[name="${fieldName}"], textarea[data-element-id="${fieldName}"]`
10831
+ );
10832
+ if (fieldElement) {
10833
+ fieldElement.scrollIntoView({ behavior: "smooth", block: "center" });
10834
+ await new Promise((resolve) => setTimeout(resolve, 200));
10835
+ if (field.type === "checkbox") {
10836
+ fieldElement.click();
10837
+ logger.info(`[ACK COMPLETE] Clicked checkbox field`);
10838
+ } else {
10839
+ fieldElement.focus();
10840
+ logger.info(`[ACK COMPLETE] Focused text field`);
10841
+ }
10842
+ } else {
10843
+ logger.warn(`[ACK COMPLETE] Field element not found with name: ${fieldName}`);
10844
+ }
8815
10845
  }
8816
10846
  }
8817
10847
  } catch (error) {
8818
- logger.warn("[ACK COMPLETE] Failed to focus field:", error);
10848
+ logger.warn("[ACK COMPLETE] Failed to interact with field:", error);
10849
+ } finally {
10850
+ acknowledgementTriggerSourceRef.current = null;
10851
+ currentAcknowledgementElementRef.current = null;
8819
10852
  }
8820
10853
  }
8821
- }, [filteredFields, hasCollectedSignature, getCollectedSignature, setSignature]);
10854
+ }, [filteredFields, hasCollectedSignature, getCollectedSignature, setSignature, setFieldValue, currentAcknowledgementFieldName]);
10855
+ const handleBulkAcknowledgementComplete = useCallback((fieldIds) => {
10856
+ logger.info(`[BULK ACK COMPLETE] Refreshing indicators for ${fieldIds.length} fields:`, fieldIds);
10857
+ fieldIds.forEach((fieldId) => {
10858
+ updateFieldIndicatorRef.current(fieldId);
10859
+ });
10860
+ const hasSignatureFields = fieldIds.some((id) => {
10861
+ const field = filteredFields.find((f) => f.id === id);
10862
+ return field?.type === "signature";
10863
+ });
10864
+ const hasInitialsFields2 = fieldIds.some((id) => {
10865
+ const field = filteredFields.find((f) => f.id === id);
10866
+ return field?.type === "initials";
10867
+ });
10868
+ if (hasSignatureFields) {
10869
+ updateFieldIndicatorRef.current("signature_field_main");
10870
+ }
10871
+ if (hasInitialsFields2) {
10872
+ updateFieldIndicatorRef.current("initials_field_main");
10873
+ }
10874
+ }, [filteredFields]);
8822
10875
  const validateForm = useCallback(async () => {
8823
10876
  const validationErrors2 = [];
8824
10877
  const currentFieldValues = fieldValuesRef.current;
8825
10878
  const currentSignatures = signaturesRef.current;
8826
10879
  const pdfFieldValues = await getFormFieldValues();
8827
10880
  const allFieldValues = { ...pdfFieldValues, ...currentFieldValues };
10881
+ const currentTouched = touchedRef.current;
10882
+ for (const [fieldId, pdfVal] of Object.entries(pdfFieldValues)) {
10883
+ if (pdfVal === "") {
10884
+ const reactVal = currentFieldValues[fieldId];
10885
+ const fieldTouched = currentTouched[fieldId] || false;
10886
+ if (!reactVal || reactVal.trim() === "" || !fieldTouched) {
10887
+ allFieldValues[fieldId] = "";
10888
+ }
10889
+ }
10890
+ }
8828
10891
  console.log("[VALIDATION] === MERGING SIGNATURES INTO allFieldValues ===");
8829
10892
  console.log("[VALIDATION] currentSignatures:", currentSignatures);
8830
10893
  for (const [fieldId, signatureValue] of Object.entries(currentSignatures)) {
@@ -8851,13 +10914,12 @@ function SubmissionForm({
8851
10914
  if (value && value.trim() !== "") {
8852
10915
  const validation = parseAndValidateDate(value);
8853
10916
  if (!validation.isValid) {
8854
- const fieldLabel = field.label || field.id;
8855
10917
  validationErrors2.push(
8856
- `${fieldLabel}: Invalid date format. Please use MM/DD/YYYY, YYYY-MM-DD, or similar standard format.`
10918
+ `${getFieldDisplayName(field)}: Invalid date format. Please use MM/DD/YYYY, YYYY-MM-DD, or similar standard format.`
8857
10919
  );
8858
10920
  }
8859
10921
  } else if (field.required) {
8860
- validationErrors2.push(`${field.label || field.id} is required`);
10922
+ validationErrors2.push(`${getFieldDisplayName(field)} is required`);
8861
10923
  }
8862
10924
  }
8863
10925
  const signatureFields = filteredFields.filter((f) => f.type === "signature");
@@ -8875,12 +10937,21 @@ function SubmissionForm({
8875
10937
  console.log("[VALIDATION] Value from allFieldValues:", value ? "(found, truncated): " + value.substring(0, 50) : "(MISSING)");
8876
10938
  if (!value || value.trim() === "") {
8877
10939
  console.log("[VALIDATION] ERROR: Field is required but missing!");
8878
- validationErrors2.push(`${field.label} is required`);
10940
+ validationErrors2.push(`${getFieldDisplayName(field)} is required`);
8879
10941
  } else {
8880
10942
  console.log("[VALIDATION] OK: Field has value");
8881
10943
  }
8882
10944
  }
8883
10945
  }
10946
+ const alreadyCheckedTypes = /* @__PURE__ */ new Set(["date", "signature", "initials"]);
10947
+ for (const field of filteredFields) {
10948
+ if (!field.required || alreadyCheckedTypes.has(field.type)) continue;
10949
+ if (field.id === "signature_field_main" || field.id === "initials_field_main") continue;
10950
+ const value = allFieldValues[field.id];
10951
+ if (!value || typeof value === "string" && value.trim() === "") {
10952
+ validationErrors2.push(`${getFieldDisplayName(field)} is required`);
10953
+ }
10954
+ }
8884
10955
  const isFieldsValid = validateFields(currentSignatures);
8885
10956
  if (!isFieldsValid) {
8886
10957
  for (const field of filteredFields) {
@@ -8990,6 +11061,16 @@ function SubmissionForm({
8990
11061
  const currentFieldValues = fieldValuesRef.current;
8991
11062
  const currentSignatures = signaturesRef.current;
8992
11063
  const finalFieldValues = { ...pdfFieldValues, ...currentFieldValues };
11064
+ const currentTouchedSubmit = touchedRef.current;
11065
+ for (const [fieldId, pdfVal] of Object.entries(pdfFieldValues)) {
11066
+ if (pdfVal === "") {
11067
+ const reactVal = currentFieldValues[fieldId];
11068
+ const fieldTouched = currentTouchedSubmit[fieldId] || false;
11069
+ if (!reactVal || reactVal.trim() === "" || !fieldTouched) {
11070
+ finalFieldValues[fieldId] = "";
11071
+ }
11072
+ }
11073
+ }
8993
11074
  console.log("[SUBMIT] === MERGING SIGNATURES INTO finalFieldValues FOR FLATTENING ===");
8994
11075
  console.log("[SUBMIT] currentSignatures:", currentSignatures);
8995
11076
  for (const [fieldId, signatureValue] of Object.entries(currentSignatures)) {
@@ -9012,6 +11093,11 @@ function SubmissionForm({
9012
11093
  }
9013
11094
  }
9014
11095
  if (enableAttachments) {
11096
+ if (attachments.length === 0) {
11097
+ setValidationErrors(["At least one attachment is required"]);
11098
+ setIsSubmitting(false);
11099
+ return;
11100
+ }
9015
11101
  const attachmentValidation = validateAttachments();
9016
11102
  if (!attachmentValidation.isValid) {
9017
11103
  setValidationErrors(attachmentValidation.errors);
@@ -9103,28 +11189,34 @@ function SubmissionForm({
9103
11189
  return false;
9104
11190
  }
9105
11191
  }
9106
- return true;
9107
- }
9108
- for (const field of filteredFields) {
9109
- if (!field.required) continue;
9110
- if (field.id === "signature_field_main" || field.id === "initials_field_main") {
9111
- if (!hasSignature(field.id)) return false;
9112
- continue;
9113
- }
9114
- if (field.type === "signature" || field.type === "initials") {
9115
- if (!hasSignature(field.id)) return false;
9116
- } else {
9117
- const value = fieldValues[field.id];
9118
- if (!value || value.trim() === "") return false;
11192
+ } else {
11193
+ for (const field of filteredFields) {
11194
+ if (!field.required) continue;
11195
+ if (field.id === "signature_field_main" || field.id === "initials_field_main") {
11196
+ if (!hasSignature(field.id)) return false;
11197
+ continue;
11198
+ }
11199
+ if (field.type === "signature" || field.type === "initials") {
11200
+ if (!hasSignature(field.id)) return false;
11201
+ } else {
11202
+ const value = fieldValues[field.id];
11203
+ if (!value || value.trim() === "") return false;
11204
+ }
9119
11205
  }
9120
11206
  }
11207
+ if (enableAttachments && attachments.length === 0) {
11208
+ return false;
11209
+ }
9121
11210
  return true;
9122
- }, [filteredFields, fieldValues, hasSignature, showFullFieldsSidebar]);
11211
+ }, [filteredFields, fieldValues, hasSignature, showFullFieldsSidebar, enableAttachments, attachments]);
9123
11212
  return /* @__PURE__ */ jsxs("div", { className: cn("signiphi-pdf-signer flex flex-col gap-4 md:gap-6 h-full overflow-auto px-2 md:px-0", className), children: [
9124
- documentTitle && /* @__PURE__ */ jsx(Card, { className: "flex-shrink-0 border-0 shadow-none", children: /* @__PURE__ */ jsx(CardHeader, { className: "space-y-2 p-0 pb-3 md:pb-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row items-start justify-between gap-3 sm:gap-4", children: [
9125
- /* @__PURE__ */ jsx(CardTitle, { className: "text-2xl sm:text-3xl md:text-4xl font-bold text-primary tracking-tight flex-1", children: documentTitle }),
9126
- showPoweredBy && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(PoweredBySigniphi, {}) })
9127
- ] }) }) }),
11213
+ (documentTitle || signingInstructions || showPoweredBy) && /* @__PURE__ */ jsxs("div", { className: "flex-shrink-0", children: [
11214
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row items-start justify-between gap-3 sm:gap-4", children: [
11215
+ documentTitle && /* @__PURE__ */ jsx("h1", { className: "text-2xl sm:text-3xl md:text-4xl font-bold text-primary tracking-tight flex-1", children: documentTitle }),
11216
+ showPoweredBy && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(PoweredBySigniphi, {}) })
11217
+ ] }),
11218
+ signingInstructions && /* @__PURE__ */ jsx("div", { className: "text-sm md:text-base text-muted-foreground", children: /* @__PURE__ */ jsx(SigningInstructions, { html: signingInstructions }) })
11219
+ ] }),
9128
11220
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col lg:flex-row gap-4", children: [
9129
11221
  showPdfViewer && /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col bg-muted/30 rounded-lg overflow-hidden relative min-w-0 border-2", children: [
9130
11222
  /* @__PURE__ */ jsx(
@@ -9192,8 +11284,7 @@ function SubmissionForm({
9192
11284
  /* @__PURE__ */ jsx("div", { className: "p-1.5 md:p-2 bg-primary/10 rounded-lg", children: /* @__PURE__ */ jsx(FileText, { className: "h-5 w-5 md:h-6 md:w-6 text-primary" }) }),
9193
11285
  "Sign Document"
9194
11286
  ] }),
9195
- /* @__PURE__ */ jsx(CardDescription, { className: "text-muted-foreground text-sm md:text-base leading-relaxed text-primary", children: customMessage || "Complete the fields in the PDF and sign below" }),
9196
- signingInstructions && /* @__PURE__ */ jsx("div", { className: "mt-2 md:mt-3 text-xs md:text-sm leading-relaxed text-muted-foreground", children: /* @__PURE__ */ jsx(SigningInstructions, { html: signingInstructions }) })
11287
+ /* @__PURE__ */ jsx(CardDescription, { className: "text-muted-foreground text-sm md:text-base leading-relaxed text-primary", children: customMessage || defaultSigningMessage })
9197
11288
  ] }),
9198
11289
  /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4 md:space-y-6 px-4 md:px-6", children: [
9199
11290
  validationErrors.length > 0 && /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
@@ -9311,7 +11402,11 @@ function SubmissionForm({
9311
11402
  onCollapseChange: setEditableFieldsCollapsed
9312
11403
  }
9313
11404
  ) }),
9314
- enableAttachments && /* @__PURE__ */ jsxs("div", { className: "mt-4 md:mt-6", children: [
11405
+ enableAttachments && /* @__PURE__ */ jsxs("div", { className: "mt-4 md:mt-6 mb-6 md:mb-8", children: [
11406
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
11407
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: "Attachments" }),
11408
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-destructive", children: "(Required)" })
11409
+ ] }),
9315
11410
  /* @__PURE__ */ jsx(
9316
11411
  AttachmentUpload,
9317
11412
  {
@@ -9321,10 +11416,12 @@ function SubmissionForm({
9321
11416
  isUploading: isUploadingAttachments,
9322
11417
  disabled: readOnly || isSubmitting,
9323
11418
  maxFiles: maxAttachments,
9324
- formatSize
11419
+ formatSize,
11420
+ constraints: attachmentConstraints,
11421
+ validationErrors: attachmentErrors.length > 0 ? attachmentErrors : void 0,
11422
+ onClearErrors: clearAttachmentErrors
9325
11423
  }
9326
- ),
9327
- attachmentErrors.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-2 p-2 md:p-3 bg-destructive/10 border border-destructive/30 rounded-lg", children: /* @__PURE__ */ jsx("ul", { className: "list-disc list-inside text-xs md:text-sm text-destructive space-y-1", children: attachmentErrors.map((error, index) => /* @__PURE__ */ jsx("li", { children: error }, index)) }) })
11424
+ )
9328
11425
  ] })
9329
11426
  ] }),
9330
11427
  !readOnly && /* @__PURE__ */ jsxs("div", { className: "flex-shrink-0 p-4 md:p-6 border-t bg-gradient-to-b from-background to-muted/20", children: [
@@ -9392,7 +11489,7 @@ function SubmissionForm({
9392
11489
  AcknowledgementModal,
9393
11490
  {
9394
11491
  open: acknowledgementModalOpen,
9395
- onOpenChange: setAcknowledgementModalOpen,
11492
+ onOpenChange: handleAcknowledgementModalOpenChange,
9396
11493
  field: currentAcknowledgementField,
9397
11494
  onAcknowledge: acknowledgeItem,
9398
11495
  isAcknowledged,
@@ -9409,12 +11506,24 @@ function SubmissionForm({
9409
11506
  unacknowledgedCount: unacknowledgedItems.length
9410
11507
  })),
9411
11508
  onAcknowledge: acknowledgeItem,
9412
- isAcknowledged
11509
+ isAcknowledged,
11510
+ onComplete: handleBulkAcknowledgementComplete
11511
+ }
11512
+ ),
11513
+ /* @__PURE__ */ jsx(
11514
+ DateFieldCalendarPopup,
11515
+ {
11516
+ open: dateCalendarOpen,
11517
+ onOpenChange: setDateCalendarOpen,
11518
+ value: currentDateField ? fieldValues[currentDateField.id] || "" : "",
11519
+ onSelect: handleDateSelect,
11520
+ fieldPosition: dateFieldPosition,
11521
+ fieldLabel: currentDateField?.label || currentDateField?.name
9413
11522
  }
9414
11523
  )
9415
11524
  ] });
9416
11525
  }
9417
11526
 
9418
- export { AttachmentValidationError, CDN_VIEWER, CDN_WORKER, CheckboxRenderer, DEFAULT_ATTACHMENT_CONSTRAINTS, DateFieldRenderer, DropdownRenderer, FormFieldError, FormFieldRenderer, FormFieldType, InitialsFieldRenderer, PdfProcessingError, PdfValidationError, PdfViewerStyled as PdfViewer, PdfViewerCore, PdfViewerStyled, PerformanceMonitor, RadioGroupRenderer, SignatureCanvas, SignatureCaptureCore, SignatureFieldRenderer, SignatureModal, SubmissionForm, SubmissionStatus, TextFieldRenderer, TextLabelRenderer, captureAuditTrail, captureDeviceMetadata, captureGeolocation, captureIpAddress, checkAndLogPdfJsVersion, checkPdfJsVersion, createPdfBlobUrl, createPerformanceMonitor, dataUrlToBytes, decodeFieldName, detectFieldType, downloadPdf, extractFieldValue, extractVisibleFormFields, fillFormFieldsWithSignatures, fillPdfWithSignatures, filterFieldsBySigner, findPageIndexByRef, findPageIndexWithFallback, formatFieldName, formatFileSize, generateFallbackLabel, getErrorMessage, getFieldPageNumbers, getPdfJsConfig, getSigniphiMetadata, getWidgetRectangleAndPage, globalPerformanceMonitor, initPdfMetadata, initializePdfJs, isAttachmentValidationError, isAutoGeneratedLabel, isFieldVisibleToSigner, isFormFieldError, isImageType, isInitialsField, isPdfProcessingError, isPdfValidationError, isRequiredField, isSignatureField, isValidISODate, isValidPdf, logVersionCheckWarning, logger, measurePerformance, parseAndValidateDate, pdfToImages, readCurrentPdfFormValues, readPdfFormFields, removeAllFormFields, resetPdfJsConfig, setPdfJsConfig, setSigniphiMetadata, shouldFlattenField, trackDocumentSent, trackDocumentSentSilent, trackDocumentSigned, trackDocumentSignedSilent, urlToPdfBytes, useFieldFiltering, useFormFields, useMultiSignerContext, usePdfViewer, useSignatureCapture, useSignatures, validateCurrentPdfState, validateFieldValues, validateFile, validateFileOrThrow, validateFiles, validateFilesOrThrow, validateFormField, validatePdfBytes, validatePdfFormFields, validatePdfUrl, validateSignatures, withPerformanceMonitoring };
11527
+ export { AttachmentValidationError, CDN_VIEWER, CDN_WORKER, CheckboxRenderer, DEFAULT_ATTACHMENT_CONSTRAINTS, DEFAULT_SIGNATURE_FONT, DateFieldRenderer, DropdownRenderer, FormFieldError, FormFieldRenderer, FormFieldType, InitialsFieldRenderer, PdfProcessingError, PdfValidationError, PdfViewerStyled as PdfViewer, PdfViewerCore, PdfViewerStyled, PerformanceMonitor, RadioGroupRenderer, SIGNATURE_FONTS, SignatureCanvas, SignatureCaptureCore, SignatureFieldRenderer, SignatureModal, SignatureTypeInput, SubmissionForm, SubmissionStatus, TextFieldRenderer, TextLabelRenderer, areAllSignersComplete, captureAuditTrail, captureDeviceMetadata, captureGeolocation, captureIpAddress, checkAndLogPdfJsVersion, checkPdfJsVersion, createPdfBlobUrl, createPerformanceMonitor, dataUrlToBytes, decodeFieldName, detectFieldType, downloadPdf, extractFieldValue, extractVisibleFormFields, fillFormFieldsWithSignatures, fillPdfWithSignatures, filterFieldsBySigner, findFieldByUuid, findPageIndexByRef, findPageIndexWithFallback, formatFieldName, formatFileSize, generateFallbackLabel, generateSignatureFromText, generateSignatureFromTextAsync, getErrorMessage, getFieldDisplayName, getFieldPageNumbers, getFieldUuidsForSigner, getNextSigner, getPdfJsConfig, getSigniphiMetadata, getWidgetRectangleAndPage, globalPerformanceMonitor, hasDrawableLabel, initPdfMetadata, initializePdfJs, isAttachmentValidationError, isAutoGeneratedLabel, isFieldVisibleToSigner, isFontLoaded, isFormFieldError, isImageType, isInitialsField, isPdfProcessingError, isPdfValidationError, isRequiredField, isSignatureField, isValidISODate, isValidPdf, loadSignatureFonts, logVersionCheckWarning, logger, measurePerformance, parseAndValidateDate, pdfToImages, preparePdfForSigner, readCurrentPdfFormValues, readPdfFormFields, removeAllFormFields, resetPdfJsConfig, resolveField, setPdfJsConfig, setSigniphiMetadata, shouldFlattenField, trackDocumentSent, trackDocumentSentSilent, trackDocumentSigned, trackDocumentSignedSilent, urlToPdfBytes, useFieldFiltering, useFormFields, useMultiSignerContext, usePdfViewer, useSignatureCapture, useSignatures, validateCurrentPdfState, validateFieldAssignments, validateFieldValues, validateFile, validateFileOrThrow, validateFiles, validateFilesOrThrow, validateFormField, validatePdfBytes, validatePdfFormFields, validatePdfUrl, validateSignatures, waitForFont, withPerformanceMonitoring };
9419
11528
  //# sourceMappingURL=index.mjs.map
9420
11529
  //# sourceMappingURL=index.mjs.map