@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
@@ -1,4 +1,4 @@
1
- import * as React8 from 'react';
1
+ import * as React9 from 'react';
2
2
  import { forwardRef, useRef, useState, useCallback, useEffect, useImperativeHandle, useMemo } from 'react';
3
3
  import { ErrorBoundary } from 'react-error-boundary';
4
4
  import * as pdfjsLib from 'pdfjs-dist';
@@ -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, FileText, Upload, ListChecks, ZoomOut, ZoomIn, Loader2, CheckCircle2, Clock, Image, File, Star } from 'lucide-react';
12
+ import { X, ChevronDown, ChevronUp, Check, Circle, AlertCircle, Lock, CheckCircle, Pen, Calendar as Calendar$1, ChevronLeft, ChevronRight, FileText, Upload, ListChecks, ZoomOut, ZoomIn, Loader2, CheckCircle2, Clock, Image, File } 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
  // src/components/PdfViewerStyled.tsx
22
23
  var _workerSrcComputed = false;
@@ -70,7 +71,7 @@ function initializePdfJs() {
70
71
  async function checkPdfJsVersion(viewerBasePath = "/pdfjs") {
71
72
  const workerVersion = pdfjsLib.version;
72
73
  try {
73
- const versionUrl = `${viewerBasePath}/.version`;
74
+ const versionUrl = `${viewerBasePath}/version.txt`;
74
75
  const response = await fetch(versionUrl);
75
76
  if (!response.ok) {
76
77
  return {
@@ -158,6 +159,7 @@ function validatePdfBytes(pdfBytes) {
158
159
  }
159
160
  function isAutoGeneratedLabel(label) {
160
161
  if (!label || !label.trim()) return true;
162
+ const trimmed = label.trim();
161
163
  const autoLabels = [
162
164
  "Signature",
163
165
  "Initials",
@@ -168,7 +170,65 @@ function isAutoGeneratedLabel(label) {
168
170
  "Option",
169
171
  "Radio"
170
172
  ];
171
- return autoLabels.includes(label.trim());
173
+ if (autoLabels.includes(trimmed)) return true;
174
+ if (/^\d{10,}$/.test(trimmed)) return true;
175
+ const typeTimestampPattern = new RegExp(
176
+ `^(${autoLabels.join("|")})[\\s_-]?\\d{10,}$`,
177
+ "i"
178
+ );
179
+ if (typeTimestampPattern.test(trimmed)) return true;
180
+ const typeNumberPattern = new RegExp(
181
+ `^(${autoLabels.join("|")})[\\s_-]?\\d{1,3}$`,
182
+ "i"
183
+ );
184
+ if (typeNumberPattern.test(trimmed)) return true;
185
+ const typeWordsJoined = autoLabels.join("|");
186
+ const corruptedPattern = new RegExp(
187
+ `^(${typeWordsJoined})[\\s_-]?\\d{10,}[\\s_-]?(${typeWordsJoined})+`,
188
+ "i"
189
+ );
190
+ if (corruptedPattern.test(trimmed)) return true;
191
+ const repeatedTypePattern = new RegExp(
192
+ `^((${typeWordsJoined})[\\s_-]+)+(${typeWordsJoined})$`,
193
+ "i"
194
+ );
195
+ if (repeatedTypePattern.test(trimmed)) return true;
196
+ return false;
197
+ }
198
+ function hasDrawableLabel(field) {
199
+ if (!field.label || !field.label.trim()) return false;
200
+ if (field.isLabelAutoGenerated) return false;
201
+ if (isAutoGeneratedLabel(field.label)) return false;
202
+ return true;
203
+ }
204
+ function getFieldDisplayName(field) {
205
+ if (field.label && !isAutoGeneratedLabel(field.label)) {
206
+ return field.label;
207
+ }
208
+ const typeNames = {
209
+ "text": "Text field",
210
+ "signature": "Signature",
211
+ "initials": "Initials",
212
+ "date": "Date field",
213
+ "checkbox": "Checkbox",
214
+ "dropdown": "Dropdown",
215
+ "radio": "Radio selection",
216
+ "radiogroup": "Radio selection",
217
+ "text_label": "Text label"
218
+ };
219
+ if (field.type) {
220
+ const typeName = typeNames[field.type.toLowerCase()];
221
+ if (typeName) {
222
+ return typeName;
223
+ }
224
+ }
225
+ if (field.name) {
226
+ let cleaned = field.name.replace(/_?(signature|initials|date)$/i, "").replace(/[_-]\d{10,}$/, "").replace(/[_-]/g, " ").trim();
227
+ if (cleaned && !isAutoGeneratedLabel(cleaned)) {
228
+ return cleaned.replace(/\b\w/g, (l) => l.toUpperCase());
229
+ }
230
+ }
231
+ return "This field";
172
232
  }
173
233
  function validateFieldValues(values) {
174
234
  const errors = [];
@@ -301,6 +361,92 @@ function getSigniphiMetadata(pdfDoc) {
301
361
  }
302
362
  }
303
363
 
364
+ // src/utils/font-loader.ts
365
+ var SIGNATURE_FONTS = [
366
+ { name: "dancing-script", family: "Dancing Script", label: "Elegant" },
367
+ { name: "great-vibes", family: "Great Vibes", label: "Formal" },
368
+ { name: "caveat", family: "Caveat", label: "Casual" },
369
+ { name: "homemade-apple", family: "Homemade Apple", label: "Natural" },
370
+ { name: "sacramento", family: "Sacramento", label: "Flowing" }
371
+ ];
372
+ var DEFAULT_SIGNATURE_FONT = {
373
+ name: "dancing-script",
374
+ family: "Dancing Script",
375
+ label: "Elegant"
376
+ };
377
+ var GOOGLE_FONTS_URL = "https://fonts.googleapis.com/css2?family=Dancing+Script&family=Great+Vibes&family=Caveat&family=Homemade+Apple&family=Sacramento&display=swap";
378
+ var fontsLoaded = false;
379
+ var fontsLoadingPromise = null;
380
+ async function loadSignatureFonts() {
381
+ if (fontsLoaded) {
382
+ return;
383
+ }
384
+ if (fontsLoadingPromise) {
385
+ return fontsLoadingPromise;
386
+ }
387
+ fontsLoadingPromise = new Promise((resolve, reject) => {
388
+ const existingLink = document.querySelector(
389
+ `link[href="${GOOGLE_FONTS_URL}"]`
390
+ );
391
+ if (existingLink) {
392
+ fontsLoaded = true;
393
+ resolve();
394
+ return;
395
+ }
396
+ const link = document.createElement("link");
397
+ link.href = GOOGLE_FONTS_URL;
398
+ link.rel = "stylesheet";
399
+ link.onload = () => {
400
+ fontsLoaded = true;
401
+ resolve();
402
+ };
403
+ link.onerror = () => {
404
+ fontsLoadingPromise = null;
405
+ reject(new Error("Failed to load signature fonts from Google Fonts"));
406
+ };
407
+ document.head.appendChild(link);
408
+ });
409
+ return fontsLoadingPromise;
410
+ }
411
+ function generateSignatureFromText(options) {
412
+ const {
413
+ text,
414
+ fontFamily,
415
+ width = 450,
416
+ height = 200,
417
+ fontSize = 48,
418
+ color = "#000000",
419
+ backgroundColor = null
420
+ } = options;
421
+ const canvas = document.createElement("canvas");
422
+ canvas.width = width;
423
+ canvas.height = height;
424
+ const ctx = canvas.getContext("2d");
425
+ if (!ctx) {
426
+ throw new Error("Failed to get canvas 2d context");
427
+ }
428
+ if (backgroundColor) {
429
+ ctx.fillStyle = backgroundColor;
430
+ ctx.fillRect(0, 0, width, height);
431
+ } else {
432
+ ctx.clearRect(0, 0, width, height);
433
+ }
434
+ ctx.font = `${fontSize}px "${fontFamily}"`;
435
+ ctx.fillStyle = color;
436
+ ctx.textAlign = "center";
437
+ ctx.textBaseline = "middle";
438
+ let adjustedFontSize = fontSize;
439
+ let textMetrics = ctx.measureText(text);
440
+ const maxWidth = width * 0.9;
441
+ while (textMetrics.width > maxWidth && adjustedFontSize > 16) {
442
+ adjustedFontSize -= 2;
443
+ ctx.font = `${adjustedFontSize}px "${fontFamily}"`;
444
+ textMetrics = ctx.measureText(text);
445
+ }
446
+ ctx.fillText(text, width / 2, height / 2);
447
+ return canvas.toDataURL("image/png");
448
+ }
449
+
304
450
  // src/utils/pdf-lib-loader.ts
305
451
  var pdfLibPromise = null;
306
452
  async function loadPdfLib() {
@@ -315,18 +461,15 @@ function isFieldVisibleToSigner(field, multiSignerContext) {
315
461
  if (!multiSignerContext.isMultiSigner) {
316
462
  return true;
317
463
  }
318
- const { currentSignerEmail, isPrimarySigner, isFinalSigner } = multiSignerContext;
464
+ const { currentSignerEmail } = multiSignerContext;
319
465
  if (!field.assignedSignerEmail) {
320
- return isFinalSigner;
466
+ return true;
321
467
  }
322
468
  if (field.assignedSignerEmail === currentSignerEmail) {
323
469
  return true;
324
470
  }
325
- if (field.assignedSignerEmail.includes("recipients")) {
326
- return isPrimarySigner;
327
- }
328
- if (field.assignedSignerEmail.includes("signers")) {
329
- return isFinalSigner;
471
+ if (field.assignedSignerEmail === "to-recipients" || field.assignedSignerEmail === "additional-signers") {
472
+ return true;
330
473
  }
331
474
  return false;
332
475
  }
@@ -340,18 +483,15 @@ function shouldFlattenField(field, multiSignerContext) {
340
483
  if (!multiSignerContext.isMultiSigner) {
341
484
  return true;
342
485
  }
343
- const { currentSignerEmail, isPrimarySigner, isFinalSigner } = multiSignerContext;
486
+ const { currentSignerEmail } = multiSignerContext;
344
487
  if (!field.assignedSignerEmail) {
345
- return isFinalSigner;
488
+ return true;
346
489
  }
347
490
  if (field.assignedSignerEmail === currentSignerEmail) {
348
491
  return true;
349
492
  }
350
- if (field.assignedSignerEmail.includes("recipients")) {
351
- return isPrimarySigner;
352
- }
353
- if (field.assignedSignerEmail.includes("signers")) {
354
- return isFinalSigner;
493
+ if (field.assignedSignerEmail === "to-recipients" || field.assignedSignerEmail === "additional-signers") {
494
+ return true;
355
495
  }
356
496
  return false;
357
497
  }
@@ -391,18 +531,23 @@ function findPageIndexWithFallback(pages, pageRef) {
391
531
 
392
532
  // src/utils/pdf-field-type-helpers.ts
393
533
  function detectFieldType(field) {
394
- const typeName = field.constructor.name;
395
- if (typeName === "PDFTextField") {
396
- return "text";
397
- } else if (typeName === "PDFCheckBox") {
534
+ const f = field;
535
+ if (typeof f.check === "function" && typeof f.uncheck === "function") {
398
536
  return "checkbox";
399
- } else if (typeName === "PDFDropdown") {
537
+ }
538
+ if (typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function") {
539
+ return "radiogroup";
540
+ }
541
+ if (typeof f.select === "function" && typeof f.getOptions === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function")) {
400
542
  return "dropdown";
401
- } else if (typeName === "PDFOptionList") {
543
+ }
544
+ if (typeof f.getOptions === "function" && typeof f.setOptions === "function" && typeof f.select !== "function") {
402
545
  return "optionlist";
403
- } else if (typeName === "PDFRadioGroup") {
404
- return "radiogroup";
405
- } else if (typeName === "PDFSignature") {
546
+ }
547
+ if (typeof f.getText === "function" && typeof f.setText === "function") {
548
+ return "text";
549
+ }
550
+ if (typeof f.getText !== "function" && typeof f.check !== "function" && typeof f.select !== "function") {
406
551
  return "signature";
407
552
  }
408
553
  return "unknown";
@@ -411,13 +556,20 @@ function extractFieldValue(field, fieldType) {
411
556
  try {
412
557
  switch (fieldType) {
413
558
  case "text":
559
+ case "date":
414
560
  return field.getText?.() || "";
415
561
  case "checkbox":
416
562
  return field.isChecked?.() ? "true" : "false";
417
563
  case "dropdown":
418
- case "optionlist":
564
+ case "optionlist": {
565
+ const selected = field.getSelected?.();
566
+ return Array.isArray(selected) ? selected[0] || "" : String(selected || "");
567
+ }
419
568
  case "radiogroup":
420
- return field.getSelected?.()?.[0] || "";
569
+ case "radio": {
570
+ const radioSelected = field.getSelected?.();
571
+ return radioSelected ? String(radioSelected) : "";
572
+ }
421
573
  default:
422
574
  return "";
423
575
  }
@@ -489,12 +641,16 @@ async function validatePdfFormFields(pdfBytes, fieldValues, signatures, extracte
489
641
  const errors = [];
490
642
  for (const field of pdfFormFields) {
491
643
  if (field.required) {
644
+ const extractedField = extractedFields?.find((f) => f.name === field.name);
492
645
  if (multiSignerContext?.isMultiSigner && extractedFields) {
493
- const extractedField = extractedFields.find((f) => f.name === field.name);
494
646
  if (!extractedField) {
495
647
  continue;
496
648
  }
497
649
  }
650
+ const logicalType = extractedField?.type || field.type;
651
+ if (logicalType === "date" || logicalType === "signature" || logicalType === "initials") {
652
+ continue;
653
+ }
498
654
  let hasValue = false;
499
655
  const fieldValue = fieldValues[field.name];
500
656
  const signatureValue = signatures[field.name];
@@ -504,16 +660,17 @@ async function validatePdfFormFields(pdfBytes, fieldValues, signatures, extracte
504
660
  hasValue = !!(signatureValue || fieldValue || mainSignature);
505
661
  } else if (field.name.includes("initials")) {
506
662
  hasValue = !!(signatureValue || fieldValue || mainInitials);
663
+ } else if (field.type === "checkbox" || logicalType === "checkbox") {
664
+ hasValue = fieldValue === "true" || field.value === "true";
507
665
  } else {
508
666
  hasValue = !!(fieldValue || field.value);
509
667
  }
510
668
  if (!hasValue) {
511
- let friendlyName = field.name.replace(/[_-]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
512
- if (field.name.includes("signature")) {
513
- friendlyName = "Signature";
514
- } else if (field.name.includes("initials")) {
515
- friendlyName = "Initials";
516
- }
669
+ const friendlyName = getFieldDisplayName({
670
+ label: extractedField?.label,
671
+ name: field.name,
672
+ type: logicalType
673
+ });
517
674
  errors.push(`${friendlyName} is required`);
518
675
  }
519
676
  }
@@ -524,7 +681,80 @@ async function validatePdfFormFields(pdfBytes, fieldValues, signatures, extracte
524
681
  return ["Unable to validate form fields. Please ensure all required fields are completed."];
525
682
  }
526
683
  }
527
- async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {}, currentSignerEmail, extractedFormFields, metadata, auditTrail, multiSignerContext) {
684
+ function extractFieldFontSize(fieldName, field, extractedFormFields) {
685
+ const DEFAULT_FONT_SIZE = 10;
686
+ const MIN_FONT_SIZE = 8;
687
+ const MAX_FONT_SIZE = 72;
688
+ const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
689
+ if (fieldInfo?.fontSize && fieldInfo.fontSize >= MIN_FONT_SIZE && fieldInfo.fontSize <= MAX_FONT_SIZE) {
690
+ return fieldInfo.fontSize;
691
+ }
692
+ const daFontSize = extractFromDAField(field);
693
+ if (daFontSize !== null) {
694
+ return daFontSize;
695
+ }
696
+ const tuFontSize = extractFromTUField(field);
697
+ if (tuFontSize !== null) {
698
+ return tuFontSize;
699
+ }
700
+ return DEFAULT_FONT_SIZE;
701
+ }
702
+ function extractFromDAField(field) {
703
+ try {
704
+ const fieldWithAcro = field;
705
+ const dict = fieldWithAcro.acroField?.dict;
706
+ if (!dict) return null;
707
+ const daKey = dict.context?.obj?.("DA") || "DA";
708
+ const daEntry = dict.lookup?.(daKey) || dict.get?.(daKey);
709
+ if (!daEntry) return null;
710
+ let daValue = "";
711
+ if (typeof daEntry.decodeText === "function") {
712
+ daValue = daEntry.decodeText();
713
+ } else if (typeof daEntry.asString === "function") {
714
+ daValue = daEntry.asString();
715
+ } else {
716
+ daValue = String(daEntry);
717
+ }
718
+ const daMatch = daValue.match(/\s+(\d+)\s+Tf/i);
719
+ if (daMatch?.[1]) {
720
+ const fontSize = parseInt(daMatch[1], 10);
721
+ if (fontSize >= 8 && fontSize <= 72) {
722
+ return fontSize;
723
+ }
724
+ }
725
+ } catch {
726
+ }
727
+ return null;
728
+ }
729
+ function extractFromTUField(field) {
730
+ try {
731
+ const fieldWithAcro = field;
732
+ const dict = fieldWithAcro.acroField?.dict;
733
+ if (!dict) return null;
734
+ const tuKey = dict.context?.obj?.("TU") || "TU";
735
+ const tuEntry = dict.lookup?.(tuKey) || dict.get?.(tuKey);
736
+ if (!tuEntry) return null;
737
+ let tuValue = "";
738
+ if (typeof tuEntry.decodeText === "function") {
739
+ tuValue = tuEntry.decodeText();
740
+ } else if (typeof tuEntry.asString === "function") {
741
+ tuValue = tuEntry.asString();
742
+ } else {
743
+ tuValue = String(tuEntry);
744
+ }
745
+ tuValue = tuValue.replace(/^\(|\)$|^<|>$/g, "");
746
+ const match = tuValue.match(/\|fontSize:(\d+)$/);
747
+ if (match?.[1]) {
748
+ const fontSize = parseInt(match[1], 10);
749
+ if (fontSize >= 8 && fontSize <= 72) {
750
+ return fontSize;
751
+ }
752
+ }
753
+ } catch {
754
+ }
755
+ return null;
756
+ }
757
+ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {}, _currentSignerEmail, extractedFormFields, metadata, auditTrail, multiSignerContext) {
528
758
  try {
529
759
  const { PDFDocument, rgb, StandardFonts } = await loadPdfLib();
530
760
  const pdfDoc = await PDFDocument.load(pdfBytes);
@@ -541,39 +771,39 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
541
771
  continue;
542
772
  }
543
773
  try {
544
- const fieldTypeName = field.constructor.name;
545
- if (fieldTypeName === "PDFTextField" || fieldTypeName === "PDFTextField2") {
546
- const textField = field;
547
- textField.setText?.(fieldValue);
548
- } else if (fieldTypeName === "PDFCheckBox" || fieldTypeName === "PDFCheckBox2") {
549
- const checkBox = field;
774
+ const f = field;
775
+ const isTextField = typeof f.getText === "function" && typeof f.setText === "function";
776
+ const isCheckbox = typeof f.check === "function" && typeof f.uncheck === "function";
777
+ const isRadio = typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function";
778
+ const isDropdown = typeof f.select === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function");
779
+ if (isTextField) {
780
+ f.setText?.(fieldValue);
781
+ } else if (isCheckbox) {
550
782
  if (fieldValue === "true" || fieldValue === "Yes" || fieldValue === "checked" || fieldValue === "On") {
551
- checkBox.check?.();
783
+ f.check?.();
552
784
  } else {
553
- checkBox.uncheck?.();
785
+ f.uncheck?.();
554
786
  }
555
- } else if (fieldTypeName === "PDFRadioGroup" || fieldTypeName === "PDFRadioGroup2") {
556
- const radioGroup = field;
787
+ } else if (isRadio) {
557
788
  if (fieldValue === "true" || fieldValue === "false") {
558
789
  continue;
559
790
  }
560
791
  const idxMatch = fieldValue.match(/__RADIO_OPTION_INDEX_(\d+)__/);
561
792
  if (idxMatch && idxMatch[1]) {
562
793
  const selectedIndex = parseInt(idxMatch[1], 10);
563
- const options = radioGroup.getOptions?.() || [];
794
+ const options = f.getOptions?.() || [];
564
795
  if (selectedIndex >= 0 && selectedIndex < options.length) {
565
- radioGroup.select?.(options[selectedIndex]);
796
+ f.select?.(options[selectedIndex]);
566
797
  }
567
798
  } else {
568
- const options = radioGroup.getOptions?.() || [];
799
+ const options = f.getOptions?.() || [];
569
800
  if (options.includes(fieldValue)) {
570
- radioGroup.select?.(fieldValue);
801
+ f.select?.(fieldValue);
571
802
  } else {
572
803
  }
573
804
  }
574
- } else if (fieldTypeName === "PDFDropdown" || fieldTypeName === "PDFDropdown2") {
575
- const dropdown = field;
576
- dropdown.select?.(fieldValue);
805
+ } else if (isDropdown) {
806
+ f.select?.(fieldValue);
577
807
  }
578
808
  } catch (fieldError) {
579
809
  logger.error(`Error setting field "${fieldName}":`, fieldError);
@@ -581,11 +811,6 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
581
811
  }
582
812
  try {
583
813
  form.updateFieldAppearances();
584
- const PDFName3 = pdfLib.PDFName;
585
- const acroForm = pdfDoc.catalog.lookup(PDFName3.of("AcroForm"));
586
- if (acroForm && typeof acroForm === "object" && "set" in acroForm) {
587
- acroForm.set(PDFName3.of("NeedAppearances"), false);
588
- }
589
814
  } catch (appearanceError) {
590
815
  logger.warn("Could not update field appearances:", appearanceError);
591
816
  }
@@ -654,6 +879,20 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
654
879
  const y = fieldPosition.y;
655
880
  const width = Math.max(fieldPosition.width, 80);
656
881
  const height = Math.max(fieldPosition.height, 30);
882
+ const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
883
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
884
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
885
+ const labelFontSize = Math.min(10, height * 0.4);
886
+ const labelX = x;
887
+ const labelY = y + height + 5;
888
+ page.drawText(fieldInfo.label, {
889
+ x: labelX,
890
+ y: labelY,
891
+ size: labelFontSize,
892
+ font: labelFont,
893
+ color: rgb(0, 0, 0)
894
+ });
895
+ }
657
896
  const signatureDims = signatureImage.scaleToFit(width - 4, height - 4);
658
897
  const finalX = x;
659
898
  const finalY = y;
@@ -688,7 +927,7 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
688
927
  console.log("[FLATTEN] After pattern matching in fieldPageMap:", foundInitialsFields);
689
928
  if (extractedFormFields && extractedFormFields.length > 0) {
690
929
  foundInitialsFields = foundInitialsFields.filter((fieldName) => {
691
- const fieldInfo = extractedFormFields.find((f) => f.name === fieldName);
930
+ const fieldInfo = extractedFormFields.find((f) => f.name === fieldName) || extractedFormFields.find((f) => f.name === fieldName.replace(/_initials$/i, "")) || extractedFormFields.find((f) => fieldName.startsWith(f.name));
692
931
  if (!fieldInfo) return false;
693
932
  const isActualInitialsField = fieldInfo.type === "initials" || fieldName.toLowerCase().includes("initials");
694
933
  if (!isActualInitialsField) return false;
@@ -717,6 +956,19 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
717
956
  const y = fieldPosition.y;
718
957
  const height = Math.max(fieldPosition.height, 20);
719
958
  const fontSize = Math.min(height * 0.7, 14);
959
+ 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");
960
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
961
+ const labelFontSize = Math.min(10, height * 0.4);
962
+ const labelX = x;
963
+ const labelY = y + height + 5;
964
+ page.drawText(fieldInfo.label, {
965
+ x: labelX,
966
+ y: labelY,
967
+ size: labelFontSize,
968
+ font,
969
+ color: rgb(0, 0, 0)
970
+ });
971
+ }
720
972
  const finalX = x + 2;
721
973
  const finalY = y + (height - fontSize) / 2;
722
974
  console.log("[FLATTEN] \u2705 Drawing initials at:", { x: finalX, y: finalY, fontSize, text: mainInitialsData });
@@ -759,11 +1011,16 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
759
1011
  let flattenedCount = 0;
760
1012
  for (const field of fieldsToFlatten) {
761
1013
  const fieldName = field.getName();
762
- const fieldType = field.constructor.name;
1014
+ const f = field;
1015
+ const isCheckboxField = typeof f.check === "function" && typeof f.uncheck === "function";
1016
+ const isRadioField = typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function";
1017
+ const isDropdownField = typeof f.select === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function");
1018
+ const isTextField = typeof f.getText === "function" && typeof f.setText === "function";
1019
+ const isSignatureType = !isCheckboxField && !isRadioField && !isDropdownField && !isTextField;
763
1020
  try {
764
1021
  const userEnteredValue = formFieldValues[fieldName];
765
- const isSignatureField = fieldName.toLowerCase().includes("signature") || fieldType.includes("Signature");
766
- const isInitialsField = fieldName.toLowerCase().includes("initials") || fieldType.includes("Initials");
1022
+ const isSignatureField = fieldName.toLowerCase().includes("signature") || isSignatureType;
1023
+ const isInitialsField = fieldName.toLowerCase().includes("initials");
767
1024
  if (isSignatureField || isInitialsField) {
768
1025
  form.removeField(field);
769
1026
  flattenedCount++;
@@ -771,15 +1028,29 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
771
1028
  }
772
1029
  const fieldWithWidgets = field;
773
1030
  const widgets = fieldWithWidgets.acroField?.getWidgets?.() || [];
774
- if (fieldType === "PDFCheckBox" || fieldType === "PDFCheckBox2") {
1031
+ if (isCheckboxField) {
775
1032
  const stringValue = String(userEnteredValue || "");
776
1033
  const isChecked = stringValue === "true" || stringValue === "Yes" || stringValue === "On" || stringValue === "checked";
1034
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
1035
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
777
1036
  if (isChecked) {
778
1037
  for (const widget of widgets) {
779
1038
  const result = getWidgetRectangleAndPage(widget, pages);
780
1039
  if (!result) continue;
781
1040
  const { rect, page } = result;
782
- const checkboxSize = Math.min(rect.width, rect.height) * 0.6;
1041
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1042
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1043
+ const labelX = rect.x + rect.width + 5;
1044
+ const labelY = rect.y + (rect.height - labelFontSize) / 2;
1045
+ page.drawText(fieldInfo.label, {
1046
+ x: labelX,
1047
+ y: labelY,
1048
+ size: labelFontSize,
1049
+ font: labelFont,
1050
+ color: rgb(0, 0, 0)
1051
+ });
1052
+ }
1053
+ const checkboxSize = Math.min(rect.width, rect.height);
783
1054
  const checkboxX = rect.x + (rect.width - checkboxSize) / 2;
784
1055
  const checkboxY = rect.y + (rect.height - checkboxSize) / 2;
785
1056
  page.drawRectangle({
@@ -790,21 +1061,51 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
790
1061
  borderColor: rgb(0, 0, 0),
791
1062
  borderWidth: 1
792
1063
  });
793
- const checkSize = checkboxSize * 0.6;
794
- const textWidth = checkSize * 0.6;
795
- const textHeight = checkSize * 0.8;
1064
+ const checkFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
1065
+ const checkFontSize = checkboxSize * 0.7;
1066
+ const textWidth = checkFont.widthOfTextAtSize("X", checkFontSize);
1067
+ const textHeight = checkFont.heightAtSize(checkFontSize);
796
1068
  page.drawText("X", {
797
1069
  x: checkboxX + (checkboxSize - textWidth) / 2,
798
- y: checkboxY + (checkboxSize - textHeight) / 2 + textHeight * 0.2,
799
- size: checkSize,
800
- font: await pdfDoc.embedFont(StandardFonts.HelveticaBold),
1070
+ y: checkboxY + (checkboxSize - textHeight) / 2 + textHeight * 0.15,
1071
+ size: checkFontSize,
1072
+ font: checkFont,
801
1073
  color: rgb(0, 0, 0)
802
1074
  });
803
1075
  }
1076
+ } else {
1077
+ for (const widget of widgets) {
1078
+ const result = getWidgetRectangleAndPage(widget, pages);
1079
+ if (!result) continue;
1080
+ const { rect, page } = result;
1081
+ const checkboxSize = Math.min(rect.width, rect.height);
1082
+ const checkboxX = rect.x + (rect.width - checkboxSize) / 2;
1083
+ const checkboxY = rect.y + (rect.height - checkboxSize) / 2;
1084
+ page.drawRectangle({
1085
+ x: checkboxX,
1086
+ y: checkboxY,
1087
+ width: checkboxSize,
1088
+ height: checkboxSize,
1089
+ borderColor: rgb(0, 0, 0),
1090
+ borderWidth: 1
1091
+ });
1092
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1093
+ const labelFontSize = Math.min(12, rect.height * 0.6);
1094
+ const labelX = rect.x + rect.width + 5;
1095
+ const labelY = rect.y + (rect.height - labelFontSize) / 2;
1096
+ page.drawText(fieldInfo.label, {
1097
+ x: labelX,
1098
+ y: labelY,
1099
+ size: labelFontSize,
1100
+ font: labelFont,
1101
+ color: rgb(0, 0, 0)
1102
+ });
1103
+ }
1104
+ }
804
1105
  }
805
- } else if (fieldType === "PDFRadioGroup" || fieldType === "PDFRadioGroup2") {
806
- const radioGroup = field;
807
- const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
1106
+ } else if (isRadioField) {
1107
+ const radioGroup = f;
1108
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
808
1109
  let actualSelectedValue = "";
809
1110
  try {
810
1111
  actualSelectedValue = radioGroup.getSelected?.() || "";
@@ -849,14 +1150,15 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
849
1150
  const result = getWidgetRectangleAndPage(widget, pages);
850
1151
  if (!result) continue;
851
1152
  const { rect, page, pageIndex } = result;
852
- if (i === 0 && fieldInfo?.label && fieldInfo.label.trim() && !isAutoGeneratedLabel(fieldInfo.label)) {
1153
+ if (i === 0 && fieldInfo && hasDrawableLabel(fieldInfo)) {
853
1154
  const groupLabelKey = `${pageIndex}-${fieldName}-grouplabel`;
854
1155
  if (!drawnGroupLabels.has(groupLabelKey)) {
1156
+ const labelFontSize = Math.min(10, rect.height * 0.4);
855
1157
  const labelY = rect.y + rect.height + 3;
856
1158
  page.drawText(fieldInfo.label, {
857
1159
  x: rect.x,
858
1160
  y: labelY,
859
- size: 8,
1161
+ size: labelFontSize,
860
1162
  font: labelFont,
861
1163
  color: rgb(0, 0, 0)
862
1164
  });
@@ -864,22 +1166,21 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
864
1166
  }
865
1167
  } else if (i === 0 && isAutoGeneratedLabel(fieldInfo?.label || "")) {
866
1168
  }
867
- const radioSize = Math.min(rect.width, rect.height) * 0.5;
868
- const radioX = rect.x + (rect.width - radioSize) / 2;
869
- const radioY = rect.y + (rect.height - radioSize) / 2;
1169
+ const radioSize = Math.min(rect.width, rect.height);
1170
+ const centerX = rect.x + rect.width / 2;
1171
+ const centerY = rect.y + rect.height / 2;
870
1172
  page.drawCircle({
871
- x: radioX + radioSize / 2,
872
- y: radioY + radioSize / 2,
873
- size: radioSize,
1173
+ x: centerX,
1174
+ y: centerY,
1175
+ size: radioSize / 2,
874
1176
  borderColor: rgb(0, 0, 0),
875
1177
  borderWidth: 1
876
1178
  });
877
1179
  if (i === selectedIndex) {
878
- const circleSize = radioSize * 0.5;
879
1180
  page.drawCircle({
880
- x: radioX + radioSize / 2,
881
- y: radioY + radioSize / 2,
882
- size: circleSize,
1181
+ x: centerX,
1182
+ y: centerY,
1183
+ size: radioSize / 4,
883
1184
  color: rgb(0, 0, 0)
884
1185
  });
885
1186
  }
@@ -889,8 +1190,8 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
889
1190
  if (optionText && !drawnOptionLabels.has(optionKey)) {
890
1191
  page.drawText(optionText, {
891
1192
  x: rect.x + rect.width + 4,
892
- y: rect.y + (rect.height - 8) / 2,
893
- size: 8,
1193
+ y: rect.y + (rect.height - 7) / 2,
1194
+ size: 7,
894
1195
  font: labelFont,
895
1196
  color: rgb(0, 0, 0)
896
1197
  });
@@ -900,17 +1201,31 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
900
1201
  }
901
1202
  if (fieldInfo?.options) {
902
1203
  }
903
- } else if (fieldType === "PDFDropdown" || fieldType === "PDFDropdown2") {
1204
+ } else if (isDropdownField) {
904
1205
  const selectedValue = userEnteredValue ? String(userEnteredValue) : "";
905
- if (selectedValue && selectedValue.trim()) {
906
- for (const widget of widgets) {
907
- const result = getWidgetRectangleAndPage(widget, pages);
908
- if (!result) continue;
909
- const { rect, page } = result;
1206
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
1207
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
1208
+ for (const widget of widgets) {
1209
+ const result = getWidgetRectangleAndPage(widget, pages);
1210
+ if (!result) continue;
1211
+ const { rect, page } = result;
1212
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1213
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1214
+ const labelX = rect.x;
1215
+ const labelY = rect.y + rect.height + 5;
1216
+ page.drawText(fieldInfo.label, {
1217
+ x: labelX,
1218
+ y: labelY,
1219
+ size: labelFontSize,
1220
+ font: labelFont,
1221
+ color: rgb(0, 0, 0)
1222
+ });
1223
+ }
1224
+ if (selectedValue && selectedValue.trim()) {
910
1225
  page.drawText(selectedValue, {
911
1226
  x: rect.x + 2,
912
1227
  y: rect.y + 2,
913
- size: 10,
1228
+ size: 14,
914
1229
  font: await pdfDoc.embedFont(StandardFonts.Helvetica),
915
1230
  color: rgb(0, 0, 0)
916
1231
  });
@@ -918,15 +1233,31 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
918
1233
  }
919
1234
  } else {
920
1235
  const fieldValue = userEnteredValue ? String(userEnteredValue) : "";
921
- if (fieldValue && fieldValue.trim()) {
922
- for (const widget of widgets) {
923
- const result = getWidgetRectangleAndPage(widget, pages);
924
- if (!result) continue;
925
- const { rect, page } = result;
1236
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
1237
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
1238
+ const isDateField = fieldName.toLowerCase().includes("date") || fieldName.toLowerCase().includes("_date");
1239
+ const fontSize = isDateField ? 14 : extractFieldFontSize(fieldName, field, extractedFormFields);
1240
+ for (const widget of widgets) {
1241
+ const result = getWidgetRectangleAndPage(widget, pages);
1242
+ if (!result) continue;
1243
+ const { rect, page } = result;
1244
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1245
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1246
+ const labelX = rect.x;
1247
+ const labelY = rect.y + rect.height + 5;
1248
+ page.drawText(fieldInfo.label, {
1249
+ x: labelX,
1250
+ y: labelY,
1251
+ size: labelFontSize,
1252
+ font: labelFont,
1253
+ color: rgb(0, 0, 0)
1254
+ });
1255
+ }
1256
+ if (fieldValue && fieldValue.trim()) {
926
1257
  page.drawText(fieldValue, {
927
1258
  x: rect.x + 2,
928
1259
  y: rect.y + 2,
929
- size: 10,
1260
+ size: fontSize,
930
1261
  font: await pdfDoc.embedFont(StandardFonts.Helvetica),
931
1262
  color: rgb(0, 0, 0)
932
1263
  });
@@ -1084,7 +1415,6 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1084
1415
  const fields = form.getFields();
1085
1416
  const visibleFields = [];
1086
1417
  let hasAnyInitialsFields = false;
1087
- let hasInitialsFieldsForCurrentSigner = false;
1088
1418
  for (const field of fields) {
1089
1419
  const fieldName = field.getName();
1090
1420
  const fieldWithExtensions = field;
@@ -1110,28 +1440,31 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1110
1440
  };
1111
1441
  })();
1112
1442
  let fieldType = "text" /* TEXT */;
1113
- const fieldTypeName = field.constructor.name;
1443
+ const f = field;
1444
+ const isCheckboxField = typeof f.check === "function" && typeof f.uncheck === "function";
1445
+ const isRadioField = typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function";
1446
+ const isDropdownField = typeof f.select === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function");
1447
+ const isTextField = typeof f.getText === "function" && typeof f.setText === "function";
1448
+ const isSignatureType = !isCheckboxField && !isRadioField && !isDropdownField && !isTextField;
1114
1449
  const cleanFieldName = fieldName.replace(/_signature$|_initials$|_date$/i, "");
1115
- const isActualSignatureField = fieldTypeName.includes("Signature") || cleanFieldName.toLowerCase().includes("signature") && !fieldTypeName.includes("CheckBox") && !fieldTypeName.includes("Radio");
1116
- const isActualInitialsField = cleanFieldName.toLowerCase().includes("initials") && !fieldTypeName.includes("CheckBox") && !fieldTypeName.includes("Radio");
1450
+ const isActualSignatureField = isSignatureType || cleanFieldName.toLowerCase().includes("signature") && !isCheckboxField && !isRadioField;
1451
+ const isActualInitialsField = cleanFieldName.toLowerCase().includes("initials") && !isCheckboxField && !isRadioField;
1117
1452
  if (isActualSignatureField) {
1118
1453
  fieldType = "signature" /* SIGNATURE */;
1119
1454
  } else if (isActualInitialsField) {
1120
1455
  fieldType = "initials" /* INITIALS */;
1121
1456
  hasAnyInitialsFields = true;
1122
- if (currentSignerEmail && fieldMetadata.signer === currentSignerEmail) {
1123
- hasInitialsFieldsForCurrentSigner = true;
1124
- }
1125
1457
  } else if (cleanFieldName.toLowerCase().includes("date")) {
1126
1458
  fieldType = "date" /* DATE */;
1127
- } else if (fieldTypeName.includes("CheckBox")) {
1459
+ } else if (isCheckboxField) {
1128
1460
  fieldType = "checkbox" /* CHECKBOX */;
1129
- } else if (fieldTypeName.toLowerCase().includes("dropdown")) {
1461
+ } else if (isDropdownField) {
1130
1462
  fieldType = "dropdown" /* DROPDOWN */;
1131
- } else if (fieldTypeName.includes("Radio")) {
1463
+ } else if (isRadioField) {
1132
1464
  fieldType = "radio" /* RADIO */;
1133
1465
  }
1134
1466
  let displayLabel = fieldMetadata.label || "";
1467
+ let isLabelAutoGenerated = false;
1135
1468
  if (!displayLabel || !displayLabel.trim()) {
1136
1469
  try {
1137
1470
  const tuLabel = extractTULabel(fieldWithExtensions);
@@ -1143,6 +1476,7 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1143
1476
  }
1144
1477
  if (!displayLabel || !displayLabel.trim()) {
1145
1478
  displayLabel = generateFallbackLabel(cleanFieldName, fieldType);
1479
+ isLabelAutoGenerated = true;
1146
1480
  }
1147
1481
  const esignField = {
1148
1482
  id: fieldName,
@@ -1154,6 +1488,8 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1154
1488
  type: fieldType,
1155
1489
  label: displayLabel,
1156
1490
  // Use friendly label for display
1491
+ isLabelAutoGenerated,
1492
+ // True when label was generated from field name (should not be drawn on PDF)
1157
1493
  position: { x: 0, y: 0, width: 100, height: 30, page: 1 },
1158
1494
  // Default position
1159
1495
  required: fieldWithExtensions.isRequired?.() ?? false,
@@ -1174,6 +1510,13 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1174
1510
  logger.warn("Error extracting options for field:", error);
1175
1511
  }
1176
1512
  }
1513
+ try {
1514
+ const currentValue = extractFieldValue(f, esignField.type);
1515
+ if (currentValue) {
1516
+ esignField.defaultValue = currentValue;
1517
+ }
1518
+ } catch {
1519
+ }
1177
1520
  visibleFields.push(esignField);
1178
1521
  }
1179
1522
  }
@@ -1190,11 +1533,8 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1190
1533
  placeholder: "Draw or upload your signature",
1191
1534
  assignedSignerEmail: currentSignerEmail
1192
1535
  });
1193
- const shouldCreateInitialsField = hasInitialsFieldsForCurrentSigner || hasAnyInitialsFields && !currentSignerEmail;
1536
+ const shouldCreateInitialsField = hasAnyInitialsFields;
1194
1537
  if (shouldCreateInitialsField) {
1195
- if (hasInitialsFieldsForCurrentSigner) {
1196
- } else {
1197
- }
1198
1538
  mainFields.push({
1199
1539
  id: "initials_field_main",
1200
1540
  fieldId: "initials_field_main",
@@ -1281,8 +1621,12 @@ function decodeFieldName(fieldName) {
1281
1621
  }
1282
1622
  function generateFallbackLabel(decodedFieldName, fieldType) {
1283
1623
  let displayLabel = decodedFieldName;
1284
- displayLabel = displayLabel.replace(/_signature$/i, "").replace(/_initials$/i, "").replace(/_date$/i, "");
1285
- if (/^(text|signature|initials|date)_\d+$/i.test(displayLabel)) {
1624
+ let prevLabel = "";
1625
+ while (displayLabel !== prevLabel) {
1626
+ prevLabel = displayLabel;
1627
+ displayLabel = displayLabel.replace(/_signature$/i, "").replace(/_initials$/i, "").replace(/_date$/i, "");
1628
+ }
1629
+ if (/^(text|signature|initials|date|checkbox|radio|dropdown)_\d+/i.test(displayLabel)) {
1286
1630
  if (fieldType === "signature" /* SIGNATURE */) return "Signature";
1287
1631
  if (fieldType === "initials" /* INITIALS */) return "Initials";
1288
1632
  if (fieldType === "date" /* DATE */) return "Date";
@@ -1290,9 +1634,9 @@ function generateFallbackLabel(decodedFieldName, fieldType) {
1290
1634
  if (fieldType === "checkbox" /* CHECKBOX */) return "Checkbox";
1291
1635
  if (fieldType === "dropdown" /* DROPDOWN */) return "Dropdown";
1292
1636
  if (fieldType === "radio" /* RADIO */) return "Option";
1293
- } else {
1294
- displayLabel = displayLabel.replace(/_/g, " ").replace(/\s+\d+$/g, "").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1637
+ if (fieldType === "text_label" /* TEXT_LABEL */) return "Text Label";
1295
1638
  }
1639
+ displayLabel = displayLabel.replace(/_/g, " ").replace(/\s+\d+$/g, "").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1296
1640
  return displayLabel;
1297
1641
  }
1298
1642
  function extractTULabel(field) {
@@ -1319,6 +1663,25 @@ function extractTULabel(field) {
1319
1663
  return "";
1320
1664
  }
1321
1665
  }
1666
+ function findFieldByUuid(fields, uuid) {
1667
+ return fields.find((f) => f.fieldId === uuid);
1668
+ }
1669
+ function resolveField(fields, identifier) {
1670
+ let field = findFieldByUuid(fields, identifier);
1671
+ if (field) {
1672
+ return field;
1673
+ }
1674
+ field = fields.find((f) => f.id === identifier);
1675
+ if (field) {
1676
+ return field;
1677
+ }
1678
+ field = fields.find((f) => f.name === identifier);
1679
+ if (field) {
1680
+ return field;
1681
+ }
1682
+ const cleanIdentifier = identifier.replace(/_signature$|_initials$|_date$/i, "");
1683
+ return fields.find((f) => f.fieldId === cleanIdentifier || f.id === cleanIdentifier || f.name === cleanIdentifier);
1684
+ }
1322
1685
 
1323
1686
  // src/utils/audit-trail.ts
1324
1687
  function captureDeviceMetadata() {
@@ -1476,13 +1839,13 @@ function getErrorMessage(error, defaultMessage = "Unknown error") {
1476
1839
 
1477
1840
  // src/utils/attachment-validators.ts
1478
1841
  var DEFAULT_ATTACHMENT_CONSTRAINTS = {
1479
- maxFileSize: 10 * 1024 * 1024,
1480
- // 10MB
1842
+ maxFileSize: 25 * 1024 * 1024,
1843
+ // 25MB
1481
1844
  maxTotalSize: 50 * 1024 * 1024,
1482
1845
  // 50MB
1483
1846
  maxFiles: 10,
1484
1847
  allowedTypes: ["image/*", "application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
1485
- allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png", ".gif", ".doc", ".docx"]
1848
+ allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".doc", ".docx"]
1486
1849
  };
1487
1850
  function validateFile(file, constraints = DEFAULT_ATTACHMENT_CONSTRAINTS) {
1488
1851
  const errors = [];
@@ -1529,6 +1892,23 @@ function formatFileSize(bytes) {
1529
1892
  const i = Math.floor(Math.log(bytes) / Math.log(k));
1530
1893
  return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
1531
1894
  }
1895
+ function isValidISODate(value) {
1896
+ if (!value || typeof value !== "string") {
1897
+ return false;
1898
+ }
1899
+ const isoPattern = /^\d{4}-\d{2}-\d{2}$/;
1900
+ if (!isoPattern.test(value)) {
1901
+ return false;
1902
+ }
1903
+ try {
1904
+ const date = parseISO(value);
1905
+ return isValid(date) && !isNaN(date.getTime());
1906
+ } catch {
1907
+ return false;
1908
+ }
1909
+ }
1910
+
1911
+ // src/utils/date-validation.ts
1532
1912
  function toIsoDateString(date) {
1533
1913
  const year = date.getFullYear();
1534
1914
  const month = String(date.getMonth() + 1).padStart(2, "0");
@@ -1652,21 +2032,6 @@ function parseAndValidateDate(value) {
1652
2032
  error: `Invalid date format: "${trimmedValue}". Please use a date picker or format like MM/DD/YYYY.`
1653
2033
  };
1654
2034
  }
1655
- function isValidISODate(value) {
1656
- if (!value || typeof value !== "string") {
1657
- return false;
1658
- }
1659
- const isoPattern = /^\d{4}-\d{2}-\d{2}$/;
1660
- if (!isoPattern.test(value)) {
1661
- return false;
1662
- }
1663
- try {
1664
- const date = parseISO(value);
1665
- return isValid(date) && !isNaN(date.getTime());
1666
- } catch {
1667
- return false;
1668
- }
1669
- }
1670
2035
 
1671
2036
  // src/utils/tracking.ts
1672
2037
  var DEFAULT_API_ENDPOINT = "https://api-dev.signiphi.ai";
@@ -1745,6 +2110,79 @@ var PdfViewerCore = forwardRef(
1745
2110
  }
1746
2111
  return PDFViewerApplication;
1747
2112
  }, [getPDFViewerApplication]);
2113
+ const fieldMetadataRef = useRef([]);
2114
+ const setFieldMetadata = useCallback((fields) => {
2115
+ fieldMetadataRef.current = fields;
2116
+ logger.info("[FIELD METADATA] Updated field metadata:", fields.map((f) => ({ name: f.name, fontSize: f.fontSize, type: f.type })));
2117
+ }, []);
2118
+ const extractFieldFontSizes = useCallback(async () => {
2119
+ const fontSizeMap = {};
2120
+ for (const field of fieldMetadataRef.current) {
2121
+ if (field.type === "text" /* TEXT */) {
2122
+ if (field.fontSize && field.fontSize >= 8 && field.fontSize <= 72) {
2123
+ fontSizeMap[field.name] = field.fontSize;
2124
+ }
2125
+ }
2126
+ }
2127
+ if (Object.keys(fontSizeMap).length === 0) {
2128
+ try {
2129
+ const PDFViewerApplication = await waitForInitialization();
2130
+ if (PDFViewerApplication?.pdfDocument?.getFieldObjects) {
2131
+ const fieldObjects = await PDFViewerApplication.pdfDocument.getFieldObjects();
2132
+ for (const [fieldName, fields] of Object.entries(fieldObjects)) {
2133
+ const fieldArray = fields;
2134
+ for (const field of fieldArray) {
2135
+ if (field.alternateText) {
2136
+ const match = field.alternateText.match(/\|fontSize:(\d+)$/);
2137
+ if (match) {
2138
+ fontSizeMap[fieldName] = parseInt(match[1], 10);
2139
+ break;
2140
+ }
2141
+ }
2142
+ }
2143
+ }
2144
+ }
2145
+ } catch (error) {
2146
+ logger.warn("[FONT SIZE] Error extracting from PDF:", error);
2147
+ }
2148
+ }
2149
+ return fontSizeMap;
2150
+ }, [waitForInitialization]);
2151
+ const injectFormFieldFontSizeCSS = useCallback(async (doc) => {
2152
+ const styleId = "signiphi-pdf-font-size-style";
2153
+ const existingStyle = doc.getElementById(styleId);
2154
+ if (existingStyle) {
2155
+ existingStyle.remove();
2156
+ }
2157
+ const fontSizeMap = await extractFieldFontSizes();
2158
+ const cssRules = [];
2159
+ for (const [fieldName, fontSize] of Object.entries(fontSizeMap)) {
2160
+ cssRules.push(`
2161
+ input[name="${fieldName}"],
2162
+ input[name="${fieldName}_date"],
2163
+ select[name="${fieldName}"] {
2164
+ font-size: ${fontSize}px !important;
2165
+ }
2166
+ `);
2167
+ }
2168
+ cssRules.push(`
2169
+ input[data-element-id][name*="date"],
2170
+ select[data-element-id] {
2171
+ font-size: 18px !important;
2172
+ }
2173
+ `);
2174
+ cssRules.push(`
2175
+ input[type="text"][data-element-id]:not([style*="font-size"]),
2176
+ select[data-element-id]:not([style*="font-size"]) {
2177
+ font-size: 12px !important; /* Default fallback */
2178
+ }
2179
+ `);
2180
+ const style = doc.createElement("style");
2181
+ style.id = styleId;
2182
+ style.textContent = `/* Dynamic font sizes from PDF metadata */
2183
+ ${cssRules.join("\n")}`;
2184
+ doc.head.appendChild(style);
2185
+ }, [extractFieldFontSizes]);
1748
2186
  const loadPdf = useCallback(
1749
2187
  async (pdfUrl) => {
1750
2188
  const iframe = iframeRef.current;
@@ -1775,7 +2213,10 @@ var PdfViewerCore = forwardRef(
1775
2213
  try {
1776
2214
  const PDFViewerApplication = getPDFViewerApplication();
1777
2215
  if (PDFViewerApplication && PDFViewerApplication.initializedPromise) {
1778
- PDFViewerApplication.initializedPromise.then(() => {
2216
+ PDFViewerApplication.initializedPromise.then(async () => {
2217
+ if (iframe.contentDocument) {
2218
+ await injectFormFieldFontSizeCSS(iframe.contentDocument);
2219
+ }
1779
2220
  resolve();
1780
2221
  }).catch(reject);
1781
2222
  } else {
@@ -1855,6 +2296,7 @@ var PdfViewerCore = forwardRef(
1855
2296
  }
1856
2297
  }
1857
2298
  logger.info(`[RADIO DETECT] Found ${Object.keys(radioGroups).length} radio groups:`, Object.keys(radioGroups));
2299
+ const processedRadioFieldNames = new Set(Object.keys(radioGroups));
1858
2300
  for (const [fieldName, radioButtons] of Object.entries(radioGroups)) {
1859
2301
  const selectedRadio = radioButtons.find(
1860
2302
  (rb) => rb.data && typeof rb.data === "object" && rb.data.value === true
@@ -1880,7 +2322,7 @@ var PdfViewerCore = forwardRef(
1880
2322
  }
1881
2323
  for (const [id, data] of Object.entries(storedData)) {
1882
2324
  const fieldName = idToNameMap[id];
1883
- if (fieldName && values[fieldName]) {
2325
+ if (fieldName && (fieldName in values || processedRadioFieldNames.has(fieldName))) {
1884
2326
  continue;
1885
2327
  }
1886
2328
  if (fieldName) {
@@ -1897,13 +2339,13 @@ var PdfViewerCore = forwardRef(
1897
2339
  } else if (data !== void 0 && data !== null) {
1898
2340
  extractedValue = String(data);
1899
2341
  }
1900
- if (extractedValue !== void 0 && extractedValue !== "") {
2342
+ if (extractedValue !== void 0) {
1901
2343
  values[fieldName] = extractedValue;
1902
2344
  }
1903
2345
  }
1904
2346
  }
1905
2347
  for (const [name, fields] of Object.entries(fieldObjects)) {
1906
- if (!values[name]) {
2348
+ if (!(name in values)) {
1907
2349
  const fieldArray = fields;
1908
2350
  const field = fieldArray[0];
1909
2351
  if (field && field.value !== void 0 && field.value !== null) {
@@ -2104,6 +2546,8 @@ var PdfViewerCore = forwardRef(
2104
2546
  }
2105
2547
  }, 1e3);
2106
2548
  }, []);
2549
+ const injectRadioLabels = useCallback((_fields) => {
2550
+ }, []);
2107
2551
  const zoomIn = useCallback(async () => {
2108
2552
  try {
2109
2553
  const PDFViewerApplication = await waitForInitialization();
@@ -2174,7 +2618,7 @@ var PdfViewerCore = forwardRef(
2174
2618
  return null;
2175
2619
  }
2176
2620
  }, [waitForInitialization]);
2177
- const attachFieldClickInterceptors = useCallback((onFieldClick) => {
2621
+ const attachFieldClickInterceptors = useCallback(async (onFieldClick) => {
2178
2622
  const iframe = iframeRef.current;
2179
2623
  if (!iframe?.contentDocument) {
2180
2624
  logger.warn("Cannot attach field interceptors: iframe not ready");
@@ -2182,6 +2626,7 @@ var PdfViewerCore = forwardRef(
2182
2626
  }
2183
2627
  try {
2184
2628
  const doc = iframe.contentDocument;
2629
+ await injectFormFieldFontSizeCSS(doc);
2185
2630
  const clickHighlightStyleId = "signiphi-click-highlight-style";
2186
2631
  if (!doc.getElementById(clickHighlightStyleId)) {
2187
2632
  const style = doc.createElement("style");
@@ -2252,7 +2697,7 @@ var PdfViewerCore = forwardRef(
2252
2697
  return;
2253
2698
  }
2254
2699
  logger.info(`Field click intercepted: ${fieldName}`, e.type);
2255
- const shouldProceed = onFieldClick(fieldName);
2700
+ const shouldProceed = onFieldClick(fieldName, target);
2256
2701
  logger.info(`Field ${fieldName} shouldProceed: ${shouldProceed}`);
2257
2702
  if (e.type !== "pointerdown" && e.type !== "mousedown") {
2258
2703
  if (!shouldProceed) {
@@ -2328,6 +2773,57 @@ var PdfViewerCore = forwardRef(
2328
2773
  logger.error("Error attaching field click interceptors:", error);
2329
2774
  }
2330
2775
  }, []);
2776
+ const attachFieldChangeListeners = useCallback(async (onFieldChange) => {
2777
+ try {
2778
+ const iframe = iframeRef.current;
2779
+ if (!iframe?.contentDocument) {
2780
+ logger.warn("Cannot attach field change listeners: iframe not ready");
2781
+ return () => {
2782
+ };
2783
+ }
2784
+ const doc = iframe.contentDocument;
2785
+ const handleChange = (event) => {
2786
+ const target = event.target;
2787
+ if (!target || !["INPUT", "SELECT", "TEXTAREA"].includes(target.tagName)) {
2788
+ return;
2789
+ }
2790
+ const fieldName = target.getAttribute("name") || target.getAttribute("data-element-id") || target.getAttribute("id") || "";
2791
+ if (!fieldName) return;
2792
+ let value = "";
2793
+ if (target.tagName === "INPUT") {
2794
+ const inputElement = target;
2795
+ if (inputElement.type === "checkbox") {
2796
+ value = inputElement.checked ? "true" : "false";
2797
+ } else if (inputElement.type === "radio") {
2798
+ if (inputElement.checked) {
2799
+ value = inputElement.value || "true";
2800
+ } else {
2801
+ return;
2802
+ }
2803
+ } else {
2804
+ value = inputElement.value;
2805
+ }
2806
+ } else if (target.tagName === "SELECT") {
2807
+ value = target.value;
2808
+ } else if (target.tagName === "TEXTAREA") {
2809
+ value = target.value;
2810
+ }
2811
+ onFieldChange(fieldName, value, target);
2812
+ };
2813
+ doc.addEventListener("change", handleChange, true);
2814
+ doc.addEventListener("input", handleChange, true);
2815
+ logger.info("Attached field change listeners to iframe document");
2816
+ return () => {
2817
+ doc.removeEventListener("change", handleChange, true);
2818
+ doc.removeEventListener("input", handleChange, true);
2819
+ logger.info("Removed field change listeners from iframe document");
2820
+ };
2821
+ } catch (error) {
2822
+ logger.error("Error attaching field change listeners:", error);
2823
+ return () => {
2824
+ };
2825
+ }
2826
+ }, []);
2331
2827
  const addFieldIndicator = useCallback(async (fieldName, indicatorType = "completed", allowedFieldIds) => {
2332
2828
  try {
2333
2829
  const iframe = iframeRef.current;
@@ -2543,41 +3039,310 @@ var PdfViewerCore = forwardRef(
2543
3039
  logger.error("Error removing field indicator:", error);
2544
3040
  }
2545
3041
  }, []);
2546
- useEffect(() => {
2547
- if (!versionChecked) {
2548
- const config = getPdfJsConfig();
2549
- const basePath = pdfjsBasePath ?? config.viewerBasePath;
2550
- checkPdfJsVersion(basePath).then((result) => {
2551
- setVersionChecked(true);
2552
- if (!result.viewerExists) {
2553
- const errorMsg = `\u26A0\uFE0F PDF.js files not found
2554
-
2555
- The PDF viewer requires PDF.js files to be installed.
2556
- This should happen automatically, but you can run:
2557
-
2558
- npx signiphi-setup
2559
-
2560
- Or reinstall the package:
2561
-
2562
- npm install @signiphi/pdf-signer`;
2563
- setSetupError(errorMsg);
2564
- logger.error(errorMsg);
2565
- } else if (!result.versionMatch) {
2566
- logger.warn(
2567
- `\u26A0\uFE0F PDF.js version mismatch:
2568
- Viewer: ${result.viewerVersion}
2569
- Worker: ${result.workerVersion}
2570
- Run: npx signiphi-setup to sync versions`
2571
- );
3042
+ const addDateFieldIndicator = useCallback((fieldName) => {
3043
+ try {
3044
+ const iframe = iframeRef.current;
3045
+ if (!iframe?.contentDocument) {
3046
+ logger.warn("Cannot add date field indicator: iframe not ready");
3047
+ return;
3048
+ }
3049
+ const doc = iframe.contentDocument;
3050
+ const formElement = doc.querySelector(
3051
+ `input[name="${fieldName}"], input[data-element-id="${fieldName}"], textarea[name="${fieldName}"], textarea[data-element-id="${fieldName}"]`
3052
+ );
3053
+ if (!formElement) {
3054
+ logger.warn(`Cannot add date indicator: field not found for "${fieldName}"`);
3055
+ return;
3056
+ }
3057
+ const annotationSection = formElement.closest("section");
3058
+ if (!annotationSection) {
3059
+ logger.warn(`Cannot add date indicator: no annotation section for "${fieldName}"`);
3060
+ return;
3061
+ }
3062
+ const existingIndicator = annotationSection.querySelector(".signiphi-date-field-indicator");
3063
+ if (existingIndicator) {
3064
+ existingIndicator.remove();
3065
+ }
3066
+ const indicator = doc.createElement("div");
3067
+ indicator.className = "signiphi-date-field-indicator";
3068
+ indicator.setAttribute("role", "button");
3069
+ indicator.setAttribute("aria-label", "Open calendar picker");
3070
+ indicator.setAttribute("tabindex", "0");
3071
+ indicator.innerHTML = `
3072
+ <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
3073
+ <rect x="2" y="3" width="14" height="13" rx="2" stroke="#6b7280" stroke-width="1.5" fill="white"/>
3074
+ <line x1="2" y1="6.5" x2="16" y2="6.5" stroke="#6b7280" stroke-width="1.5"/>
3075
+ <line x1="5.5" y1="1" x2="5.5" y2="5" stroke="#6b7280" stroke-width="1.5" stroke-linecap="round"/>
3076
+ <line x1="12.5" y1="1" x2="12.5" y2="5" stroke="#6b7280" stroke-width="1.5" stroke-linecap="round"/>
3077
+ <circle cx="5.5" cy="9.5" r="0.8" fill="#6b7280"/>
3078
+ <circle cx="9" cy="9.5" r="0.8" fill="#6b7280"/>
3079
+ <circle cx="12.5" cy="9.5" r="0.8" fill="#6b7280"/>
3080
+ <circle cx="5.5" cy="12.5" r="0.8" fill="#6b7280"/>
3081
+ <circle cx="9" cy="12.5" r="0.8" fill="#6b7280"/>
3082
+ <circle cx="12.5" cy="12.5" r="0.8" fill="#6b7280"/>
3083
+ </svg>
3084
+ `;
3085
+ Object.assign(indicator.style, {
3086
+ position: "absolute",
3087
+ top: "50%",
3088
+ right: "4px",
3089
+ transform: "translateY(-50%)",
3090
+ width: "24px",
3091
+ height: "24px",
3092
+ display: "flex",
3093
+ alignItems: "center",
3094
+ justifyContent: "center",
3095
+ zIndex: "1000",
3096
+ cursor: "pointer",
3097
+ opacity: "0.7",
3098
+ transition: "opacity 0.2s ease-in-out",
3099
+ pointerEvents: "auto",
3100
+ padding: "3px",
3101
+ borderRadius: "4px"
3102
+ });
3103
+ indicator.addEventListener("mouseenter", () => {
3104
+ indicator.style.opacity = "1";
3105
+ indicator.style.backgroundColor = "#f3f4f6";
3106
+ });
3107
+ indicator.addEventListener("mouseleave", () => {
3108
+ indicator.style.opacity = "0.7";
3109
+ indicator.style.backgroundColor = "transparent";
3110
+ });
3111
+ indicator.addEventListener("keydown", (e) => {
3112
+ if (e.key === "Enter" || e.key === " ") {
3113
+ e.preventDefault();
3114
+ indicator.click();
2572
3115
  }
2573
- }).catch((error) => {
2574
- logger.error("Failed to check PDF.js version:", error);
2575
- setVersionChecked(true);
2576
3116
  });
3117
+ const currentPosition = window.getComputedStyle(annotationSection).position;
3118
+ if (currentPosition === "static") {
3119
+ annotationSection.style.position = "relative";
3120
+ }
3121
+ annotationSection.appendChild(indicator);
3122
+ logger.info(`Added date field indicator for: ${fieldName}`);
3123
+ } catch (error) {
3124
+ logger.error("Error adding date field indicator:", error);
2577
3125
  }
2578
- }, [pdfjsBasePath, versionChecked]);
2579
- const handleIframeLoad = useCallback(() => {
2580
- const iframe = iframeRef.current;
3126
+ }, []);
3127
+ const removeDateFieldIndicator = useCallback((fieldName) => {
3128
+ try {
3129
+ const iframe = iframeRef.current;
3130
+ if (!iframe?.contentDocument) return;
3131
+ const doc = iframe.contentDocument;
3132
+ const formElement = doc.querySelector(
3133
+ `input[name="${fieldName}"], input[data-element-id="${fieldName}"], textarea[name="${fieldName}"], textarea[data-element-id="${fieldName}"]`
3134
+ );
3135
+ if (!formElement) return;
3136
+ const annotationSection = formElement.closest("section");
3137
+ if (!annotationSection) return;
3138
+ const indicator = annotationSection.querySelector(".signiphi-date-field-indicator");
3139
+ if (indicator) {
3140
+ indicator.remove();
3141
+ logger.info(`Removed date field indicator from: ${fieldName}`);
3142
+ }
3143
+ } catch (error) {
3144
+ logger.error("Error removing date field indicator:", error);
3145
+ }
3146
+ }, []);
3147
+ const findFieldElements = useCallback((fieldName) => {
3148
+ const iframe = iframeRef.current;
3149
+ if (!iframe?.contentDocument) return [];
3150
+ const doc = iframe.contentDocument;
3151
+ const targetElements = [];
3152
+ if (fieldName === "signature_field_main") {
3153
+ const allInputs = doc.querySelectorAll("input[name], input[data-element-id]");
3154
+ allInputs.forEach((input) => {
3155
+ const name = input.getAttribute("name") || input.getAttribute("data-element-id") || "";
3156
+ if (name.toLowerCase().includes("signature") && !name.toLowerCase().includes("initials")) {
3157
+ targetElements.push(input);
3158
+ }
3159
+ });
3160
+ } else if (fieldName === "initials_field_main") {
3161
+ const allInputs = doc.querySelectorAll("input[name], input[data-element-id]");
3162
+ allInputs.forEach((input) => {
3163
+ const name = input.getAttribute("name") || input.getAttribute("data-element-id") || "";
3164
+ if (name.toLowerCase().includes("initials")) {
3165
+ targetElements.push(input);
3166
+ }
3167
+ });
3168
+ } else {
3169
+ const formElement = doc.querySelector(
3170
+ `input[name="${fieldName}"], input[data-element-id="${fieldName}"]`
3171
+ );
3172
+ if (formElement) {
3173
+ targetElements.push(formElement);
3174
+ }
3175
+ }
3176
+ return targetElements;
3177
+ }, []);
3178
+ const previewSignature = useCallback((fieldName, dataUrl) => {
3179
+ try {
3180
+ const iframe = iframeRef.current;
3181
+ if (!iframe?.contentDocument) {
3182
+ logger.warn("Cannot preview signature: iframe not ready");
3183
+ return;
3184
+ }
3185
+ const doc = iframe.contentDocument;
3186
+ const targetElements = findFieldElements(fieldName);
3187
+ targetElements.forEach((formElement) => {
3188
+ const annotationSection = formElement.closest("section");
3189
+ if (!annotationSection) return;
3190
+ const existing = annotationSection.querySelector(".signiphi-signature-preview");
3191
+ if (existing) existing.remove();
3192
+ if (!dataUrl) return;
3193
+ const preview = doc.createElement("div");
3194
+ preview.className = "signiphi-signature-preview";
3195
+ preview.setAttribute("aria-hidden", "true");
3196
+ const img = doc.createElement("img");
3197
+ img.src = dataUrl;
3198
+ img.alt = "";
3199
+ Object.assign(img.style, {
3200
+ width: "100%",
3201
+ height: "100%",
3202
+ objectFit: "contain",
3203
+ pointerEvents: "none"
3204
+ });
3205
+ preview.appendChild(img);
3206
+ Object.assign(preview.style, {
3207
+ position: "absolute",
3208
+ top: "0",
3209
+ left: "0",
3210
+ width: "100%",
3211
+ height: "100%",
3212
+ display: "flex",
3213
+ alignItems: "center",
3214
+ justifyContent: "center",
3215
+ zIndex: "999",
3216
+ pointerEvents: "none",
3217
+ padding: "2px",
3218
+ boxSizing: "border-box",
3219
+ backgroundColor: "rgba(255, 255, 255, 0.85)",
3220
+ opacity: "0",
3221
+ transition: "opacity 0.3s ease-in-out"
3222
+ });
3223
+ const currentPosition = annotationSection.style.position || getComputedStyle(annotationSection).position;
3224
+ if (currentPosition === "static" || !currentPosition) {
3225
+ annotationSection.style.position = "relative";
3226
+ }
3227
+ annotationSection.appendChild(preview);
3228
+ requestAnimationFrame(() => {
3229
+ preview.style.opacity = "1";
3230
+ });
3231
+ });
3232
+ logger.info(`Previewed signature on ${targetElements.length} field(s) for: ${fieldName}`);
3233
+ } catch (error) {
3234
+ logger.error("Error previewing signature:", error);
3235
+ }
3236
+ }, [findFieldElements]);
3237
+ const previewInitials = useCallback((fieldName, text) => {
3238
+ try {
3239
+ const iframe = iframeRef.current;
3240
+ if (!iframe?.contentDocument) {
3241
+ logger.warn("Cannot preview initials: iframe not ready");
3242
+ return;
3243
+ }
3244
+ const doc = iframe.contentDocument;
3245
+ const targetElements = findFieldElements(fieldName);
3246
+ targetElements.forEach((formElement) => {
3247
+ const annotationSection = formElement.closest("section");
3248
+ if (!annotationSection) return;
3249
+ const existing = annotationSection.querySelector(".signiphi-initials-preview");
3250
+ if (existing) existing.remove();
3251
+ if (!text) return;
3252
+ const preview = doc.createElement("div");
3253
+ preview.className = "signiphi-initials-preview";
3254
+ preview.setAttribute("aria-hidden", "true");
3255
+ preview.textContent = text;
3256
+ Object.assign(preview.style, {
3257
+ position: "absolute",
3258
+ top: "0",
3259
+ left: "0",
3260
+ width: "100%",
3261
+ height: "100%",
3262
+ display: "flex",
3263
+ alignItems: "center",
3264
+ justifyContent: "center",
3265
+ zIndex: "999",
3266
+ pointerEvents: "none",
3267
+ fontFamily: "'Dancing Script', 'Brush Script MT', cursive",
3268
+ fontStyle: "italic",
3269
+ fontSize: "70%",
3270
+ color: "#000000",
3271
+ backgroundColor: "rgba(255, 255, 255, 0.85)",
3272
+ overflow: "hidden",
3273
+ opacity: "0",
3274
+ transition: "opacity 0.3s ease-in-out"
3275
+ });
3276
+ const currentPosition = annotationSection.style.position || getComputedStyle(annotationSection).position;
3277
+ if (currentPosition === "static" || !currentPosition) {
3278
+ annotationSection.style.position = "relative";
3279
+ }
3280
+ annotationSection.appendChild(preview);
3281
+ requestAnimationFrame(() => {
3282
+ preview.style.opacity = "1";
3283
+ });
3284
+ });
3285
+ logger.info(`Previewed initials on ${targetElements.length} field(s) for: ${fieldName}`);
3286
+ } catch (error) {
3287
+ logger.error("Error previewing initials:", error);
3288
+ }
3289
+ }, [findFieldElements]);
3290
+ const clearFieldPreviews = useCallback((fieldName) => {
3291
+ try {
3292
+ const iframe = iframeRef.current;
3293
+ if (!iframe?.contentDocument) return;
3294
+ const doc = iframe.contentDocument;
3295
+ const selectors = ".signiphi-signature-preview, .signiphi-initials-preview";
3296
+ if (fieldName) {
3297
+ const targetElements = findFieldElements(fieldName);
3298
+ targetElements.forEach((el) => {
3299
+ const section = el.closest("section");
3300
+ if (section) {
3301
+ section.querySelectorAll(selectors).forEach((p) => p.remove());
3302
+ }
3303
+ });
3304
+ } else {
3305
+ doc.querySelectorAll(selectors).forEach((el) => el.remove());
3306
+ }
3307
+ } catch (error) {
3308
+ logger.error("Error clearing field previews:", error);
3309
+ }
3310
+ }, [findFieldElements]);
3311
+ useEffect(() => {
3312
+ if (!versionChecked) {
3313
+ const config = getPdfJsConfig();
3314
+ const basePath = pdfjsBasePath ?? config.viewerBasePath;
3315
+ checkPdfJsVersion(basePath).then((result) => {
3316
+ setVersionChecked(true);
3317
+ if (!result.viewerExists) {
3318
+ const errorMsg = `\u26A0\uFE0F PDF.js files not found
3319
+
3320
+ The PDF viewer requires PDF.js files to be installed.
3321
+ This should happen automatically, but you can run:
3322
+
3323
+ npx signiphi-setup
3324
+
3325
+ Or reinstall the package:
3326
+
3327
+ npm install @signiphi/pdf-signer`;
3328
+ setSetupError(errorMsg);
3329
+ logger.error(errorMsg);
3330
+ } else if (!result.versionMatch) {
3331
+ logger.warn(
3332
+ `\u26A0\uFE0F PDF.js version mismatch:
3333
+ Viewer: ${result.viewerVersion}
3334
+ Worker: ${result.workerVersion}
3335
+ Run: npx signiphi-setup to sync versions`
3336
+ );
3337
+ }
3338
+ }).catch((error) => {
3339
+ logger.error("Failed to check PDF.js version:", error);
3340
+ setVersionChecked(true);
3341
+ });
3342
+ }
3343
+ }, [pdfjsBasePath, versionChecked]);
3344
+ const handleIframeLoad = useCallback(() => {
3345
+ const iframe = iframeRef.current;
2581
3346
  if (!iframe || !iframe.contentWindow) {
2582
3347
  return;
2583
3348
  }
@@ -2604,22 +3369,14 @@ Or check your configuration.`;
2604
3369
  await initializationPromise.current;
2605
3370
  isLoadingRef.current = false;
2606
3371
  onLoad?.();
2607
- } else {
2608
- const PDFViewerApplication = getPDFViewerApplication();
2609
- if (PDFViewerApplication) {
2610
- isLoadingRef.current = false;
2611
- onLoad?.();
2612
- } else {
2613
- throw new Error("PDFViewerApplication not found after iframe load");
2614
- }
2615
3372
  }
2616
3373
  } catch (error) {
2617
- const errorMessage = error instanceof Error ? error.message : "Unknown error loading PDF viewer";
2618
- logger.error("Error in PDF.js initialization:", error);
3374
+ const errorMessage = error instanceof Error ? error.message : "Failed to load PDF viewer";
3375
+ logger.error("Error in handleIframeLoad:", errorMessage);
2619
3376
  isLoadingRef.current = false;
2620
3377
  onError?.(errorMessage);
2621
3378
  }
2622
- }, 1e3);
3379
+ }, 100);
2623
3380
  } catch (error) {
2624
3381
  const errorMessage = error instanceof Error ? error.message : "Unknown error in iframe load handler";
2625
3382
  logger.error("Error in iframe load handler:", error);
@@ -2637,6 +3394,8 @@ Or check your configuration.`;
2637
3394
  saveDocument,
2638
3395
  getPDFViewerApplication,
2639
3396
  injectPlaceholders,
3397
+ injectRadioLabels,
3398
+ setFieldMetadata,
2640
3399
  zoomIn,
2641
3400
  zoomOut,
2642
3401
  nextPage,
@@ -2644,10 +3403,16 @@ Or check your configuration.`;
2644
3403
  getCurrentPage,
2645
3404
  getTotalPages,
2646
3405
  attachFieldClickInterceptors,
3406
+ attachFieldChangeListeners,
2647
3407
  addFieldIndicator,
2648
- removeFieldIndicator
3408
+ removeFieldIndicator,
3409
+ addDateFieldIndicator,
3410
+ removeDateFieldIndicator,
3411
+ previewSignature,
3412
+ previewInitials,
3413
+ clearFieldPreviews
2649
3414
  }),
2650
- [loadPdf, getFormFieldValues, setFormFieldValues, getAllFieldNames, saveDocument, getPDFViewerApplication, injectPlaceholders, zoomIn, zoomOut, nextPage, previousPage, getCurrentPage, getTotalPages, attachFieldClickInterceptors, addFieldIndicator, removeFieldIndicator]
3415
+ [loadPdf, getFormFieldValues, setFormFieldValues, getAllFieldNames, saveDocument, getPDFViewerApplication, injectPlaceholders, injectRadioLabels, setFieldMetadata, zoomIn, zoomOut, nextPage, previousPage, getCurrentPage, getTotalPages, attachFieldClickInterceptors, attachFieldChangeListeners, addFieldIndicator, removeFieldIndicator, addDateFieldIndicator, removeDateFieldIndicator, previewSignature, previewInitials, clearFieldPreviews]
2651
3416
  );
2652
3417
  return /* @__PURE__ */ jsx(Fragment, { children: children({ iframeRef, handleIframeLoad }) });
2653
3418
  }
@@ -2936,7 +3701,7 @@ var buttonVariants = cva(
2936
3701
  }
2937
3702
  }
2938
3703
  );
2939
- var Button = React8.forwardRef(
3704
+ var Button = React9.forwardRef(
2940
3705
  ({ className, variant, size, asChild = false, ...props }, ref) => {
2941
3706
  const Comp = asChild ? Slot : "button";
2942
3707
  return /* @__PURE__ */ jsx(
@@ -3044,9 +3809,171 @@ var SignatureCanvas = forwardRef(
3044
3809
  }
3045
3810
  );
3046
3811
  SignatureCanvas.displayName = "SignatureCanvas";
3812
+ var Input = React9.forwardRef(
3813
+ ({ className, type, ...props }, ref) => {
3814
+ return /* @__PURE__ */ jsx(
3815
+ "input",
3816
+ {
3817
+ type,
3818
+ className: cn(
3819
+ "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",
3820
+ className
3821
+ ),
3822
+ ref,
3823
+ ...props
3824
+ }
3825
+ );
3826
+ }
3827
+ );
3828
+ Input.displayName = "Input";
3829
+ function SignatureTypeInput({
3830
+ onSave,
3831
+ onCancel,
3832
+ width = 450,
3833
+ height = 200,
3834
+ initialText = "",
3835
+ initialFont,
3836
+ className
3837
+ }) {
3838
+ const resolvedInitialFont = initialFont ?? DEFAULT_SIGNATURE_FONT;
3839
+ const [text, setText] = useState(initialText);
3840
+ const [selectedFont, setSelectedFont] = useState(resolvedInitialFont);
3841
+ const [fontsReady, setFontsReady] = useState(false);
3842
+ const [previewDataUrl, setPreviewDataUrl] = useState(null);
3843
+ const previewCanvasRef = useRef(null);
3844
+ useEffect(() => {
3845
+ loadSignatureFonts().then(() => {
3846
+ setFontsReady(true);
3847
+ }).catch((error) => {
3848
+ console.error("Failed to load signature fonts:", error);
3849
+ setFontsReady(true);
3850
+ });
3851
+ }, []);
3852
+ useEffect(() => {
3853
+ if (!fontsReady || !text.trim()) {
3854
+ setPreviewDataUrl(null);
3855
+ return;
3856
+ }
3857
+ const timeoutId = setTimeout(() => {
3858
+ try {
3859
+ const dataUrl = generateSignatureFromText({
3860
+ text: text.trim(),
3861
+ fontFamily: selectedFont.family,
3862
+ width,
3863
+ height
3864
+ });
3865
+ setPreviewDataUrl(dataUrl);
3866
+ } catch (error) {
3867
+ console.error("Failed to generate signature preview:", error);
3868
+ setPreviewDataUrl(null);
3869
+ }
3870
+ }, 50);
3871
+ return () => clearTimeout(timeoutId);
3872
+ }, [text, selectedFont, fontsReady, width, height]);
3873
+ const handleTextChange = useCallback(
3874
+ (e) => {
3875
+ setText(e.target.value);
3876
+ },
3877
+ []
3878
+ );
3879
+ const handleFontSelect = useCallback((font) => {
3880
+ setSelectedFont(font);
3881
+ }, []);
3882
+ const handleSave = useCallback(() => {
3883
+ if (!text.trim() || !previewDataUrl) {
3884
+ return;
3885
+ }
3886
+ onSave(previewDataUrl);
3887
+ }, [text, previewDataUrl, onSave]);
3888
+ const hasValidSignature = text.trim().length > 0 && previewDataUrl !== null;
3889
+ return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
3890
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
3891
+ /* @__PURE__ */ jsx(
3892
+ "label",
3893
+ {
3894
+ htmlFor: "signature-text",
3895
+ className: "text-sm font-medium text-foreground",
3896
+ children: "Enter your name:"
3897
+ }
3898
+ ),
3899
+ /* @__PURE__ */ jsx(
3900
+ Input,
3901
+ {
3902
+ id: "signature-text",
3903
+ type: "text",
3904
+ value: text,
3905
+ onChange: handleTextChange,
3906
+ placeholder: "Type your name here",
3907
+ maxLength: 50,
3908
+ autoComplete: "off",
3909
+ className: "text-base"
3910
+ }
3911
+ )
3912
+ ] }),
3913
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
3914
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-foreground", children: "Select style:" }),
3915
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: SIGNATURE_FONTS.map((font) => /* @__PURE__ */ jsx(
3916
+ Button,
3917
+ {
3918
+ type: "button",
3919
+ variant: selectedFont.name === font.name ? "default" : "outline",
3920
+ size: "sm",
3921
+ onClick: () => handleFontSelect(font),
3922
+ className: "text-xs md:text-sm",
3923
+ style: {
3924
+ fontFamily: fontsReady ? `"${font.family}", cursive` : void 0
3925
+ },
3926
+ children: font.label
3927
+ },
3928
+ font.name
3929
+ )) })
3930
+ ] }),
3931
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
3932
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium text-foreground", children: "Preview:" }),
3933
+ /* @__PURE__ */ jsx(
3934
+ "div",
3935
+ {
3936
+ className: "border-2 border-dashed border-border rounded-lg bg-muted/30 flex items-center justify-center overflow-hidden",
3937
+ style: { minHeight: Math.min(height, 150), maxHeight: height },
3938
+ 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(
3939
+ "img",
3940
+ {
3941
+ src: previewDataUrl,
3942
+ alt: "Signature preview",
3943
+ className: "max-w-full max-h-full object-contain"
3944
+ }
3945
+ ) : /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Generating preview..." })
3946
+ }
3947
+ )
3948
+ ] }),
3949
+ /* @__PURE__ */ jsx("canvas", { ref: previewCanvasRef, className: "hidden" }),
3950
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
3951
+ /* @__PURE__ */ jsx(
3952
+ Button,
3953
+ {
3954
+ type: "button",
3955
+ variant: "outline",
3956
+ onClick: onCancel,
3957
+ className: "flex-1",
3958
+ children: "Cancel"
3959
+ }
3960
+ ),
3961
+ /* @__PURE__ */ jsx(
3962
+ Button,
3963
+ {
3964
+ type: "button",
3965
+ onClick: handleSave,
3966
+ disabled: !hasValidSignature,
3967
+ className: "flex-1 font-semibold",
3968
+ children: "Save Signature"
3969
+ }
3970
+ )
3971
+ ] })
3972
+ ] }) });
3973
+ }
3047
3974
  var Dialog = DialogPrimitive.Root;
3048
3975
  var DialogPortal = DialogPrimitive.Portal;
3049
- var DialogOverlay = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { className: "signiphi-pdf-signer", children: /* @__PURE__ */ jsx(
3976
+ var DialogOverlay = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { className: "signiphi-pdf-signer", children: /* @__PURE__ */ jsx(
3050
3977
  DialogPrimitive.Overlay,
3051
3978
  {
3052
3979
  ref,
@@ -3058,7 +3985,7 @@ var DialogOverlay = React8.forwardRef(({ className, ...props }, ref) => /* @__PU
3058
3985
  }
3059
3986
  ) }));
3060
3987
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
3061
- var DialogContent = React8.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
3988
+ var DialogContent = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
3062
3989
  /* @__PURE__ */ jsx(DialogOverlay, {}),
3063
3990
  /* @__PURE__ */ jsx("div", { className: "signiphi-pdf-signer", children: /* @__PURE__ */ jsxs(
3064
3991
  DialogPrimitive.Content,
@@ -3108,7 +4035,7 @@ var DialogFooter = ({
3108
4035
  }
3109
4036
  );
3110
4037
  DialogFooter.displayName = "DialogFooter";
3111
- var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4038
+ var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3112
4039
  DialogPrimitive.Title,
3113
4040
  {
3114
4041
  ref,
@@ -3120,7 +4047,7 @@ var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
3120
4047
  }
3121
4048
  ));
3122
4049
  DialogTitle.displayName = DialogPrimitive.Title.displayName;
3123
- var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4050
+ var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3124
4051
  DialogPrimitive.Description,
3125
4052
  {
3126
4053
  ref,
@@ -3136,6 +4063,7 @@ function SignatureModalCore({
3136
4063
  fieldLabel
3137
4064
  }) {
3138
4065
  const canvasRef = useRef(null);
4066
+ const [activeTab, setActiveTab] = useState("draw");
3139
4067
  const [uploadedImage, setUploadedImage] = useState(null);
3140
4068
  const [uploadError, setUploadError] = useState(null);
3141
4069
  const fileInputRef = useRef(null);
@@ -3193,10 +4121,30 @@ function SignatureModalCore({
3193
4121
  onClose();
3194
4122
  }
3195
4123
  }, [uploadedImage, onSave, onClose]);
4124
+ const handleTabChange = useCallback((tab) => {
4125
+ setActiveTab(tab);
4126
+ }, []);
3196
4127
  const canvasWidth = 450;
3197
4128
  const canvasHeight = 200;
3198
4129
  const mobileCanvasWidth = 320;
3199
4130
  const mobileCanvasHeight = 150;
4131
+ const tabs = [
4132
+ {
4133
+ id: "draw",
4134
+ label: "Draw",
4135
+ 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" }) })
4136
+ },
4137
+ {
4138
+ id: "upload",
4139
+ label: "Upload",
4140
+ 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" }) })
4141
+ },
4142
+ {
4143
+ id: "type",
4144
+ label: "Type",
4145
+ 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" }) })
4146
+ }
4147
+ ];
3200
4148
  return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: /* @__PURE__ */ jsxs(DialogContent, { children: [
3201
4149
  /* @__PURE__ */ jsxs(DialogHeader, { className: "space-y-2 md:space-y-3", children: [
3202
4150
  /* @__PURE__ */ jsx(DialogTitle, { className: "text-lg md:text-xl font-semibold", children: "Sign Document" }),
@@ -3206,110 +4154,163 @@ function SignatureModalCore({
3206
4154
  ] })
3207
4155
  ] }),
3208
4156
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 md:gap-6", children: [
3209
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
3210
- /* @__PURE__ */ jsxs(
3211
- Button,
3212
- {
3213
- type: "button",
3214
- variant: "outline",
3215
- onClick: handleUploadClick,
3216
- className: "flex-1 text-xs md:text-sm h-9 md:h-10",
3217
- children: [
3218
- /* @__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" }) }),
3219
- "Upload Image"
3220
- ]
3221
- }
3222
- ),
3223
- /* @__PURE__ */ jsx(
3224
- "input",
4157
+ /* @__PURE__ */ jsx("div", { className: "flex gap-1 p-1 bg-muted rounded-lg", children: tabs.map((tab) => /* @__PURE__ */ jsxs(
4158
+ Button,
4159
+ {
4160
+ type: "button",
4161
+ variant: activeTab === tab.id ? "default" : "ghost",
4162
+ size: "sm",
4163
+ onClick: () => handleTabChange(tab.id),
4164
+ className: `flex-1 gap-1.5 text-xs md:text-sm ${activeTab === tab.id ? "" : "hover:bg-background/50"}`,
4165
+ children: [
4166
+ tab.icon,
4167
+ tab.label
4168
+ ]
4169
+ },
4170
+ tab.id
4171
+ )) }),
4172
+ activeTab === "draw" && /* @__PURE__ */ jsxs(Fragment, { children: [
4173
+ /* @__PURE__ */ jsx("div", { className: "block sm:hidden", children: /* @__PURE__ */ jsx(
4174
+ SignatureCanvas,
3225
4175
  {
3226
- ref: fileInputRef,
3227
- type: "file",
3228
- accept: "image/png,image/jpeg,image/jpg",
3229
- className: "hidden",
3230
- onChange: handleFileChange
4176
+ ref: canvasRef,
4177
+ width: mobileCanvasWidth,
4178
+ height: mobileCanvasHeight,
4179
+ title: "Sign Document",
4180
+ onSignature: handleSave,
4181
+ onCancel: handleCancel,
4182
+ showActions: true
3231
4183
  }
3232
- ),
3233
- uploadedImage && /* @__PURE__ */ jsx(
3234
- Button,
4184
+ ) }),
4185
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsx(
4186
+ SignatureCanvas,
3235
4187
  {
3236
- type: "button",
3237
- variant: "outline",
3238
- onClick: handleClearUpload,
3239
- className: "text-xs md:text-sm h-9 md:h-10",
3240
- children: "Clear"
4188
+ ref: canvasRef,
4189
+ width: canvasWidth,
4190
+ height: canvasHeight,
4191
+ title: "Sign Document",
4192
+ onSignature: handleSave,
4193
+ onCancel: handleCancel,
4194
+ showActions: true
3241
4195
  }
3242
- )
3243
- ] }),
3244
- !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" }),
3245
- 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: [
3246
- /* @__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" }) }),
3247
- /* @__PURE__ */ jsx("span", { children: uploadError })
4196
+ ) })
3248
4197
  ] }),
3249
- uploadedImage ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:gap-4", children: [
3250
- /* @__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(
3251
- "img",
3252
- {
3253
- src: uploadedImage,
3254
- alt: "Uploaded signature preview",
3255
- className: "max-w-full",
3256
- style: {
3257
- maxWidth: canvasWidth,
3258
- maxHeight: canvasHeight,
3259
- objectFit: "contain"
4198
+ activeTab === "upload" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
4199
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
4200
+ /* @__PURE__ */ jsxs(
4201
+ Button,
4202
+ {
4203
+ type: "button",
4204
+ variant: "outline",
4205
+ onClick: handleUploadClick,
4206
+ className: "flex-1 text-xs md:text-sm h-9 md:h-10",
4207
+ children: [
4208
+ /* @__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" }) }),
4209
+ "Choose Image"
4210
+ ]
3260
4211
  }
3261
- }
3262
- ) }),
3263
- /* @__PURE__ */ jsx(Button, { onClick: handleSaveUpload, size: "lg", className: "font-semibold text-sm md:text-base h-10 md:h-11", children: "Save Signature" })
3264
- ] }) : /* @__PURE__ */ jsx("div", { className: "block sm:hidden", children: /* @__PURE__ */ jsx(
3265
- SignatureCanvas,
4212
+ ),
4213
+ /* @__PURE__ */ jsx(
4214
+ "input",
4215
+ {
4216
+ ref: fileInputRef,
4217
+ type: "file",
4218
+ accept: "image/png,image/jpeg,image/jpg",
4219
+ className: "hidden",
4220
+ onChange: handleFileChange
4221
+ }
4222
+ ),
4223
+ uploadedImage && /* @__PURE__ */ jsx(
4224
+ Button,
4225
+ {
4226
+ type: "button",
4227
+ variant: "outline",
4228
+ onClick: handleClearUpload,
4229
+ className: "text-xs md:text-sm h-9 md:h-10",
4230
+ children: "Clear"
4231
+ }
4232
+ )
4233
+ ] }),
4234
+ !uploadedImage && /* @__PURE__ */ jsx("p", { className: "text-xs md:text-sm text-muted-foreground text-center", children: "Supported formats: PNG, JPEG (Max 5MB)" }),
4235
+ 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: [
4236
+ /* @__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" }) }),
4237
+ /* @__PURE__ */ jsx("span", { children: uploadError })
4238
+ ] }),
4239
+ uploadedImage ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:gap-4", children: [
4240
+ /* @__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(
4241
+ "img",
4242
+ {
4243
+ src: uploadedImage,
4244
+ alt: "Uploaded signature preview",
4245
+ className: "max-w-full",
4246
+ style: {
4247
+ maxWidth: canvasWidth,
4248
+ maxHeight: canvasHeight,
4249
+ objectFit: "contain"
4250
+ }
4251
+ }
4252
+ ) }),
4253
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
4254
+ /* @__PURE__ */ jsx(
4255
+ Button,
4256
+ {
4257
+ type: "button",
4258
+ variant: "outline",
4259
+ onClick: handleCancel,
4260
+ className: "flex-1",
4261
+ children: "Cancel"
4262
+ }
4263
+ ),
4264
+ /* @__PURE__ */ jsx(
4265
+ Button,
4266
+ {
4267
+ onClick: handleSaveUpload,
4268
+ className: "flex-1 font-semibold",
4269
+ children: "Save Signature"
4270
+ }
4271
+ )
4272
+ ] })
4273
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:gap-4", children: [
4274
+ /* @__PURE__ */ jsx(
4275
+ "div",
4276
+ {
4277
+ className: "border-2 border-dashed border-border rounded-lg flex items-center justify-center bg-muted/30",
4278
+ style: { minHeight: mobileCanvasHeight },
4279
+ children: /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "Upload an image of your signature" })
4280
+ }
4281
+ ),
4282
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
4283
+ /* @__PURE__ */ jsx(
4284
+ Button,
4285
+ {
4286
+ type: "button",
4287
+ variant: "outline",
4288
+ onClick: handleCancel,
4289
+ className: "flex-1",
4290
+ children: "Cancel"
4291
+ }
4292
+ ),
4293
+ /* @__PURE__ */ jsx(Button, { disabled: true, className: "flex-1 font-semibold", children: "Save Signature" })
4294
+ ] })
4295
+ ] })
4296
+ ] }),
4297
+ activeTab === "type" && /* @__PURE__ */ jsx(
4298
+ SignatureTypeInput,
3266
4299
  {
3267
- ref: canvasRef,
3268
- width: mobileCanvasWidth,
3269
- height: mobileCanvasHeight,
3270
- title: "Sign Document",
3271
- onSignature: handleSave,
4300
+ onSave: handleSave,
3272
4301
  onCancel: handleCancel,
3273
- showActions: true
3274
- }
3275
- ) }),
3276
- !uploadedImage && /* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsx(
3277
- SignatureCanvas,
3278
- {
3279
- ref: canvasRef,
3280
4302
  width: canvasWidth,
3281
- height: canvasHeight,
3282
- title: "Sign Document",
3283
- onSignature: handleSave,
3284
- onCancel: handleCancel,
3285
- showActions: true
4303
+ height: canvasHeight
3286
4304
  }
3287
- ) })
4305
+ )
3288
4306
  ] })
3289
4307
  ] }) });
3290
4308
  }
3291
4309
  var SignatureModal = SignatureModalCore;
3292
- var Input = React8.forwardRef(
3293
- ({ className, type, ...props }, ref) => {
3294
- return /* @__PURE__ */ jsx(
3295
- "input",
3296
- {
3297
- type,
3298
- className: cn(
3299
- "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",
3300
- className
3301
- ),
3302
- ref,
3303
- ...props
3304
- }
3305
- );
3306
- }
3307
- );
3308
- Input.displayName = "Input";
3309
4310
  var labelVariants = cva(
3310
4311
  "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
3311
4312
  );
3312
- var Label = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4313
+ var Label = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3313
4314
  LabelPrimitive.Root,
3314
4315
  {
3315
4316
  ref,
@@ -3390,7 +4391,7 @@ function InitialsModal({
3390
4391
  ] })
3391
4392
  ] }) });
3392
4393
  }
3393
- var Card = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4394
+ var Card = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3394
4395
  "div",
3395
4396
  {
3396
4397
  ref,
@@ -3402,7 +4403,7 @@ var Card = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ j
3402
4403
  }
3403
4404
  ));
3404
4405
  Card.displayName = "Card";
3405
- var CardHeader = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4406
+ var CardHeader = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3406
4407
  "div",
3407
4408
  {
3408
4409
  ref,
@@ -3411,7 +4412,7 @@ var CardHeader = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE_
3411
4412
  }
3412
4413
  ));
3413
4414
  CardHeader.displayName = "CardHeader";
3414
- var CardTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4415
+ var CardTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3415
4416
  "div",
3416
4417
  {
3417
4418
  ref,
@@ -3420,7 +4421,7 @@ var CardTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__
3420
4421
  }
3421
4422
  ));
3422
4423
  CardTitle.displayName = "CardTitle";
3423
- var CardDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4424
+ var CardDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3424
4425
  "div",
3425
4426
  {
3426
4427
  ref,
@@ -3429,9 +4430,9 @@ var CardDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__
3429
4430
  }
3430
4431
  ));
3431
4432
  CardDescription.displayName = "CardDescription";
3432
- var CardContent = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("px-6", className), ...props }));
4433
+ var CardContent = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("px-6", className), ...props }));
3433
4434
  CardContent.displayName = "CardContent";
3434
- var CardFooter = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4435
+ var CardFooter = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3435
4436
  "div",
3436
4437
  {
3437
4438
  ref,
@@ -3594,7 +4595,7 @@ function TextFieldRenderer({
3594
4595
  }
3595
4596
  var Select = SelectPrimitive.Root;
3596
4597
  var SelectValue = SelectPrimitive.Value;
3597
- var SelectTrigger = React8.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
4598
+ var SelectTrigger = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
3598
4599
  SelectPrimitive.Trigger,
3599
4600
  {
3600
4601
  ref,
@@ -3610,7 +4611,7 @@ var SelectTrigger = React8.forwardRef(({ className, children, ...props }, ref) =
3610
4611
  }
3611
4612
  ));
3612
4613
  SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
3613
- var SelectScrollUpButton = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4614
+ var SelectScrollUpButton = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3614
4615
  SelectPrimitive.ScrollUpButton,
3615
4616
  {
3616
4617
  ref,
@@ -3623,7 +4624,7 @@ var SelectScrollUpButton = React8.forwardRef(({ className, ...props }, ref) => /
3623
4624
  }
3624
4625
  ));
3625
4626
  SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
3626
- var SelectScrollDownButton = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4627
+ var SelectScrollDownButton = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3627
4628
  SelectPrimitive.ScrollDownButton,
3628
4629
  {
3629
4630
  ref,
@@ -3636,37 +4637,53 @@ var SelectScrollDownButton = React8.forwardRef(({ className, ...props }, ref) =>
3636
4637
  }
3637
4638
  ));
3638
4639
  SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
3639
- var SelectContent = React8.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
3640
- SelectPrimitive.Content,
3641
- {
3642
- ref,
3643
- className: cn(
3644
- "signiphi-pdf-signer",
3645
- // Add scoping class to portal content
3646
- "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",
3647
- 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",
3648
- className
3649
- ),
3650
- position,
3651
- ...props,
3652
- children: [
3653
- /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
3654
- /* @__PURE__ */ jsx(
3655
- SelectPrimitive.Viewport,
3656
- {
3657
- className: cn(
3658
- "p-1",
3659
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
3660
- ),
3661
- children
3662
- }
4640
+ var SelectContent = React9.forwardRef(({ className, children, position = "popper", ...props }, ref) => {
4641
+ const mergedRef = React9.useCallback(
4642
+ (node) => {
4643
+ if (node) {
4644
+ node.style.setProperty("z-index", "10001", "important");
4645
+ }
4646
+ if (typeof ref === "function") {
4647
+ ref(node);
4648
+ } else if (ref) {
4649
+ ref.current = node;
4650
+ }
4651
+ },
4652
+ [ref]
4653
+ );
4654
+ return /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
4655
+ SelectPrimitive.Content,
4656
+ {
4657
+ ref: mergedRef,
4658
+ className: cn(
4659
+ "signiphi-pdf-signer",
4660
+ // Add scoping class to portal content
4661
+ "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",
4662
+ 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",
4663
+ className
3663
4664
  ),
3664
- /* @__PURE__ */ jsx(SelectScrollDownButton, {})
3665
- ]
3666
- }
3667
- ) }));
4665
+ position,
4666
+ style: { backgroundColor: "var(--sps-popover, white)", ...props.style },
4667
+ ...props,
4668
+ children: [
4669
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
4670
+ /* @__PURE__ */ jsx(
4671
+ SelectPrimitive.Viewport,
4672
+ {
4673
+ className: cn(
4674
+ "p-1",
4675
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
4676
+ ),
4677
+ children
4678
+ }
4679
+ ),
4680
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
4681
+ ]
4682
+ }
4683
+ ) });
4684
+ });
3668
4685
  SelectContent.displayName = SelectPrimitive.Content.displayName;
3669
- var SelectLabel = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4686
+ var SelectLabel = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3670
4687
  SelectPrimitive.Label,
3671
4688
  {
3672
4689
  ref,
@@ -3675,7 +4692,7 @@ var SelectLabel = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
3675
4692
  }
3676
4693
  ));
3677
4694
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
3678
- var SelectItem = React8.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
4695
+ var SelectItem = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
3679
4696
  SelectPrimitive.Item,
3680
4697
  {
3681
4698
  ref,
@@ -3691,7 +4708,7 @@ var SelectItem = React8.forwardRef(({ className, children, ...props }, ref) => /
3691
4708
  }
3692
4709
  ));
3693
4710
  SelectItem.displayName = SelectPrimitive.Item.displayName;
3694
- var SelectSeparator = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4711
+ var SelectSeparator = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3695
4712
  SelectPrimitive.Separator,
3696
4713
  {
3697
4714
  ref,
@@ -3822,7 +4839,7 @@ function Calendar({
3822
4839
  Calendar.displayName = "Calendar";
3823
4840
  var Popover = PopoverPrimitive.Root;
3824
4841
  var PopoverTrigger = PopoverPrimitive.Trigger;
3825
- var PopoverContent = React8.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
4842
+ var PopoverContent = React9.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
3826
4843
  PopoverPrimitive.Content,
3827
4844
  {
3828
4845
  ref,
@@ -3834,6 +4851,7 @@ var PopoverContent = React8.forwardRef(({ className, align = "center", sideOffse
3834
4851
  "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",
3835
4852
  className
3836
4853
  ),
4854
+ style: { backgroundColor: "var(--sps-popover, white)", ...props.style },
3837
4855
  ...props
3838
4856
  }
3839
4857
  ) }));
@@ -3939,10 +4957,10 @@ function DateFieldRenderer({
3939
4957
  "aria-required": field.required,
3940
4958
  children: hasInvalidDate ? /* @__PURE__ */ jsxs(Fragment, { children: [
3941
4959
  /* @__PURE__ */ jsx(AlertCircle, { className: "mr-2 h-4 w-4 text-destructive" }),
3942
- /* @__PURE__ */ jsx("span", { className: "text-destructive", children: invalidDateValue })
4960
+ /* @__PURE__ */ jsx("span", { className: "text-destructive text-sm", children: invalidDateValue })
3943
4961
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
3944
4962
  /* @__PURE__ */ jsx(Calendar$1, { className: "mr-2 h-4 w-4" }),
3945
- date ? format(date, "PPP") : /* @__PURE__ */ jsx("span", { children: "Pick a date" })
4963
+ date ? /* @__PURE__ */ jsx("span", { className: "text-sm", children: format(date, "PPP") }) : /* @__PURE__ */ jsx("span", { className: "text-sm", children: "Pick a date" })
3946
4964
  ] })
3947
4965
  }
3948
4966
  ) }),
@@ -3975,7 +4993,7 @@ function DateFieldRenderer({
3975
4993
  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." })
3976
4994
  ] });
3977
4995
  }
3978
- var Checkbox = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4996
+ var Checkbox = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3979
4997
  CheckboxPrimitive.Root,
3980
4998
  {
3981
4999
  ref,
@@ -4038,7 +5056,7 @@ function CheckboxRenderer({
4038
5056
  error && /* @__PURE__ */ jsx("div", { className: "text-sm text-destructive mt-1", children: error })
4039
5057
  ] });
4040
5058
  }
4041
- var RadioGroup = React8.forwardRef(({ className, ...props }, ref) => {
5059
+ var RadioGroup = React9.forwardRef(({ className, ...props }, ref) => {
4042
5060
  return /* @__PURE__ */ jsx(
4043
5061
  RadioGroupPrimitive.Root,
4044
5062
  {
@@ -4049,7 +5067,7 @@ var RadioGroup = React8.forwardRef(({ className, ...props }, ref) => {
4049
5067
  );
4050
5068
  });
4051
5069
  RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
4052
- var RadioGroupItem = React8.forwardRef(({ className, ...props }, ref) => {
5070
+ var RadioGroupItem = React9.forwardRef(({ className, ...props }, ref) => {
4053
5071
  return /* @__PURE__ */ jsx(
4054
5072
  RadioGroupPrimitive.Item,
4055
5073
  {
@@ -4072,12 +5090,22 @@ function RadioGroupRenderer({
4072
5090
  className = ""
4073
5091
  }) {
4074
5092
  const options = field.options || [];
5093
+ const [localValue, setLocalValue] = useState(value);
5094
+ useEffect(() => {
5095
+ if (value && value !== localValue) {
5096
+ setLocalValue(value);
5097
+ }
5098
+ }, [value]);
5099
+ const handleChange = (newValue) => {
5100
+ setLocalValue(newValue);
5101
+ onChange(newValue);
5102
+ };
4075
5103
  return /* @__PURE__ */ jsxs("div", { className: cn("w-full", className), children: [
4076
5104
  /* @__PURE__ */ jsx(
4077
5105
  RadioGroup,
4078
5106
  {
4079
- value,
4080
- onValueChange: onChange,
5107
+ value: localValue,
5108
+ onValueChange: handleChange,
4081
5109
  required: field.required,
4082
5110
  className: "",
4083
5111
  children: options.map((option) => {
@@ -4086,7 +5114,7 @@ function RadioGroupRenderer({
4086
5114
  "div",
4087
5115
  {
4088
5116
  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",
4089
- onClick: () => onChange(option),
5117
+ onClick: () => handleChange(option),
4090
5118
  children: [
4091
5119
  /* @__PURE__ */ jsx(
4092
5120
  RadioGroupItem,
@@ -4571,11 +5599,21 @@ function AttachmentUpload({
4571
5599
  disabled = false,
4572
5600
  maxFiles = 10,
4573
5601
  formatSize,
4574
- className = ""
5602
+ className = "",
5603
+ constraints,
5604
+ validationErrors,
5605
+ onClearErrors
4575
5606
  }) {
4576
5607
  const [isDragging, setIsDragging] = useState(false);
4577
5608
  const fileInputRef = useRef(null);
4578
5609
  const dragCounterRef = useRef(0);
5610
+ const acceptString = useMemo(() => {
5611
+ if (!constraints) return void 0;
5612
+ const types = constraints.allowedTypes ?? [];
5613
+ const extensions = constraints.allowedExtensions ?? [];
5614
+ const combined = [...types, ...extensions].join(",");
5615
+ return combined || void 0;
5616
+ }, [constraints]);
4579
5617
  const handleFileSelect = useCallback(
4580
5618
  (event) => {
4581
5619
  const files = event.target.files;
@@ -4586,6 +5624,11 @@ function AttachmentUpload({
4586
5624
  },
4587
5625
  [onAdd]
4588
5626
  );
5627
+ const openFilePicker = useCallback(() => {
5628
+ if (!disabled && !isUploading) {
5629
+ fileInputRef.current?.click();
5630
+ }
5631
+ }, [disabled, isUploading]);
4589
5632
  const handleDragEnter = useCallback((event) => {
4590
5633
  event.preventDefault();
4591
5634
  event.stopPropagation();
@@ -4620,6 +5663,36 @@ function AttachmentUpload({
4620
5663
  },
4621
5664
  [disabled, isUploading, onAdd]
4622
5665
  );
5666
+ const handlePaste = useCallback(
5667
+ (event) => {
5668
+ if (disabled || isUploading) return;
5669
+ const items = event.clipboardData?.items;
5670
+ if (!items) return;
5671
+ const files = [];
5672
+ for (let i = 0; i < items.length; i++) {
5673
+ const item = items[i];
5674
+ if (item.kind === "file") {
5675
+ const file = item.getAsFile();
5676
+ if (file) files.push(file);
5677
+ }
5678
+ }
5679
+ if (files.length > 0) {
5680
+ event.preventDefault();
5681
+ onAdd(files);
5682
+ }
5683
+ },
5684
+ [disabled, isUploading, onAdd]
5685
+ );
5686
+ const handleKeyDown = useCallback(
5687
+ (event) => {
5688
+ if (disabled || isUploading) return;
5689
+ if (event.key === "Enter" || event.key === " ") {
5690
+ event.preventDefault();
5691
+ fileInputRef.current?.click();
5692
+ }
5693
+ },
5694
+ [disabled, isUploading]
5695
+ );
4623
5696
  const getFileIcon = (fileType) => {
4624
5697
  if (fileType.startsWith("image/")) {
4625
5698
  return /* @__PURE__ */ jsx(Image, { className: "w-6 h-6" });
@@ -4630,15 +5703,22 @@ function AttachmentUpload({
4630
5703
  return /* @__PURE__ */ jsx(File, { className: "w-6 h-6" });
4631
5704
  };
4632
5705
  const canAddMore = attachments.length < maxFiles;
4633
- return /* @__PURE__ */ jsxs("div", { className: `space-y-3 md:space-y-4 ${className}`, children: [
5706
+ const totalSize = attachments.reduce((sum, att) => sum + att.size, 0);
5707
+ return /* @__PURE__ */ jsxs("div", { className: `space-y-3 md:space-y-4 ${className}`, onPaste: handlePaste, children: [
4634
5708
  canAddMore && /* @__PURE__ */ jsxs(
4635
5709
  "div",
4636
5710
  {
5711
+ onClick: openFilePicker,
5712
+ onKeyDown: handleKeyDown,
4637
5713
  onDragEnter: handleDragEnter,
4638
5714
  onDragLeave: handleDragLeave,
4639
5715
  onDragOver: handleDragOver,
4640
5716
  onDrop: handleDrop,
4641
- 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"}`,
5717
+ role: "button",
5718
+ tabIndex: disabled ? -1 : 0,
5719
+ "aria-label": `Upload files. ${attachments.length} of ${maxFiles} files attached.`,
5720
+ "aria-disabled": disabled || isUploading,
5721
+ 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"}`,
4642
5722
  children: [
4643
5723
  /* @__PURE__ */ jsx(
4644
5724
  "input",
@@ -4646,6 +5726,7 @@ function AttachmentUpload({
4646
5726
  ref: fileInputRef,
4647
5727
  type: "file",
4648
5728
  multiple: true,
5729
+ accept: acceptString,
4649
5730
  onChange: handleFileSelect,
4650
5731
  disabled: disabled || isUploading,
4651
5732
  className: "hidden",
@@ -4668,20 +5749,11 @@ function AttachmentUpload({
4668
5749
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
4669
5750
  /* @__PURE__ */ jsx("p", { className: "text-xs md:text-sm font-medium text-foreground", children: isDragging ? "Drop files here" : "Upload Attachments" }),
4670
5751
  /* @__PURE__ */ jsxs("p", { className: "text-xs md:text-sm text-muted-foreground px-2", children: [
4671
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Drag and drop or " }),
4672
- /* @__PURE__ */ jsxs(
4673
- "button",
4674
- {
4675
- type: "button",
4676
- onClick: () => fileInputRef.current?.click(),
4677
- disabled: disabled || isUploading,
4678
- className: "text-primary hover:text-primary/80 font-medium focus:outline-none focus:underline disabled:text-muted-foreground disabled:cursor-not-allowed",
4679
- children: [
4680
- /* @__PURE__ */ jsx("span", { className: "sm:hidden", children: "Tap to " }),
4681
- "browse files"
4682
- ]
4683
- }
4684
- )
5752
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: "Drag and drop, paste, or " }),
5753
+ /* @__PURE__ */ jsxs("span", { className: "text-primary font-medium", children: [
5754
+ /* @__PURE__ */ jsx("span", { className: "sm:hidden", children: "Tap to " }),
5755
+ "browse files"
5756
+ ] })
4685
5757
  ] }),
4686
5758
  /* @__PURE__ */ jsxs("p", { className: "text-xs md:text-sm text-muted-foreground", children: [
4687
5759
  "PDF, Word, Images \u2022 Max ",
@@ -4697,13 +5769,34 @@ function AttachmentUpload({
4697
5769
  ]
4698
5770
  }
4699
5771
  ),
5772
+ 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: [
5773
+ /* @__PURE__ */ jsx(AlertCircle, { className: "w-4 h-4 mt-0.5 flex-shrink-0" }),
5774
+ /* @__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)) }) }),
5775
+ onClearErrors && /* @__PURE__ */ jsx(
5776
+ "button",
5777
+ {
5778
+ type: "button",
5779
+ onClick: onClearErrors,
5780
+ className: "flex-shrink-0 p-0.5 rounded text-destructive/70 hover:text-destructive transition-colors",
5781
+ "aria-label": "Dismiss errors",
5782
+ children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
5783
+ }
5784
+ )
5785
+ ] }),
4700
5786
  attachments.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
4701
- /* @__PURE__ */ jsxs("p", { className: "text-xs md:text-sm font-medium text-foreground", children: [
4702
- "Attached Files (",
4703
- attachments.length,
4704
- "/",
4705
- maxFiles,
4706
- ")"
5787
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
5788
+ /* @__PURE__ */ jsxs("p", { className: "text-xs md:text-sm font-medium text-foreground", children: [
5789
+ "Attached Files (",
5790
+ attachments.length,
5791
+ "/",
5792
+ maxFiles,
5793
+ ")"
5794
+ ] }),
5795
+ constraints && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
5796
+ formatSize(totalSize),
5797
+ " / ",
5798
+ formatSize(constraints.maxTotalSize)
5799
+ ] })
4707
5800
  ] }),
4708
5801
  /* @__PURE__ */ jsx("div", { className: "space-y-2", children: attachments.map((attachment) => /* @__PURE__ */ jsxs(
4709
5802
  "div",
@@ -4813,39 +5906,37 @@ function RequiredFieldNavigation({
4813
5906
  className = ""
4814
5907
  }) {
4815
5908
  if (totalRequired === 0) return null;
4816
- return /* @__PURE__ */ jsxs("div", { className: `flex items-center gap-2 ${className}`, children: [
4817
- /* @__PURE__ */ jsx(Star, { size: 16, className: "text-amber-500" }),
4818
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Required Fields" }),
4819
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
4820
- /* @__PURE__ */ jsx(
4821
- Button,
4822
- {
4823
- variant: "ghost",
4824
- size: "icon",
4825
- onClick: onPrevious,
4826
- className: "h-8 w-8",
4827
- "aria-label": "Previous required field",
4828
- children: /* @__PURE__ */ jsx(ChevronLeft, { size: 16 })
4829
- }
4830
- ),
4831
- /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground px-2", children: [
4832
- currentIndex + 1,
4833
- " / ",
4834
- totalRequired
4835
- ] }),
4836
- /* @__PURE__ */ jsx(
4837
- Button,
4838
- {
4839
- variant: "ghost",
4840
- size: "icon",
4841
- onClick: onNext,
4842
- className: "h-8 w-8",
4843
- "aria-label": "Next required field",
4844
- children: /* @__PURE__ */ jsx(ChevronRight, { size: 16 })
4845
- }
4846
- )
4847
- ] })
4848
- ] });
5909
+ return /* @__PURE__ */ jsx("div", { className: `flex items-center gap-2 ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
5910
+ /* @__PURE__ */ jsx(
5911
+ Button,
5912
+ {
5913
+ variant: "ghost",
5914
+ size: "icon",
5915
+ onClick: onPrevious,
5916
+ className: "h-8 w-8",
5917
+ "aria-label": "Previous required field",
5918
+ children: /* @__PURE__ */ jsx(ChevronLeft, { size: 16 })
5919
+ }
5920
+ ),
5921
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground px-2", children: [
5922
+ currentIndex < 0 ? 0 : currentIndex + 1,
5923
+ " / ",
5924
+ totalRequired
5925
+ ] }),
5926
+ /* @__PURE__ */ jsxs(
5927
+ Button,
5928
+ {
5929
+ variant: "ghost",
5930
+ onClick: onNext,
5931
+ className: "h-8 px-3 gap-1.5",
5932
+ "aria-label": "Next required field",
5933
+ children: [
5934
+ /* @__PURE__ */ jsx(ChevronRight, { size: 16 }),
5935
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Required Field" })
5936
+ ]
5937
+ }
5938
+ )
5939
+ ] }) });
4849
5940
  }
4850
5941
  function ViewToggleToolbar({
4851
5942
  currentView,
@@ -5034,7 +6125,51 @@ function ViewToggleToolbar({
5034
6125
  }
5035
6126
  )
5036
6127
  ] }),
5037
- 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: [
6128
+ 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: [
6129
+ /* @__PURE__ */ jsx(
6130
+ Button,
6131
+ {
6132
+ type: "button",
6133
+ variant: "ghost",
6134
+ size: "icon",
6135
+ onClick: handlePreviousPage,
6136
+ disabled: !isViewerReady || currentPage === null || currentPage <= 1,
6137
+ className: cn(
6138
+ "h-7 w-7 sm:h-8 sm:w-8 rounded-md transition-all duration-200",
6139
+ "hover:bg-background/80 hover:shadow-sm",
6140
+ "disabled:opacity-40 disabled:cursor-not-allowed",
6141
+ "active:scale-95"
6142
+ ),
6143
+ "aria-label": "Previous page",
6144
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-3.5 w-3.5 sm:h-4 sm:w-4 text-foreground/80" })
6145
+ }
6146
+ ),
6147
+ 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: [
6148
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: currentPage }),
6149
+ /* @__PURE__ */ jsx("span", { className: "mx-0.5 sm:mx-1 text-foreground/40", children: "/" }),
6150
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: totalPages })
6151
+ ] }),
6152
+ /* @__PURE__ */ jsx(
6153
+ Button,
6154
+ {
6155
+ type: "button",
6156
+ variant: "ghost",
6157
+ size: "icon",
6158
+ onClick: handleNextPage,
6159
+ disabled: !isViewerReady || currentPage === null || totalPages === null || currentPage >= totalPages,
6160
+ className: cn(
6161
+ "h-7 w-7 sm:h-8 sm:w-8 rounded-md transition-all duration-200",
6162
+ "hover:bg-background/80 hover:shadow-sm",
6163
+ "disabled:opacity-40 disabled:cursor-not-allowed",
6164
+ "active:scale-95"
6165
+ ),
6166
+ "aria-label": "Next page",
6167
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5 sm:h-4 sm:w-4 text-foreground/80" })
6168
+ }
6169
+ )
6170
+ ] }),
6171
+ 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: [
6172
+ /* @__PURE__ */ jsx("div", { className: "h-5 sm:h-6 w-px bg-border/60 hidden sm:block" }),
5038
6173
  /* @__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: [
5039
6174
  /* @__PURE__ */ jsx(
5040
6175
  Button,
@@ -5073,50 +6208,6 @@ function ViewToggleToolbar({
5073
6208
  }
5074
6209
  )
5075
6210
  ] }),
5076
- /* @__PURE__ */ jsx("div", { className: "h-5 sm:h-6 w-px bg-border/60 hidden sm:block" }),
5077
- /* @__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: [
5078
- /* @__PURE__ */ jsx(
5079
- Button,
5080
- {
5081
- type: "button",
5082
- variant: "ghost",
5083
- size: "icon",
5084
- onClick: handlePreviousPage,
5085
- disabled: !isViewerReady || currentPage === null || currentPage <= 1,
5086
- className: cn(
5087
- "h-7 w-7 sm:h-8 sm:w-8 rounded-md transition-all duration-200",
5088
- "hover:bg-background/80 hover:shadow-sm",
5089
- "disabled:opacity-40 disabled:cursor-not-allowed",
5090
- "active:scale-95"
5091
- ),
5092
- "aria-label": "Previous page",
5093
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-3.5 w-3.5 sm:h-4 sm:w-4 text-foreground/80" })
5094
- }
5095
- ),
5096
- 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: [
5097
- /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: currentPage }),
5098
- /* @__PURE__ */ jsx("span", { className: "mx-0.5 sm:mx-1 text-foreground/40", children: "/" }),
5099
- /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: totalPages })
5100
- ] }),
5101
- /* @__PURE__ */ jsx(
5102
- Button,
5103
- {
5104
- type: "button",
5105
- variant: "ghost",
5106
- size: "icon",
5107
- onClick: handleNextPage,
5108
- disabled: !isViewerReady || currentPage === null || totalPages === null || currentPage >= totalPages,
5109
- className: cn(
5110
- "h-7 w-7 sm:h-8 sm:w-8 rounded-md transition-all duration-200",
5111
- "hover:bg-background/80 hover:shadow-sm",
5112
- "disabled:opacity-40 disabled:cursor-not-allowed",
5113
- "active:scale-95"
5114
- ),
5115
- "aria-label": "Next page",
5116
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5 sm:h-4 sm:w-4 text-foreground/80" })
5117
- }
5118
- )
5119
- ] }),
5120
6211
  requiredFieldNavigation?.hasRequiredFields && /* @__PURE__ */ jsxs(Fragment, { children: [
5121
6212
  /* @__PURE__ */ jsx("div", { className: "h-5 sm:h-6 w-px bg-border/60 hidden sm:block" }),
5122
6213
  /* @__PURE__ */ jsx(
@@ -5191,7 +6282,7 @@ var alertVariants = cva(
5191
6282
  }
5192
6283
  }
5193
6284
  );
5194
- var Alert = React8.forwardRef(({ className, variant, ...props }, ref) => /* @__PURE__ */ jsx(
6285
+ var Alert = React9.forwardRef(({ className, variant, ...props }, ref) => /* @__PURE__ */ jsx(
5195
6286
  "div",
5196
6287
  {
5197
6288
  ref,
@@ -5201,7 +6292,7 @@ var Alert = React8.forwardRef(({ className, variant, ...props }, ref) => /* @__P
5201
6292
  }
5202
6293
  ));
5203
6294
  Alert.displayName = "Alert";
5204
- var AlertTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
6295
+ var AlertTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5205
6296
  "h5",
5206
6297
  {
5207
6298
  ref,
@@ -5210,7 +6301,7 @@ var AlertTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE_
5210
6301
  }
5211
6302
  ));
5212
6303
  AlertTitle.displayName = "AlertTitle";
5213
- var AlertDescription = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
6304
+ var AlertDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
5214
6305
  "div",
5215
6306
  {
5216
6307
  ref,
@@ -5574,10 +6665,15 @@ function UnacknowledgedFieldsModal({
5574
6665
  onOpenChange,
5575
6666
  unacknowledgedFields,
5576
6667
  onAcknowledge,
5577
- isAcknowledged
6668
+ isAcknowledged,
6669
+ onComplete
5578
6670
  }) {
5579
6671
  const [expandedItem, setExpandedItem] = useState("");
5580
6672
  const [isManuallyClosing, setIsManuallyClosing] = useState(false);
6673
+ const onCompleteRef = useRef(onComplete);
6674
+ useEffect(() => {
6675
+ onCompleteRef.current = onComplete;
6676
+ }, [onComplete]);
5581
6677
  useEffect(() => {
5582
6678
  if (open && unacknowledgedFields.length > 0 && expandedItem === "") {
5583
6679
  const firstField = unacknowledgedFields[0];
@@ -5600,6 +6696,9 @@ function UnacknowledgedFieldsModal({
5600
6696
  (ack) => ack.id === ackId || isAcknowledged(fieldId, ack.id)
5601
6697
  );
5602
6698
  if (allAcknowledged) {
6699
+ setTimeout(() => {
6700
+ onCompleteRef.current?.([fieldId]);
6701
+ }, 150);
5603
6702
  const currentIndex = unacknowledgedFields.findIndex((f) => f.field.id === fieldId);
5604
6703
  const nextUnacknowledged = unacknowledgedFields[currentIndex + 1];
5605
6704
  if (nextUnacknowledged) {
@@ -5618,18 +6717,29 @@ function UnacknowledgedFieldsModal({
5618
6717
  };
5619
6718
  const handleAcknowledgeAll = async () => {
5620
6719
  setIsManuallyClosing(true);
6720
+ const acknowledgedFieldIds = [];
5621
6721
  unacknowledgedFields.forEach(({ field }) => {
5622
6722
  if (field.acknowledgements) {
6723
+ let fieldHadUnacknowledged = false;
5623
6724
  field.acknowledgements.forEach((ack) => {
5624
6725
  if (!isAcknowledged(field.id, ack.id)) {
5625
6726
  onAcknowledge(field.id, ack.id);
6727
+ fieldHadUnacknowledged = true;
5626
6728
  }
5627
6729
  });
6730
+ if (fieldHadUnacknowledged) {
6731
+ acknowledgedFieldIds.push(field.id);
6732
+ }
5628
6733
  }
5629
6734
  });
5630
6735
  await new Promise((resolve) => setTimeout(resolve, 150));
5631
6736
  onOpenChange(false);
5632
6737
  setIsManuallyClosing(false);
6738
+ if (acknowledgedFieldIds.length > 0) {
6739
+ setTimeout(() => {
6740
+ onCompleteRef.current?.(acknowledgedFieldIds);
6741
+ }, 150);
6742
+ }
5633
6743
  };
5634
6744
  return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-2xl max-h-[80vh] flex flex-col", children: [
5635
6745
  /* @__PURE__ */ jsxs(DialogHeader, { children: [
@@ -5914,8 +7024,297 @@ function AcknowledgementsSidebar({
5914
7024
  }) }) })
5915
7025
  ] });
5916
7026
  }
7027
+ function calculatePopupPosition(fieldPos, popupWidth, popupHeight) {
7028
+ const viewportWidth = window.innerWidth;
7029
+ const viewportHeight = window.innerHeight;
7030
+ const gap = 8;
7031
+ const edgePadding = 16;
7032
+ const fieldTop = fieldPos.y;
7033
+ const fieldBottom = fieldPos.y + fieldPos.height;
7034
+ const fieldLeft = fieldPos.x;
7035
+ const fieldRight = fieldPos.x + fieldPos.width;
7036
+ const spaceBelow = viewportHeight - fieldBottom - edgePadding;
7037
+ const spaceAbove = fieldTop - edgePadding;
7038
+ const positionBelow = spaceBelow >= popupHeight || spaceBelow >= spaceAbove;
7039
+ let top;
7040
+ if (positionBelow) {
7041
+ top = fieldBottom + gap;
7042
+ if (top + popupHeight > viewportHeight - edgePadding) {
7043
+ const topAbove = fieldTop - popupHeight - gap;
7044
+ if (topAbove >= edgePadding) {
7045
+ top = topAbove;
7046
+ } else {
7047
+ top = Math.min(fieldBottom + gap, viewportHeight - edgePadding - popupHeight);
7048
+ top = Math.max(top, edgePadding);
7049
+ }
7050
+ }
7051
+ } else {
7052
+ top = fieldTop - popupHeight - gap;
7053
+ if (top < edgePadding) {
7054
+ top = fieldBottom + gap;
7055
+ if (top + popupHeight > viewportHeight - edgePadding) {
7056
+ top = Math.max(edgePadding, viewportHeight - edgePadding - popupHeight);
7057
+ }
7058
+ }
7059
+ }
7060
+ if (top < fieldBottom && top + popupHeight > fieldTop) {
7061
+ top = fieldBottom + gap;
7062
+ if (top + popupHeight > viewportHeight - edgePadding) {
7063
+ top = fieldTop - popupHeight - gap;
7064
+ if (top < edgePadding) {
7065
+ top = edgePadding;
7066
+ }
7067
+ }
7068
+ }
7069
+ let left = fieldLeft;
7070
+ if (left + popupWidth > viewportWidth - edgePadding) {
7071
+ left = fieldRight - popupWidth;
7072
+ }
7073
+ if (left + popupWidth > viewportWidth - edgePadding) {
7074
+ left = viewportWidth - edgePadding - popupWidth;
7075
+ }
7076
+ if (left < edgePadding) {
7077
+ left = edgePadding;
7078
+ }
7079
+ return { top: Math.round(top), left: Math.round(left) };
7080
+ }
7081
+ function DateFieldCalendarPopup({
7082
+ open,
7083
+ onOpenChange,
7084
+ value,
7085
+ onSelect,
7086
+ fieldPosition,
7087
+ fieldLabel
7088
+ }) {
7089
+ const [date, setDate] = useState(void 0);
7090
+ const [position, setPosition] = useState({ top: 0, left: 0 });
7091
+ const popupRef = useRef(null);
7092
+ const frozenPositionRef = useRef(null);
7093
+ const [mounted, setMounted] = useState(false);
7094
+ useEffect(() => {
7095
+ if (value) {
7096
+ const validation = parseAndValidateDate(value);
7097
+ if (validation.isValid && validation.date) {
7098
+ setDate(validation.date);
7099
+ } else {
7100
+ setDate(void 0);
7101
+ }
7102
+ } else {
7103
+ setDate(void 0);
7104
+ }
7105
+ }, [value]);
7106
+ useEffect(() => {
7107
+ if (open && fieldPosition) {
7108
+ if (!frozenPositionRef.current) {
7109
+ frozenPositionRef.current = { ...fieldPosition };
7110
+ requestAnimationFrame(() => {
7111
+ if (popupRef.current && frozenPositionRef.current) {
7112
+ const popupRect = popupRef.current.getBoundingClientRect();
7113
+ const newPosition = calculatePopupPosition(
7114
+ frozenPositionRef.current,
7115
+ popupRect.width || 350,
7116
+ // Default width estimate
7117
+ popupRect.height || 400
7118
+ // Default height estimate
7119
+ );
7120
+ setPosition(newPosition);
7121
+ }
7122
+ });
7123
+ }
7124
+ } else if (!open) {
7125
+ frozenPositionRef.current = null;
7126
+ setPosition({ top: 0, left: 0 });
7127
+ }
7128
+ }, [open, fieldPosition]);
7129
+ useEffect(() => {
7130
+ if (popupRef.current && (position.top !== 0 || position.left !== 0)) {
7131
+ popupRef.current.style.setProperty("top", `${position.top}px`, "important");
7132
+ popupRef.current.style.setProperty("left", `${position.left}px`, "important");
7133
+ popupRef.current.style.setProperty("position", "fixed", "important");
7134
+ popupRef.current.style.setProperty("transform", "none", "important");
7135
+ }
7136
+ }, [position]);
7137
+ useEffect(() => {
7138
+ if (!open) return;
7139
+ const handleClickOutside = (event) => {
7140
+ const target = event.target;
7141
+ if (popupRef.current && popupRef.current.contains(target)) {
7142
+ return;
7143
+ }
7144
+ const selectElement = target.closest?.(
7145
+ "[data-radix-select-content], [data-radix-select-item], [data-radix-select-trigger], [data-radix-select-viewport]"
7146
+ );
7147
+ if (selectElement) {
7148
+ return;
7149
+ }
7150
+ onOpenChange(false);
7151
+ };
7152
+ const handleEscape = (event) => {
7153
+ if (event.key === "Escape") {
7154
+ onOpenChange(false);
7155
+ }
7156
+ };
7157
+ const timeoutId = setTimeout(() => {
7158
+ document.addEventListener("mousedown", handleClickOutside);
7159
+ document.addEventListener("keydown", handleEscape);
7160
+ }, 100);
7161
+ return () => {
7162
+ clearTimeout(timeoutId);
7163
+ document.removeEventListener("mousedown", handleClickOutside);
7164
+ document.removeEventListener("keydown", handleEscape);
7165
+ };
7166
+ }, [open, onOpenChange]);
7167
+ useEffect(() => {
7168
+ if (!open) return;
7169
+ const handleScroll = () => {
7170
+ onOpenChange(false);
7171
+ };
7172
+ const timeoutId = setTimeout(() => {
7173
+ window.addEventListener("scroll", handleScroll, { passive: true });
7174
+ const iframe = document.querySelector("iframe");
7175
+ if (iframe?.contentWindow) {
7176
+ iframe.contentWindow.addEventListener("scroll", handleScroll, { passive: true });
7177
+ const viewerContainer = iframe.contentDocument?.querySelector("#viewerContainer");
7178
+ if (viewerContainer) {
7179
+ viewerContainer.addEventListener("scroll", handleScroll, { passive: true });
7180
+ }
7181
+ }
7182
+ }, 100);
7183
+ return () => {
7184
+ clearTimeout(timeoutId);
7185
+ window.removeEventListener("scroll", handleScroll);
7186
+ const iframe = document.querySelector("iframe");
7187
+ if (iframe?.contentWindow) {
7188
+ iframe.contentWindow.removeEventListener("scroll", handleScroll);
7189
+ const viewerContainer = iframe.contentDocument?.querySelector("#viewerContainer");
7190
+ if (viewerContainer) {
7191
+ viewerContainer.removeEventListener("scroll", handleScroll);
7192
+ }
7193
+ }
7194
+ };
7195
+ }, [open, onOpenChange]);
7196
+ useEffect(() => {
7197
+ setMounted(true);
7198
+ return () => setMounted(false);
7199
+ }, []);
7200
+ const handleDateSelect = (selectedDate) => {
7201
+ if (selectedDate) {
7202
+ setDate(selectedDate);
7203
+ const isoString = format(selectedDate, "yyyy-MM-dd");
7204
+ onSelect(isoString);
7205
+ onOpenChange(false);
7206
+ }
7207
+ };
7208
+ const handleToday = () => {
7209
+ const today = /* @__PURE__ */ new Date();
7210
+ handleDateSelect(today);
7211
+ };
7212
+ const handleClear = () => {
7213
+ setDate(void 0);
7214
+ onSelect("");
7215
+ onOpenChange(false);
7216
+ };
7217
+ const handleClose = () => {
7218
+ onOpenChange(false);
7219
+ };
7220
+ if (!open || !mounted || !fieldPosition) {
7221
+ return null;
7222
+ }
7223
+ const hasValidPosition = position.top !== 0 || position.left !== 0;
7224
+ const content = /* @__PURE__ */ jsx("div", { className: "signiphi-pdf-signer", children: /* @__PURE__ */ jsx(
7225
+ "div",
7226
+ {
7227
+ ref: popupRef,
7228
+ className: "signiphi-pdf-calendar-popup",
7229
+ style: {
7230
+ visibility: hasValidPosition ? "visible" : "hidden",
7231
+ // Use visibility instead of opacity
7232
+ opacity: hasValidPosition ? 1 : 0,
7233
+ // Also keep opacity for smooth fade-in
7234
+ pointerEvents: hasValidPosition ? "auto" : "none"
7235
+ // Disable interaction until positioned
7236
+ },
7237
+ role: "dialog",
7238
+ "aria-label": fieldLabel ? `Select date for ${fieldLabel}` : "Select date",
7239
+ "aria-modal": "true",
7240
+ children: /* @__PURE__ */ jsxs(Card, { className: "p-0 shadow-lg border-2 border-border bg-popover rounded-lg", children: [
7241
+ /* @__PURE__ */ jsx("div", { className: "p-3", children: /* @__PURE__ */ jsx(
7242
+ Calendar,
7243
+ {
7244
+ mode: "single",
7245
+ selected: date,
7246
+ onSelect: handleDateSelect,
7247
+ initialFocus: true
7248
+ }
7249
+ ) }),
7250
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2 p-3 border-t border-border", children: [
7251
+ /* @__PURE__ */ jsx(
7252
+ Button,
7253
+ {
7254
+ variant: "outline",
7255
+ size: "sm",
7256
+ onClick: handleToday,
7257
+ className: "flex-1",
7258
+ children: "Today"
7259
+ }
7260
+ ),
7261
+ /* @__PURE__ */ jsx(
7262
+ Button,
7263
+ {
7264
+ variant: "outline",
7265
+ size: "sm",
7266
+ onClick: handleClear,
7267
+ className: "flex-1",
7268
+ children: "Clear"
7269
+ }
7270
+ ),
7271
+ /* @__PURE__ */ jsx(
7272
+ Button,
7273
+ {
7274
+ variant: "ghost",
7275
+ size: "sm",
7276
+ onClick: handleClose,
7277
+ className: "flex-1",
7278
+ children: "Cancel"
7279
+ }
7280
+ )
7281
+ ] })
7282
+ ] })
7283
+ }
7284
+ ) });
7285
+ return createPortal(content, document.body);
7286
+ }
5917
7287
 
5918
7288
  // src/utils/pdf-viewer-filter.ts
7289
+ function robustlyRemoveField(form, field) {
7290
+ try {
7291
+ const fieldWithAcro = field;
7292
+ if (fieldWithAcro.acroField && typeof fieldWithAcro.acroField.getWidgets === "function") {
7293
+ let widgets = fieldWithAcro.acroField.getWidgets() || [];
7294
+ let safetyCounter = 0;
7295
+ const maxIterations = widgets.length + 5;
7296
+ while (widgets.length > 0 && safetyCounter < maxIterations) {
7297
+ try {
7298
+ const widgetIndex = widgets.length - 1;
7299
+ fieldWithAcro.acroField.removeWidget?.(widgetIndex);
7300
+ widgets = fieldWithAcro.acroField.getWidgets() || [];
7301
+ } catch {
7302
+ break;
7303
+ }
7304
+ safetyCounter++;
7305
+ }
7306
+ }
7307
+ form.removeField(field);
7308
+ return true;
7309
+ } catch (error) {
7310
+ try {
7311
+ form.removeField(field);
7312
+ return true;
7313
+ } catch {
7314
+ return false;
7315
+ }
7316
+ }
7317
+ }
5919
7318
  async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
5920
7319
  try {
5921
7320
  const labelFont = await pdfDoc.embedFont("Helvetica-Bold");
@@ -5925,8 +7324,7 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
5925
7324
  for (const field of fieldsToLabel) {
5926
7325
  const isRadioField = field.type === "radio" /* RADIO */;
5927
7326
  const hasRadioOptions = isRadioField && field.options && field.options.length > 0;
5928
- const hasCustomLabel = field.label && field.label.trim() && !isAutoGeneratedLabel(field.label);
5929
- if (!hasCustomLabel && !hasRadioOptions) {
7327
+ if (!hasDrawableLabel(field) && !hasRadioOptions) {
5930
7328
  continue;
5931
7329
  }
5932
7330
  const pdfField = form.getFieldMaybe(field.name);
@@ -5937,27 +7335,30 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
5937
7335
  const widgets = fieldWithExtensions.acroField?.getWidgets?.() || [];
5938
7336
  if (widgets.length === 0) continue;
5939
7337
  if (isRadioField) {
5940
- if (field.label && field.label.trim() && !isAutoGeneratedLabel(field.label)) {
7338
+ if (hasDrawableLabel(field)) {
5941
7339
  const firstWidget = widgets[0];
5942
- if (!firstWidget) continue;
5943
- const firstRect = firstWidget.getRectangle?.();
5944
- if (firstRect) {
5945
- const pageRef = firstWidget.P?.();
5946
- const pageIndex = findPageIndexWithFallback(pages, pageRef);
5947
- if (pageIndex >= 0) {
5948
- const page = pages[pageIndex];
5949
- if (!page) continue;
5950
- const groupLabelKey = `${pageIndex}-${field.name}-grouplabel`;
5951
- if (!drawnOnce.has(groupLabelKey)) {
5952
- const labelY = firstRect.y + firstRect.height + 5;
5953
- page.drawText(field.label, {
5954
- x: firstRect.x,
5955
- y: labelY,
5956
- size: 10,
5957
- font: labelFont,
5958
- color: rgb(0, 0, 0)
5959
- });
5960
- drawnOnce.add(groupLabelKey);
7340
+ if (firstWidget) {
7341
+ const firstRect = firstWidget.getRectangle?.();
7342
+ if (firstRect) {
7343
+ const pageRef = firstWidget.P?.();
7344
+ const pageIndex = findPageIndexWithFallback(pages, pageRef);
7345
+ if (pageIndex >= 0) {
7346
+ const page = pages[pageIndex];
7347
+ if (page) {
7348
+ const groupLabelKey = `${pageIndex}-${field.name}-grouplabel`;
7349
+ if (!drawnOnce.has(groupLabelKey)) {
7350
+ const radioLabelFontSize = Math.min(10, firstRect.height * 0.4);
7351
+ const labelY = firstRect.y + firstRect.height + 5;
7352
+ page.drawText(field.label, {
7353
+ x: firstRect.x,
7354
+ y: labelY,
7355
+ size: radioLabelFontSize,
7356
+ font: labelFont,
7357
+ color: rgb(0, 0, 0)
7358
+ });
7359
+ drawnOnce.add(groupLabelKey);
7360
+ }
7361
+ }
5961
7362
  }
5962
7363
  }
5963
7364
  }
@@ -5970,17 +7371,16 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
5970
7371
  if (!rect) continue;
5971
7372
  const pageRef = widget.P?.();
5972
7373
  const pageIndex = findPageIndexWithFallback(pages, pageRef);
5973
- if (pageIndex >= 0) {
7374
+ if (pageIndex >= 0 && pageIndex < pages.length) {
5974
7375
  const page = pages[pageIndex];
5975
7376
  if (!page) continue;
5976
- const optionTexts = field.options;
5977
- const optionText = optionTexts[i] || optionTexts[0];
7377
+ const optionText = field.options[i];
5978
7378
  const optionKey = `${pageIndex}-${field.name}-opt-${i}`;
5979
7379
  if (optionText && !drawnOnce.has(optionKey)) {
5980
7380
  page.drawText(optionText, {
5981
- x: rect.x + rect.width + 6,
5982
- y: rect.y + (rect.height - 10) / 2,
5983
- size: 10,
7381
+ x: rect.x + rect.width + 4,
7382
+ y: rect.y + (rect.height - 7) / 2,
7383
+ size: 7,
5984
7384
  font: labelFont,
5985
7385
  color: rgb(0, 0, 0)
5986
7386
  });
@@ -6001,12 +7401,13 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
6001
7401
  if (!page) continue;
6002
7402
  const key = `${pageIndex}-${field.name}`;
6003
7403
  if (!drawnOnce.has(key)) {
6004
- if (!isAutoGeneratedLabel(field.label)) {
7404
+ if (hasDrawableLabel(field)) {
7405
+ const labelFontSize = Math.min(10, rect.height * 0.4);
6005
7406
  if (field.type === "checkbox" /* CHECKBOX */) {
6006
7407
  page.drawText(field.label, {
6007
7408
  x: rect.x + rect.width + 5,
6008
- y: rect.y + rect.height * 0.2,
6009
- size: 10,
7409
+ y: rect.y + (rect.height - labelFontSize) / 2,
7410
+ size: labelFontSize,
6010
7411
  font: labelFont,
6011
7412
  color: rgb(0, 0, 0)
6012
7413
  });
@@ -6015,9 +7416,9 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
6015
7416
  page.drawText(field.label, {
6016
7417
  x: rect.x,
6017
7418
  y: labelY,
6018
- size: 9,
7419
+ size: labelFontSize,
6019
7420
  font: labelFont,
6020
- color: rgb(0.3, 0.3, 0.3)
7421
+ color: rgb(0, 0, 0)
6021
7422
  });
6022
7423
  }
6023
7424
  drawnOnce.add(key);
@@ -6078,7 +7479,7 @@ async function filterPdfForCurrentSigner(pdfBytes, allFields, multiSignerContext
6078
7479
  try {
6079
7480
  const pdfField = form.getFieldMaybe(field.name);
6080
7481
  if (pdfField) {
6081
- form.removeField(pdfField);
7482
+ robustlyRemoveField(form, pdfField);
6082
7483
  removedCount++;
6083
7484
  } else {
6084
7485
  notFoundCount++;
@@ -6112,7 +7513,7 @@ function usePdfViewer(multiSignerContext) {
6112
7513
  const [originalPdfBytes, setOriginalPdfBytes] = useState(null);
6113
7514
  const [_viewerPdfBytes, setViewerPdfBytes] = useState(null);
6114
7515
  const [extractedFields, setExtractedFields] = useState([]);
6115
- const loadPdf = useCallback(async (url) => {
7516
+ const loadPdf = useCallback(async (url, signerEmailForExtraction) => {
6116
7517
  setIsLoading(true);
6117
7518
  setError(null);
6118
7519
  setIsLoaded(false);
@@ -6125,8 +7526,22 @@ function usePdfViewer(multiSignerContext) {
6125
7526
  }
6126
7527
  const bytes = await urlToPdfBytes(url);
6127
7528
  setOriginalPdfBytes(bytes);
6128
- setViewerPdfBytes(bytes);
6129
- await viewerRef.current?.loadPdf(url);
7529
+ const fields = await extractVisibleFormFields(bytes, signerEmailForExtraction);
7530
+ setExtractedFields(fields);
7531
+ const filteredBytes = await filterPdfForCurrentSigner(
7532
+ bytes,
7533
+ fields,
7534
+ multiSignerContext || {
7535
+ isMultiSigner: false,
7536
+ currentSigner: null,
7537
+ currentSignerEmail: "",
7538
+ isPrimarySigner: true,
7539
+ isFinalSigner: true
7540
+ }
7541
+ );
7542
+ setViewerPdfBytes(filteredBytes);
7543
+ const blobUrl = createPdfBlobUrl(filteredBytes);
7544
+ await viewerRef.current?.loadPdf(blobUrl);
6130
7545
  } catch (err) {
6131
7546
  const errorMessage = err instanceof Error ? err.message : "Failed to load PDF";
6132
7547
  logger.error("Error loading PDF:", err);
@@ -6134,7 +7549,7 @@ function usePdfViewer(multiSignerContext) {
6134
7549
  setOriginalPdfBytes(null);
6135
7550
  setViewerPdfBytes(null);
6136
7551
  }
6137
- }, []);
7552
+ }, [multiSignerContext]);
6138
7553
  const handleLoad = useCallback(() => {
6139
7554
  setIsLoading(false);
6140
7555
  setIsLoaded(true);
@@ -6170,52 +7585,15 @@ function usePdfViewer(multiSignerContext) {
6170
7585
  return await viewerRef.current.saveDocument();
6171
7586
  }, []);
6172
7587
  const extractFormFields = useCallback(
6173
- async (currentSignerEmail) => {
6174
- if (!originalPdfBytes) {
6175
- throw new Error("No PDF loaded");
6176
- }
6177
- try {
6178
- const fields = await extractVisibleFormFields(
6179
- originalPdfBytes,
6180
- currentSignerEmail
6181
- );
6182
- setExtractedFields(fields);
6183
- if (multiSignerContext?.isMultiSigner) {
6184
- const filteredBytes = await filterPdfForCurrentSigner(
6185
- originalPdfBytes,
6186
- fields,
6187
- multiSignerContext
6188
- );
6189
- setViewerPdfBytes(filteredBytes);
6190
- const filteredBlobUrl = createPdfBlobUrl(filteredBytes);
6191
- await viewerRef.current?.loadPdf(filteredBlobUrl);
6192
- } else {
6193
- const labeledBytes = await filterPdfForCurrentSigner(
6194
- originalPdfBytes,
6195
- fields,
6196
- multiSignerContext || {
6197
- isMultiSigner: false,
6198
- currentSigner: null,
6199
- currentSignerEmail: "",
6200
- isPrimarySigner: true,
6201
- isFinalSigner: true
6202
- }
6203
- );
6204
- setViewerPdfBytes(labeledBytes);
6205
- const labeledBlobUrl = createPdfBlobUrl(labeledBytes);
6206
- await viewerRef.current?.loadPdf(labeledBlobUrl);
6207
- }
6208
- if (viewerRef.current && fields.length > 0) {
6209
- await new Promise((resolve) => setTimeout(resolve, 100));
6210
- viewerRef.current.injectPlaceholders(fields);
6211
- }
6212
- return fields;
6213
- } catch (err) {
6214
- const errorMessage = err instanceof Error ? err.message : "Failed to extract form fields";
6215
- throw new Error(errorMessage);
7588
+ async (_currentSignerEmail) => {
7589
+ if (viewerRef.current && extractedFields.length > 0) {
7590
+ await new Promise((resolve) => setTimeout(resolve, 100));
7591
+ viewerRef.current.injectPlaceholders(extractedFields);
7592
+ viewerRef.current.injectRadioLabels(extractedFields);
6216
7593
  }
7594
+ return extractedFields;
6217
7595
  },
6218
- [originalPdfBytes, multiSignerContext]
7596
+ [extractedFields]
6219
7597
  );
6220
7598
  const fillPdf = useCallback(
6221
7599
  async (fieldValues, signatures, currentSignerEmail, metadata, auditTrail) => {
@@ -6358,6 +7736,20 @@ function usePdfViewer(multiSignerContext) {
6358
7736
  }
6359
7737
  function useFormFields(fields = []) {
6360
7738
  const [fieldValues, setFieldValues] = useState({});
7739
+ const seededFieldsRef = useRef(/* @__PURE__ */ new Set());
7740
+ useEffect(() => {
7741
+ if (fields.length === 0) return;
7742
+ const defaults = {};
7743
+ for (const field of fields) {
7744
+ if (field.defaultValue && field.defaultValue.trim() && !seededFieldsRef.current.has(field.id)) {
7745
+ defaults[field.id] = field.defaultValue;
7746
+ seededFieldsRef.current.add(field.id);
7747
+ }
7748
+ }
7749
+ if (Object.keys(defaults).length > 0) {
7750
+ setFieldValues((prev) => ({ ...defaults, ...prev }));
7751
+ }
7752
+ }, [fields]);
6361
7753
  const [errors, setErrors] = useState([]);
6362
7754
  const [touched, setTouched] = useState({});
6363
7755
  const updateField = useCallback((fieldId, value) => {
@@ -6397,14 +7789,14 @@ function useFormFields(fields = []) {
6397
7789
  if (!value || value.trim() === "") {
6398
7790
  newErrors.push({
6399
7791
  field: fieldId,
6400
- message: `${field.label || field.name} is required`
7792
+ message: `${getFieldDisplayName(field)} is required`
6401
7793
  });
6402
7794
  }
6403
7795
  }
6404
7796
  if (value && field.maxLength && value.length > field.maxLength) {
6405
7797
  newErrors.push({
6406
7798
  field: fieldId,
6407
- message: `${field.label || field.name} must be at most ${field.maxLength} characters`
7799
+ message: `${getFieldDisplayName(field)} must be at most ${field.maxLength} characters`
6408
7800
  });
6409
7801
  }
6410
7802
  if (value && field.type === "date") {
@@ -6430,13 +7822,13 @@ function useFormFields(fields = []) {
6430
7822
  if (!signatures[field.id]) {
6431
7823
  newErrors.push({
6432
7824
  field: field.id,
6433
- message: `${field.label || field.name} is required`
7825
+ message: `${getFieldDisplayName(field)} is required`
6434
7826
  });
6435
7827
  }
6436
7828
  } else if (!value2 || value2.trim() === "") {
6437
7829
  newErrors.push({
6438
7830
  field: field.id,
6439
- message: `${field.label || field.name} is required`
7831
+ message: `${getFieldDisplayName(field)} is required`
6440
7832
  });
6441
7833
  }
6442
7834
  }
@@ -6444,7 +7836,7 @@ function useFormFields(fields = []) {
6444
7836
  if (value && field.maxLength && value.length > field.maxLength) {
6445
7837
  newErrors.push({
6446
7838
  field: field.id,
6447
- message: `${field.label || field.name} must be at most ${field.maxLength} characters`
7839
+ message: `${getFieldDisplayName(field)} must be at most ${field.maxLength} characters`
6448
7840
  });
6449
7841
  }
6450
7842
  if (value && field.type === "date") {
@@ -6551,7 +7943,7 @@ function useSignatures() {
6551
7943
  for (const field of fields) {
6552
7944
  if (field.required && (field.type === "signature" || field.type === "initials")) {
6553
7945
  if (!signaturesToCheck[field.id]) {
6554
- errors.push(`${field.label || field.name} is required`);
7946
+ errors.push(`${getFieldDisplayName(field)} is required`);
6555
7947
  }
6556
7948
  }
6557
7949
  }
@@ -6669,6 +8061,13 @@ function useAttachments(options = {}) {
6669
8061
  errors.push(...validation.errors);
6670
8062
  continue;
6671
8063
  }
8064
+ const isDuplicate = [...attachments, ...newAttachments].some(
8065
+ (att) => att.name.toLowerCase() === file.name.toLowerCase()
8066
+ );
8067
+ if (isDuplicate) {
8068
+ errors.push(`"${file.name}" is already attached`);
8069
+ continue;
8070
+ }
6672
8071
  const currentTotalSize = [...attachments, ...newAttachments].reduce((sum, att) => sum + att.size, 0);
6673
8072
  if (currentTotalSize + file.size > constraints.maxTotalSize) {
6674
8073
  const maxTotalMB = (constraints.maxTotalSize / 1024 / 1024).toFixed(1);
@@ -6719,6 +8118,9 @@ function useAttachments(options = {}) {
6719
8118
  setAttachments([]);
6720
8119
  setValidationErrors([]);
6721
8120
  }, []);
8121
+ const clearValidationErrors = useCallback(() => {
8122
+ setValidationErrors([]);
8123
+ }, []);
6722
8124
  const getTotalSize = useCallback(() => {
6723
8125
  return attachments.reduce((sum, att) => sum + att.size, 0);
6724
8126
  }, [attachments]);
@@ -6750,6 +8152,7 @@ function useAttachments(options = {}) {
6750
8152
  addFiles,
6751
8153
  removeAttachment,
6752
8154
  clearAttachments,
8155
+ clearValidationErrors,
6753
8156
  // Utilities
6754
8157
  getTotalSize,
6755
8158
  formatSize,
@@ -6782,6 +8185,10 @@ function useAcknowledgements(fields) {
6782
8185
  const [acknowledgedMap, setAcknowledgedMap] = useState(
6783
8186
  /* @__PURE__ */ new Map()
6784
8187
  );
8188
+ const acknowledgedMapRef = useRef(acknowledgedMap);
8189
+ useEffect(() => {
8190
+ acknowledgedMapRef.current = acknowledgedMap;
8191
+ }, [acknowledgedMap]);
6785
8192
  const getFieldsWithAcknowledgements = useCallback((fieldsToFilter) => {
6786
8193
  return fieldsToFilter.filter(
6787
8194
  (field) => field.acknowledgements && field.acknowledgements.length > 0
@@ -6793,26 +8200,27 @@ function useAcknowledgements(fields) {
6793
8200
  const fieldAcks = newMap.get(fieldId) || /* @__PURE__ */ new Set();
6794
8201
  fieldAcks.add(ackId);
6795
8202
  newMap.set(fieldId, fieldAcks);
8203
+ acknowledgedMapRef.current = newMap;
6796
8204
  return newMap;
6797
8205
  });
6798
8206
  }, []);
6799
8207
  const isAcknowledged = useCallback((fieldId, ackId) => {
6800
- const fieldAcks = acknowledgedMap.get(fieldId);
8208
+ const fieldAcks = acknowledgedMapRef.current.get(fieldId);
6801
8209
  if (!fieldAcks) return false;
6802
8210
  if (ackId) {
6803
8211
  return fieldAcks.has(ackId);
6804
8212
  }
6805
8213
  return fieldAcks.size > 0;
6806
- }, [acknowledgedMap]);
8214
+ }, []);
6807
8215
  const isFieldFullyAcknowledged = useCallback((fieldId) => {
6808
8216
  const field = fields.find((f) => f.id === fieldId);
6809
8217
  if (!field || !field.acknowledgements || field.acknowledgements.length === 0) {
6810
8218
  return true;
6811
8219
  }
6812
- const fieldAcks = acknowledgedMap.get(fieldId);
8220
+ const fieldAcks = acknowledgedMapRef.current.get(fieldId);
6813
8221
  if (!fieldAcks) return false;
6814
8222
  return field.acknowledgements.every((ack) => fieldAcks.has(ack.id));
6815
- }, [fields, acknowledgedMap]);
8223
+ }, [fields]);
6816
8224
  const getFieldAcknowledgementProgress = useCallback((fieldId) => {
6817
8225
  const field = fields.find((f) => f.id === fieldId);
6818
8226
  if (!field || !field.acknowledgements || field.acknowledgements.length === 0) {
@@ -6867,7 +8275,7 @@ function useAcknowledgements(fields) {
6867
8275
  };
6868
8276
  }
6869
8277
  function useRequiredFieldNavigation(fields) {
6870
- const [currentRequiredIndex, setCurrentRequiredIndex] = useState(0);
8278
+ const [currentRequiredIndex, setCurrentRequiredIndex] = useState(-1);
6871
8279
  const requiredFields = useMemo(() => {
6872
8280
  return fields.filter((f) => {
6873
8281
  if (f.id === "signature_field_main" || f.id === "initials_field_main") {
@@ -6879,18 +8287,18 @@ function useRequiredFieldNavigation(fields) {
6879
8287
  const hasRequiredFields = requiredFields.length > 0;
6880
8288
  const goToNextRequired = useCallback(() => {
6881
8289
  if (!hasRequiredFields) return null;
6882
- const nextIndex = (currentRequiredIndex + 1) % requiredFields.length;
8290
+ const nextIndex = currentRequiredIndex === -1 ? 0 : (currentRequiredIndex + 1) % requiredFields.length;
6883
8291
  setCurrentRequiredIndex(nextIndex);
6884
8292
  return requiredFields[nextIndex] || null;
6885
8293
  }, [currentRequiredIndex, requiredFields, hasRequiredFields]);
6886
8294
  const goToPreviousRequired = useCallback(() => {
6887
8295
  if (!hasRequiredFields) return null;
6888
- const prevIndex = currentRequiredIndex === 0 ? requiredFields.length - 1 : currentRequiredIndex - 1;
8296
+ const prevIndex = currentRequiredIndex <= 0 ? requiredFields.length - 1 : currentRequiredIndex - 1;
6889
8297
  setCurrentRequiredIndex(prevIndex);
6890
8298
  return requiredFields[prevIndex] || null;
6891
8299
  }, [currentRequiredIndex, requiredFields, hasRequiredFields]);
6892
8300
  const currentRequiredField = useMemo(() => {
6893
- if (!hasRequiredFields) return null;
8301
+ if (!hasRequiredFields || currentRequiredIndex < 0) return null;
6894
8302
  return requiredFields[currentRequiredIndex] || null;
6895
8303
  }, [requiredFields, currentRequiredIndex, hasRequiredFields]);
6896
8304
  return {
@@ -6956,8 +8364,9 @@ function SubmissionForm({
6956
8364
  validateFields,
6957
8365
  getFieldError,
6958
8366
  isFieldTouched,
8367
+ touched,
6959
8368
  clearFields
6960
- } = useFormFields();
8369
+ } = useFormFields(extractedFields);
6961
8370
  const {
6962
8371
  signatures,
6963
8372
  setSignature: setSignatureOriginal,
@@ -6969,12 +8378,16 @@ function SubmissionForm({
6969
8378
  } = useSignatures();
6970
8379
  const signaturesRef = useRef(signatures);
6971
8380
  const fieldValuesRef = useRef(fieldValues);
8381
+ const touchedRef = useRef(touched);
6972
8382
  useEffect(() => {
6973
8383
  signaturesRef.current = signatures;
6974
8384
  }, [signatures]);
6975
8385
  useEffect(() => {
6976
8386
  fieldValuesRef.current = fieldValues;
6977
8387
  }, [fieldValues]);
8388
+ useEffect(() => {
8389
+ touchedRef.current = touched;
8390
+ }, [touched]);
6978
8391
  const formFields = customFormFields || extractedFields;
6979
8392
  const { filteredFields, isFieldVisible } = useFieldFiltering(formFields, multiSignerContext);
6980
8393
  const {
@@ -6995,7 +8408,15 @@ function SubmissionForm({
6995
8408
  } = useRequiredFieldNavigation(filteredFields);
6996
8409
  const [acknowledgementModalOpen, setAcknowledgementModalOpen] = useState(false);
6997
8410
  const [currentAcknowledgementField, setCurrentAcknowledgementField] = useState(null);
8411
+ const [currentAcknowledgementFieldName, setCurrentAcknowledgementFieldName] = useState(null);
8412
+ const currentAcknowledgementElementRef = useRef(null);
8413
+ const acknowledgementTriggerSourceRef = useRef(null);
8414
+ const acknowledgementCleanupTimeoutRef = useRef(null);
6998
8415
  const [unacknowledgedModalOpen, setUnacknowledgedModalOpen] = useState(false);
8416
+ const recentlyAcknowledgedFieldsRef = useRef(/* @__PURE__ */ new Map());
8417
+ const [dateCalendarOpen, setDateCalendarOpen] = useState(false);
8418
+ const [currentDateField, setCurrentDateField] = useState(null);
8419
+ const [dateFieldPosition, setDateFieldPosition] = useState(null);
6999
8420
  const realPdfFieldsByType = useMemo(() => {
7000
8421
  return {
7001
8422
  signature: filteredFields.filter(
@@ -7006,6 +8427,15 @@ function SubmissionForm({
7006
8427
  )
7007
8428
  };
7008
8429
  }, [filteredFields]);
8430
+ const hasInitialsFields = useMemo(() => {
8431
+ return filteredFields.some((f) => f.id === "initials_field_main" || f.type === "initials");
8432
+ }, [filteredFields]);
8433
+ const defaultSigningMessage = useMemo(() => {
8434
+ if (hasInitialsFields) {
8435
+ return "Fill out the form fields directly in the PDF document. Your signature and initials below will be applied to the document automatically.";
8436
+ }
8437
+ return "Fill out the form fields directly in the PDF document. Your signature below will be applied to the document automatically.";
8438
+ }, [hasInitialsFields]);
7009
8439
  const getPdfFieldNamesForIndicators = useCallback((fieldType) => {
7010
8440
  const realPdfFields = realPdfFieldsByType[fieldType];
7011
8441
  console.log(`[SubmissionForm] Total filteredFields count:`, filteredFields.length);
@@ -7042,6 +8472,15 @@ function SubmissionForm({
7042
8472
  return result;
7043
8473
  }, [realPdfFieldsByType, filteredFields, isFieldFullyAcknowledged]);
7044
8474
  const indicatorUpdateTimers = useRef(/* @__PURE__ */ new Map());
8475
+ useEffect(() => {
8476
+ return () => {
8477
+ if (acknowledgementCleanupTimeoutRef.current) {
8478
+ clearTimeout(acknowledgementCleanupTimeoutRef.current);
8479
+ }
8480
+ indicatorUpdateTimers.current.forEach((timer) => clearTimeout(timer));
8481
+ indicatorUpdateTimers.current.clear();
8482
+ };
8483
+ }, []);
7045
8484
  const updateFieldIndicator = useCallback((fieldId) => {
7046
8485
  const existingTimer = indicatorUpdateTimers.current.get(fieldId);
7047
8486
  if (existingTimer) {
@@ -7088,11 +8527,11 @@ function SubmissionForm({
7088
8527
  }
7089
8528
  let hasValue;
7090
8529
  if (field.type === "signature" && fieldId !== "signature_field_main") {
7091
- hasValue = hasSignature("signature_field_main");
8530
+ hasValue = !!signaturesRef.current["signature_field_main"];
7092
8531
  } else if (field.type === "initials" && fieldId !== "initials_field_main") {
7093
- hasValue = !!(fieldValues["initials_field_main"] && String(fieldValues["initials_field_main"]).trim() !== "");
8532
+ hasValue = !!(fieldValuesRef.current["initials_field_main"] && String(fieldValuesRef.current["initials_field_main"]).trim() !== "");
7094
8533
  } else {
7095
- hasValue = field.type === "signature" ? hasSignature(field.id) : !!(fieldValues[field.id] && String(fieldValues[field.id]).trim() !== "");
8534
+ hasValue = field.type === "signature" ? !!signaturesRef.current[field.id] : !!(fieldValuesRef.current[field.id] && String(fieldValuesRef.current[field.id]).trim() !== "");
7096
8535
  }
7097
8536
  const hasAcknowledgements = field.acknowledgements && field.acknowledgements.length > 0;
7098
8537
  if (!hasValue && !hasAcknowledgements) {
@@ -7143,24 +8582,38 @@ function SubmissionForm({
7143
8582
  } catch (error) {
7144
8583
  logger.error(`[FIELD INDICATOR] Failed to add ${indicatorType} indicator for ${field.id}:`, error);
7145
8584
  }
7146
- indicatorUpdateTimers.current.delete(fieldId);
7147
- }, 200);
8585
+ }, 50);
7148
8586
  indicatorUpdateTimers.current.set(fieldId, timer);
7149
- }, [filteredFields, fieldValues, hasSignature, isFieldFullyAcknowledged, viewerRef, getPdfFieldNamesForIndicators]);
8587
+ }, [filteredFields, isFieldFullyAcknowledged, viewerRef, getPdfFieldNamesForIndicators]);
8588
+ const updateFieldIndicatorRef = useRef(updateFieldIndicator);
8589
+ useEffect(() => {
8590
+ updateFieldIndicatorRef.current = updateFieldIndicator;
8591
+ }, [updateFieldIndicator]);
7150
8592
  const acknowledgeItem = useCallback((fieldId, ackId) => {
7151
8593
  acknowledgeItemOriginal(fieldId, ackId);
7152
- updateFieldIndicator(fieldId);
7153
- }, [acknowledgeItemOriginal, updateFieldIndicator]);
8594
+ updateFieldIndicatorRef.current(fieldId);
8595
+ }, [acknowledgeItemOriginal]);
7154
8596
  const setSignature = useCallback((fieldId, dataUrl) => {
7155
8597
  setSignatureOriginal(fieldId, dataUrl);
7156
- updateFieldIndicator(fieldId);
7157
- }, [setSignatureOriginal, updateFieldIndicator]);
8598
+ signaturesRef.current = {
8599
+ ...signaturesRef.current,
8600
+ [fieldId]: dataUrl
8601
+ };
8602
+ updateFieldIndicatorRef.current(fieldId);
8603
+ if (dataUrl.startsWith("data:")) {
8604
+ viewerRef.current?.previewSignature(fieldId, dataUrl);
8605
+ } else {
8606
+ viewerRef.current?.previewInitials(fieldId, dataUrl);
8607
+ }
8608
+ }, [setSignatureOriginal]);
7158
8609
  const {
7159
8610
  attachments,
7160
8611
  isUploading: isUploadingAttachments,
7161
8612
  validationErrors: attachmentErrors,
8613
+ constraints: attachmentConstraints,
7162
8614
  addFiles,
7163
8615
  removeAttachment,
8616
+ clearValidationErrors: clearAttachmentErrors,
7164
8617
  formatSize,
7165
8618
  validateAll: validateAttachments
7166
8619
  } = useAttachments({
@@ -7183,17 +8636,17 @@ function SubmissionForm({
7183
8636
  const formFieldsViewRef = useRef(null);
7184
8637
  useEffect(() => {
7185
8638
  if (pdfUrl) {
7186
- loadPdf(pdfUrl);
8639
+ const signerEmailForExtraction = multiSignerContext.isMultiSigner ? effectiveSignerEmail : void 0;
8640
+ loadPdf(pdfUrl, signerEmailForExtraction);
7187
8641
  }
7188
- }, [pdfUrl, loadPdf]);
8642
+ }, [pdfUrl, loadPdf, multiSignerContext.isMultiSigner, effectiveSignerEmail]);
7189
8643
  useEffect(() => {
7190
8644
  if (isPdfLoaded && !customFormFields) {
7191
- const signerEmailForExtraction = multiSignerContext.isMultiSigner ? effectiveSignerEmail : void 0;
7192
- extractFormFields(signerEmailForExtraction).catch((error) => {
7193
- logger.error("Failed to extract form fields:", error);
8645
+ extractFormFields().catch((error) => {
8646
+ logger.error("Failed to inject placeholders:", error);
7194
8647
  });
7195
8648
  }
7196
- }, [isPdfLoaded, effectiveSignerEmail, extractFormFields, customFormFields, multiSignerContext.isMultiSigner]);
8649
+ }, [isPdfLoaded, extractFormFields, customFormFields]);
7197
8650
  useEffect(() => {
7198
8651
  if (!isPdfLoaded || !viewerRef.current) {
7199
8652
  return;
@@ -7208,7 +8661,7 @@ function SubmissionForm({
7208
8661
  });
7209
8662
  }, 700);
7210
8663
  return () => clearTimeout(timeoutId);
7211
- }, [isPdfLoaded, filteredFields, updateFieldIndicator, viewerRef]);
8664
+ }, [isPdfLoaded, filteredFields, viewerRef]);
7212
8665
  useEffect(() => {
7213
8666
  if (!isPdfLoaded || !viewerRef.current) {
7214
8667
  return;
@@ -7217,68 +8670,49 @@ function SubmissionForm({
7217
8670
  if (signatures["signature_field_main"]) {
7218
8671
  updateFieldIndicator("signature_field_main");
7219
8672
  }
7220
- if (fieldValues["initials_field_main"]) {
8673
+ if (signatures["initials_field_main"] || fieldValues["initials_field_main"]) {
7221
8674
  updateFieldIndicator("initials_field_main");
7222
8675
  }
7223
8676
  }, 250);
7224
8677
  return () => clearTimeout(timeoutId);
7225
- }, [signatures, fieldValues, updateFieldIndicator, isPdfLoaded, viewerRef]);
8678
+ }, [signatures, isPdfLoaded, viewerRef]);
7226
8679
  useEffect(() => {
7227
- if (!isPdfLoaded || !viewerRef.current || viewMode !== "pdf") {
7228
- return;
7229
- }
8680
+ if (!isPdfLoaded || !viewerRef.current) return;
7230
8681
  const iframe = document.querySelector("iframe");
7231
- if (!iframe?.contentDocument) {
7232
- return;
7233
- }
7234
- const doc = iframe.contentDocument;
7235
- const handlePdfFieldChange = async (event) => {
7236
- const target = event.target;
7237
- const fieldName = target.getAttribute("name") || target.getAttribute("data-element-id") || "";
7238
- if (!fieldName) return;
7239
- let field = filteredFields.find((f) => f.fieldId === fieldName);
7240
- if (!field) {
7241
- field = filteredFields.find((f) => f.id === fieldName);
7242
- }
7243
- if (!field) {
7244
- field = filteredFields.find((f) => f.name === fieldName);
7245
- }
7246
- if (field) {
7247
- let value = "";
7248
- if (target.tagName === "INPUT") {
7249
- const inputElement = target;
7250
- if (inputElement.type === "checkbox") {
7251
- value = inputElement.checked ? "true" : "false";
7252
- } else if (inputElement.type === "radio") {
7253
- if (inputElement.checked) {
7254
- value = inputElement.value || "true";
7255
- }
7256
- } else {
7257
- value = inputElement.value;
7258
- }
7259
- } else if (target.tagName === "SELECT") {
7260
- value = target.value;
7261
- } else if (target.tagName === "TEXTAREA") {
7262
- value = target.value;
8682
+ if (!iframe?.contentDocument) return;
8683
+ const reapplyPreviews = () => {
8684
+ const doc = iframe.contentDocument;
8685
+ if (!doc) return;
8686
+ const mainSig = signaturesRef.current["signature_field_main"];
8687
+ if (mainSig) {
8688
+ const existingSigPreviews = doc.querySelectorAll(".signiphi-signature-preview");
8689
+ if (existingSigPreviews.length === 0) {
8690
+ viewerRef.current?.previewSignature("signature_field_main", mainSig);
8691
+ }
8692
+ }
8693
+ const mainInitials = fieldValuesRef.current["initials_field_main"];
8694
+ if (mainInitials) {
8695
+ const existingInitialsPreviews = doc.querySelectorAll(".signiphi-initials-preview");
8696
+ if (existingInitialsPreviews.length === 0) {
8697
+ viewerRef.current?.previewInitials("initials_field_main", mainInitials);
7263
8698
  }
7264
- setFieldValue(field.id, value);
7265
- logger.info(`[PDF FIELD CHANGE] Field ${field.id} changed to: "${value}"`);
7266
- updateFieldIndicator(field.id);
7267
8699
  }
7268
8700
  };
7269
- const formFields2 = doc.querySelectorAll("input, select, textarea");
7270
- formFields2.forEach((field) => {
7271
- field.addEventListener("change", handlePdfFieldChange);
7272
- field.addEventListener("input", handlePdfFieldChange);
8701
+ let debounceTimeout;
8702
+ const debouncedReapply = () => {
8703
+ clearTimeout(debounceTimeout);
8704
+ debounceTimeout = setTimeout(reapplyPreviews, 150);
8705
+ };
8706
+ const observer = new MutationObserver(debouncedReapply);
8707
+ const annotationLayers = iframe.contentDocument.querySelectorAll(".annotationLayer");
8708
+ annotationLayers.forEach((layer) => {
8709
+ observer.observe(layer, { childList: true, subtree: false });
7273
8710
  });
7274
- logger.info("[PDF FIELD CHANGE] Attached change listeners to", formFields2.length, "PDF form fields");
7275
8711
  return () => {
7276
- formFields2.forEach((field) => {
7277
- field.removeEventListener("change", handlePdfFieldChange);
7278
- field.removeEventListener("input", handlePdfFieldChange);
7279
- });
8712
+ observer.disconnect();
8713
+ clearTimeout(debounceTimeout);
7280
8714
  };
7281
- }, [isPdfLoaded, viewMode, filteredFields, setFieldValue, updateFieldIndicator]);
8715
+ }, [isPdfLoaded]);
7282
8716
  useEffect(() => {
7283
8717
  const fieldsWithAcks = filteredFields.filter((f) => f.acknowledgements && f.acknowledgements.length > 0);
7284
8718
  if (fieldsWithAcks.length > 0) {
@@ -7302,24 +8736,33 @@ function SubmissionForm({
7302
8736
  })));
7303
8737
  }, [filteredFields]);
7304
8738
  useEffect(() => {
7305
- if (!isPdfLoaded || !viewerRef.current?.attachFieldClickInterceptors) {
8739
+ if (isPdfLoaded && viewerRef.current?.setFieldMetadata && filteredFields.length > 0) {
8740
+ viewerRef.current.setFieldMetadata(filteredFields);
8741
+ logger.info("[FIELD METADATA] Set field metadata for PDF viewer:", filteredFields.length, "fields");
8742
+ }
8743
+ }, [isPdfLoaded, filteredFields]);
8744
+ useEffect(() => {
8745
+ if (!isPdfLoaded || !viewerRef.current?.attachFieldClickInterceptors || !viewerRef.current?.attachFieldChangeListeners) {
7306
8746
  return;
7307
8747
  }
7308
- const timeoutId = setTimeout(() => {
7309
- viewerRef.current?.attachFieldClickInterceptors((fieldName) => {
8748
+ let changeListenerCleanup = null;
8749
+ const timeoutId = setTimeout(async () => {
8750
+ changeListenerCleanup = await viewerRef.current?.attachFieldChangeListeners((fieldName, value) => {
8751
+ const field = resolveField(filteredFields, fieldName);
8752
+ if (field) {
8753
+ setFieldValue(field.id, value);
8754
+ fieldValuesRef.current = {
8755
+ ...fieldValuesRef.current,
8756
+ [field.id]: value
8757
+ };
8758
+ logger.info(`[PDF FIELD CHANGE] Field ${field.id} changed to: "${value}"`);
8759
+ updateFieldIndicatorRef.current(field.id);
8760
+ }
8761
+ }) || null;
8762
+ await viewerRef.current?.attachFieldClickInterceptors((fieldName, element) => {
7310
8763
  logger.info(`[PDF Click] Checking field: ${fieldName}`);
7311
8764
  logger.info(`[PDF Click] Available fields:`, filteredFields.map((f) => ({ id: f.id, fieldId: f.fieldId, name: f.name, acks: f.acknowledgements?.length || 0 })));
7312
- let field = filteredFields.find((f) => f.fieldId === fieldName);
7313
- if (!field) {
7314
- field = filteredFields.find((f) => f.id === fieldName);
7315
- }
7316
- if (!field) {
7317
- field = filteredFields.find((f) => f.name === fieldName);
7318
- }
7319
- if (!field) {
7320
- const cleanFieldName = fieldName.replace(/_signature$|_initials$|_date$/i, "");
7321
- field = filteredFields.find((f) => f.fieldId === cleanFieldName || f.id === cleanFieldName || f.name === cleanFieldName);
7322
- }
8765
+ const field = resolveField(filteredFields, fieldName);
7323
8766
  if (!field) {
7324
8767
  logger.info(`[PDF Click] Field not found: ${fieldName}`);
7325
8768
  return true;
@@ -7334,6 +8777,7 @@ function SubmissionForm({
7334
8777
  if (!isAcked) {
7335
8778
  logger.info(`[PDF Click] Blocking ${field.type} field and showing acknowledgement modal`);
7336
8779
  setCurrentAcknowledgementField(field);
8780
+ setCurrentAcknowledgementFieldName(fieldName);
7337
8781
  setAcknowledgementModalOpen(true);
7338
8782
  return false;
7339
8783
  }
@@ -7348,6 +8792,10 @@ function SubmissionForm({
7348
8792
  setSignature(field.id, collected);
7349
8793
  } else if (field.type === "initials") {
7350
8794
  setFieldValue(field.id, collected);
8795
+ fieldValuesRef.current = {
8796
+ ...fieldValuesRef.current,
8797
+ [field.id]: collected
8798
+ };
7351
8799
  }
7352
8800
  updateFieldIndicator(field.id);
7353
8801
  }
@@ -7376,15 +8824,27 @@ function SubmissionForm({
7376
8824
  return false;
7377
8825
  }
7378
8826
  let shouldBlock = false;
7379
- if (!field.acknowledgements || field.acknowledgements.length === 0) {
7380
- logger.info(`[PDF Click] Field has no acknowledgements`);
8827
+ const wasRecentlyAcknowledged = recentlyAcknowledgedFieldsRef.current.has(field.id);
8828
+ if (wasRecentlyAcknowledged) {
8829
+ logger.info(`[PDF Click] Field was recently acknowledged, skipping check`);
7381
8830
  shouldBlock = false;
7382
8831
  } else {
7383
- const isAcked = isFieldFullyAcknowledged(field.id);
7384
- logger.info(`[PDF Click] Field is fully acknowledged: ${isAcked}`);
7385
- shouldBlock = !isAcked;
8832
+ if (!field.acknowledgements || field.acknowledgements.length === 0) {
8833
+ logger.info(`[PDF Click] Field has no acknowledgements`);
8834
+ shouldBlock = false;
8835
+ } else {
8836
+ const isAcked = isFieldFullyAcknowledged(field.id);
8837
+ logger.info(`[PDF Click] Field is fully acknowledged: ${isAcked}`);
8838
+ shouldBlock = !isAcked;
8839
+ }
7386
8840
  }
7387
- if (!shouldBlock) {
8841
+ if (shouldBlock) {
8842
+ logger.info(`[PDF Click] Blocking field and showing modal`);
8843
+ setCurrentAcknowledgementField(field);
8844
+ setCurrentAcknowledgementFieldName(fieldName);
8845
+ currentAcknowledgementElementRef.current = element || null;
8846
+ acknowledgementTriggerSourceRef.current = "pdf-click";
8847
+ setAcknowledgementModalOpen(true);
7388
8848
  try {
7389
8849
  const iframe = document.querySelector("iframe");
7390
8850
  if (iframe?.contentDocument) {
@@ -7392,23 +8852,140 @@ function SubmissionForm({
7392
8852
  navigationHighlights.forEach((el) => el.classList.remove("active-required-field"));
7393
8853
  const navigationAnnotationHighlights = iframe.contentDocument.querySelectorAll(".active-required-field-annotation");
7394
8854
  navigationAnnotationHighlights.forEach((el) => el.classList.remove("active-required-field-annotation"));
7395
- logger.info("[PDF Click] Cleared navigation highlights (interaction allowed)");
8855
+ logger.info("[PDF Click] Cleared navigation highlights (blocked)");
7396
8856
  }
7397
8857
  } catch (error) {
7398
8858
  logger.warn("[PDF Click] Failed to clear navigation highlights:", error);
7399
8859
  }
8860
+ return false;
7400
8861
  }
7401
- if (shouldBlock) {
7402
- logger.info(`[PDF Click] Blocking field and showing modal`);
7403
- setCurrentAcknowledgementField(field);
7404
- setAcknowledgementModalOpen(true);
8862
+ if (field.type === "date") {
8863
+ logger.info(`[PDF Click] Opening calendar for date field: ${field.id}`);
8864
+ try {
8865
+ const iframe = document.querySelector("iframe");
8866
+ if (iframe?.contentDocument) {
8867
+ const formElement = iframe.contentDocument.querySelector(
8868
+ `input[name="${fieldName}"], input[data-element-id="${fieldName}"]`
8869
+ );
8870
+ if (formElement) {
8871
+ const rect = formElement.getBoundingClientRect();
8872
+ const iframeRect = iframe.getBoundingClientRect();
8873
+ const position = {
8874
+ x: Math.round(iframeRect.left + rect.left),
8875
+ y: Math.round(iframeRect.top + rect.top),
8876
+ width: Math.round(rect.width),
8877
+ height: Math.round(rect.height)
8878
+ };
8879
+ setCurrentDateField(field);
8880
+ setDateFieldPosition(position);
8881
+ setDateCalendarOpen(true);
8882
+ }
8883
+ }
8884
+ } catch (error) {
8885
+ logger.error("[PDF Click] Failed to get field position:", error);
8886
+ }
8887
+ try {
8888
+ const iframe = document.querySelector("iframe");
8889
+ if (iframe?.contentDocument) {
8890
+ const navigationHighlights = iframe.contentDocument.querySelectorAll(".active-required-field");
8891
+ navigationHighlights.forEach((el) => el.classList.remove("active-required-field"));
8892
+ const navigationAnnotationHighlights = iframe.contentDocument.querySelectorAll(".active-required-field-annotation");
8893
+ navigationAnnotationHighlights.forEach((el) => el.classList.remove("active-required-field-annotation"));
8894
+ logger.info("[PDF Click] Cleared navigation highlights (calendar)");
8895
+ }
8896
+ } catch (error) {
8897
+ logger.warn("[PDF Click] Failed to clear navigation highlights:", error);
8898
+ }
7405
8899
  return false;
7406
8900
  }
8901
+ if (!shouldBlock) {
8902
+ try {
8903
+ const iframe = document.querySelector("iframe");
8904
+ if (iframe?.contentDocument) {
8905
+ const navigationHighlights = iframe.contentDocument.querySelectorAll(".active-required-field");
8906
+ navigationHighlights.forEach((el) => el.classList.remove("active-required-field"));
8907
+ const navigationAnnotationHighlights = iframe.contentDocument.querySelectorAll(".active-required-field-annotation");
8908
+ navigationAnnotationHighlights.forEach((el) => el.classList.remove("active-required-field-annotation"));
8909
+ logger.info("[PDF Click] Cleared navigation highlights (interaction allowed)");
8910
+ }
8911
+ } catch (error) {
8912
+ logger.warn("[PDF Click] Failed to clear navigation highlights:", error);
8913
+ }
8914
+ }
7407
8915
  return true;
7408
8916
  });
7409
8917
  }, 500);
7410
- return () => clearTimeout(timeoutId);
8918
+ return () => {
8919
+ clearTimeout(timeoutId);
8920
+ if (changeListenerCleanup) {
8921
+ changeListenerCleanup();
8922
+ }
8923
+ };
7411
8924
  }, [isPdfLoaded, filteredFields, isFieldFullyAcknowledged, hasCollectedSignature, getCollectedSignature, setSignature, setFieldValue, updateFieldIndicator]);
8925
+ useEffect(() => {
8926
+ if (!isPdfLoaded || !viewerRef.current?.addDateFieldIndicator) {
8927
+ return;
8928
+ }
8929
+ const timeoutId = setTimeout(() => {
8930
+ const dateFields = filteredFields.filter((f) => f.type === "date");
8931
+ logger.info(`[DATE INDICATORS] Adding calendar icons to ${dateFields.length} date fields`);
8932
+ dateFields.forEach((field) => {
8933
+ try {
8934
+ viewerRef.current?.addDateFieldIndicator(field.name);
8935
+ logger.info(`[DATE INDICATORS] Added calendar icon to: ${field.name}`);
8936
+ } catch (error) {
8937
+ logger.error(`[DATE INDICATORS] Failed to add calendar icon to ${field.name}:`, error);
8938
+ }
8939
+ });
8940
+ const iframe = document.querySelector("iframe");
8941
+ if (iframe?.contentDocument) {
8942
+ const calendarIndicators = iframe.contentDocument.querySelectorAll(".signiphi-date-field-indicator");
8943
+ calendarIndicators.forEach((indicator) => {
8944
+ indicator.addEventListener("click", (event) => {
8945
+ event.stopPropagation();
8946
+ event.preventDefault();
8947
+ const annotationSection = indicator.closest("section");
8948
+ if (annotationSection) {
8949
+ const formElement = annotationSection.querySelector("input, textarea");
8950
+ if (formElement) {
8951
+ const fieldName = formElement.getAttribute("name") || formElement.getAttribute("data-element-id") || "";
8952
+ let field = filteredFields.find((f) => f.fieldId === fieldName || f.id === fieldName || f.name === fieldName);
8953
+ if (!field) {
8954
+ const cleanFieldName = fieldName.replace(/_date$/i, "");
8955
+ field = filteredFields.find((f) => f.fieldId === cleanFieldName || f.id === cleanFieldName || f.name === cleanFieldName);
8956
+ }
8957
+ if (field && field.type === "date") {
8958
+ logger.info(`[DATE INDICATOR CLICK] Opening calendar for: ${field.id}`);
8959
+ const hasAcknowledgements = field.acknowledgements && field.acknowledgements.length > 0;
8960
+ if (hasAcknowledgements && !isFieldFullyAcknowledged(field.id)) {
8961
+ logger.info(`[DATE INDICATOR CLICK] Showing acknowledgement modal first`);
8962
+ setCurrentAcknowledgementField(field);
8963
+ setCurrentAcknowledgementFieldName(fieldName);
8964
+ setAcknowledgementModalOpen(true);
8965
+ return;
8966
+ }
8967
+ const rect = formElement.getBoundingClientRect();
8968
+ const iframeRect = iframe.getBoundingClientRect();
8969
+ const position = {
8970
+ x: Math.round(iframeRect.left + rect.left),
8971
+ y: Math.round(iframeRect.top + rect.top),
8972
+ width: Math.round(rect.width),
8973
+ height: Math.round(rect.height)
8974
+ };
8975
+ logger.info("[DATE INDICATOR CLICK] Opening calendar at position:", position);
8976
+ setCurrentDateField(field);
8977
+ setDateFieldPosition(position);
8978
+ setDateCalendarOpen(true);
8979
+ }
8980
+ }
8981
+ }
8982
+ });
8983
+ });
8984
+ logger.info(`[DATE INDICATORS] Attached click handlers to ${calendarIndicators.length} calendar icons`);
8985
+ }
8986
+ }, 600);
8987
+ return () => clearTimeout(timeoutId);
8988
+ }, [isPdfLoaded, filteredFields, isFieldFullyAcknowledged]);
7412
8989
  const prevViewModeRef = useRef(viewMode);
7413
8990
  useEffect(() => {
7414
8991
  const syncFieldsOnViewChange = async () => {
@@ -7448,6 +9025,9 @@ function SubmissionForm({
7448
9025
  }
7449
9026
  }
7450
9027
  }
9028
+ if (field.type === "radio" && (normalizedPdfValue === "true" || normalizedPdfValue === "false")) {
9029
+ normalizedPdfValue = void 0;
9030
+ }
7451
9031
  if (field.type === "date" && pdfValue) {
7452
9032
  const validation = parseAndValidateDate(pdfValue);
7453
9033
  if (!validation.isValid) {
@@ -7461,8 +9041,12 @@ function SubmissionForm({
7461
9041
  normalizedPdfValue = validation.isoString;
7462
9042
  }
7463
9043
  }
7464
- if (normalizedPdfValue && normalizedPdfValue !== currentReactValue) {
9044
+ const isUnresolvedRadioIndex = field.type === "radio" && normalizedPdfValue?.includes("__RADIO_OPTION_INDEX_");
9045
+ const pdfFieldReported = field.id in pdfValues;
9046
+ if (normalizedPdfValue && normalizedPdfValue !== currentReactValue && !isUnresolvedRadioIndex) {
7465
9047
  setFieldValue(field.id, normalizedPdfValue);
9048
+ } else if (pdfFieldReported && !normalizedPdfValue && currentReactValue) {
9049
+ setFieldValue(field.id, "");
7466
9050
  }
7467
9051
  }
7468
9052
  if (invalidDates.length > 0) {
@@ -7529,14 +9113,76 @@ function SubmissionForm({
7529
9113
  }
7530
9114
  }
7531
9115
  updateFieldIndicator(fieldId);
9116
+ if (field && field.type === "initials") {
9117
+ viewerRef.current?.previewInitials("initials_field_main", value || null);
9118
+ }
7532
9119
  },
7533
9120
  [setFieldValue, filteredFields, updateFieldIndicator]
7534
9121
  );
7535
- const handleAcknowledgementRequired = useCallback((field) => {
9122
+ const handleAcknowledgementRequired = useCallback((field, element, fieldName) => {
7536
9123
  if (readOnly) return;
7537
9124
  setCurrentAcknowledgementField(field);
9125
+ if (element) {
9126
+ currentAcknowledgementElementRef.current = element;
9127
+ logger.info(`[ACK REQUIRED] Stored element reference for field ${field.id}`, {
9128
+ elementTag: element.tagName,
9129
+ dataElementId: element.getAttribute("data-element-id")
9130
+ });
9131
+ }
9132
+ if (fieldName) {
9133
+ setCurrentAcknowledgementFieldName(fieldName);
9134
+ logger.info(`[ACK REQUIRED] Stored fieldName: ${fieldName} for field ${field.id}`);
9135
+ }
7538
9136
  setAcknowledgementModalOpen(true);
7539
9137
  }, [readOnly]);
9138
+ const handleAcknowledgementModalOpenChange = useCallback((open) => {
9139
+ setAcknowledgementModalOpen(open);
9140
+ if (acknowledgementCleanupTimeoutRef.current) {
9141
+ clearTimeout(acknowledgementCleanupTimeoutRef.current);
9142
+ acknowledgementCleanupTimeoutRef.current = null;
9143
+ }
9144
+ if (!open) {
9145
+ acknowledgementCleanupTimeoutRef.current = setTimeout(() => {
9146
+ if (acknowledgementTriggerSourceRef.current !== null) {
9147
+ acknowledgementTriggerSourceRef.current = null;
9148
+ currentAcknowledgementElementRef.current = null;
9149
+ logger.info("[ACK MODAL] Cleaned up refs after modal dismiss (not completed)");
9150
+ }
9151
+ acknowledgementCleanupTimeoutRef.current = null;
9152
+ }, 500);
9153
+ }
9154
+ }, []);
9155
+ const handleDateSelect = useCallback(async (dateValue) => {
9156
+ if (readOnly || !currentDateField) return;
9157
+ logger.info(`[DATE SELECT] Selected date for field ${currentDateField.id}:`, dateValue);
9158
+ setFieldValue(currentDateField.id, dateValue);
9159
+ try {
9160
+ await setFormFieldValues({ [currentDateField.id]: dateValue });
9161
+ const pdfApp = viewerRef.current?.getPDFViewerApplication?.();
9162
+ const pdfDocument = pdfApp?.pdfDocument;
9163
+ if (pdfDocument?.annotationStorage && typeof pdfDocument.getFieldObjects === "function") {
9164
+ const fieldObjects = await pdfDocument.getFieldObjects() || {};
9165
+ const widgets = fieldObjects[currentDateField.id];
9166
+ if (widgets?.[0]?.id) {
9167
+ pdfDocument.annotationStorage.setValue(widgets[0].id, { value: dateValue });
9168
+ }
9169
+ }
9170
+ logger.info(`[DATE SELECT] Updated PDF field ${currentDateField.id}`);
9171
+ } catch (error) {
9172
+ logger.error(`[DATE SELECT] Failed to update PDF field:`, error);
9173
+ }
9174
+ if (dateValue) {
9175
+ const validation = parseAndValidateDate(dateValue);
9176
+ if (validation.isValid) {
9177
+ setValidationErrors(
9178
+ (prev) => prev.filter(
9179
+ (error) => !error.includes("date field(s) contain invalid values") || !error.includes(currentDateField.label || currentDateField.id)
9180
+ )
9181
+ );
9182
+ }
9183
+ }
9184
+ updateFieldIndicator(currentDateField.id);
9185
+ }, [readOnly, currentDateField, setFieldValue, setFormFieldValues, updateFieldIndicator]);
7540
9186
  const handleSignatureClick = useCallback((field) => {
7541
9187
  if (readOnly) return;
7542
9188
  logger.info(`[SIGNATURE CLICK] Field:`, {
@@ -7552,6 +9198,10 @@ function SubmissionForm({
7552
9198
  setSignature(field.id, collected);
7553
9199
  } else if (field.type === "initials") {
7554
9200
  setFieldValue(field.id, collected);
9201
+ fieldValuesRef.current = {
9202
+ ...fieldValuesRef.current,
9203
+ [field.id]: collected
9204
+ };
7555
9205
  }
7556
9206
  logger.info(`[SIGNATURE CLICK] Auto-placed ${fieldType} for field ${field.id}`);
7557
9207
  updateFieldIndicator(field.id);
@@ -7593,6 +9243,7 @@ function SubmissionForm({
7593
9243
  }
7594
9244
  }
7595
9245
  let highlightedFieldElement = null;
9246
+ let pdfFieldName = field.name;
7596
9247
  try {
7597
9248
  const iframe = document.querySelector("iframe");
7598
9249
  if (iframe?.contentDocument) {
@@ -7653,7 +9304,6 @@ function SubmissionForm({
7653
9304
  iframe.contentDocument.head.appendChild(style);
7654
9305
  logger.info("[REQUIRED NAV] Added CSS styles");
7655
9306
  }
7656
- let pdfFieldName = field.name;
7657
9307
  const pdfApp = viewerRef.current?.getPDFViewerApplication?.();
7658
9308
  if (pdfApp?.pdfDocument?.getData) {
7659
9309
  try {
@@ -7774,7 +9424,8 @@ function SubmissionForm({
7774
9424
  if (field.acknowledgements && field.acknowledgements.length > 0) {
7775
9425
  if (!isFieldFullyAcknowledged(field.id)) {
7776
9426
  logger.info(`[REQUIRED NAV] Field has unacknowledged items, opening acknowledgement modal`);
7777
- handleAcknowledgementRequired(field);
9427
+ acknowledgementTriggerSourceRef.current = "navigation";
9428
+ handleAcknowledgementRequired(field, highlightedFieldElement || void 0, pdfFieldName);
7778
9429
  return;
7779
9430
  }
7780
9431
  }
@@ -7828,13 +9479,25 @@ function SubmissionForm({
7828
9479
  setFieldValue(currentInitialsField.id, initialsText);
7829
9480
  collectSignature("initials", initialsText);
7830
9481
  setFieldValue("initials_field_main", initialsText);
9482
+ fieldValuesRef.current = {
9483
+ ...fieldValuesRef.current,
9484
+ [currentInitialsField.id]: initialsText,
9485
+ "initials_field_main": initialsText
9486
+ };
9487
+ const fieldIdToUpdate = currentInitialsField.id;
7831
9488
  setCurrentInitialsField(null);
7832
9489
  setInitialsModalOpen(false);
9490
+ updateFieldIndicator(fieldIdToUpdate);
9491
+ viewerRef.current?.previewInitials("initials_field_main", initialsText || null);
7833
9492
  },
7834
- [currentInitialsField, setFieldValue, collectSignature]
9493
+ [currentInitialsField, setFieldValue, collectSignature, updateFieldIndicator]
7835
9494
  );
7836
9495
  const handleAcknowledgementComplete = useCallback(async (fieldId) => {
7837
9496
  logger.info(`[ACK COMPLETE] Field ${fieldId} fully acknowledged, continuing interaction`);
9497
+ recentlyAcknowledgedFieldsRef.current.set(fieldId, Date.now());
9498
+ setTimeout(() => {
9499
+ recentlyAcknowledgedFieldsRef.current.delete(fieldId);
9500
+ }, 3e3);
7838
9501
  const field = filteredFields.find((f) => f.id === fieldId);
7839
9502
  if (!field) {
7840
9503
  logger.warn(`[ACK COMPLETE] Field ${fieldId} not found`);
@@ -7862,30 +9525,165 @@ function SubmissionForm({
7862
9525
  setInitialsModalOpen(true);
7863
9526
  }
7864
9527
  }
9528
+ } else if (field.type === "date") {
9529
+ logger.info(`[ACK COMPLETE] Date field, opening calendar`);
9530
+ try {
9531
+ await new Promise((resolve) => setTimeout(resolve, 200));
9532
+ const fieldName = currentAcknowledgementFieldName || field.fieldId || field.name;
9533
+ logger.info(`[ACK COMPLETE] Looking for field with name: ${fieldName}`);
9534
+ const iframe = document.querySelector("iframe");
9535
+ if (iframe?.contentDocument) {
9536
+ const formElement = iframe.contentDocument.querySelector(
9537
+ `input[name="${fieldName}"], input[data-element-id="${fieldName}"]`
9538
+ );
9539
+ if (formElement) {
9540
+ const rect = formElement.getBoundingClientRect();
9541
+ const iframeRect = iframe.getBoundingClientRect();
9542
+ const position = {
9543
+ x: Math.round(iframeRect.left + rect.left),
9544
+ y: Math.round(iframeRect.top + rect.top),
9545
+ width: Math.round(rect.width),
9546
+ height: Math.round(rect.height)
9547
+ };
9548
+ setCurrentDateField(field);
9549
+ setDateFieldPosition(position);
9550
+ setDateCalendarOpen(true);
9551
+ logger.info(`[ACK COMPLETE] Opened calendar for date field in PDF view`);
9552
+ } else {
9553
+ logger.warn(`[ACK COMPLETE] Date field element not found in PDF with name: ${fieldName}`);
9554
+ }
9555
+ }
9556
+ } catch (error) {
9557
+ logger.error("[ACK COMPLETE] Failed to open calendar:", error);
9558
+ }
7865
9559
  } else {
7866
- logger.info(`[ACK COMPLETE] Regular field, attempting to focus in PDF`);
9560
+ const triggerSource = acknowledgementTriggerSourceRef.current;
9561
+ logger.info(`[ACK COMPLETE] Regular field (${field.type}), trigger: ${triggerSource}`);
9562
+ if (triggerSource === "navigation" && (field.type === "checkbox" || field.type === "radio")) {
9563
+ logger.info(`[ACK COMPLETE] Skipping auto-interaction for ${field.type} via navigation (unclear intent)`);
9564
+ acknowledgementTriggerSourceRef.current = null;
9565
+ currentAcknowledgementElementRef.current = null;
9566
+ return;
9567
+ }
7867
9568
  try {
7868
- await new Promise((resolve) => setTimeout(resolve, 100));
9569
+ await new Promise((resolve) => setTimeout(resolve, 200));
9570
+ const fieldName = currentAcknowledgementFieldName || field.fieldId || field.name;
9571
+ logger.info(`[ACK COMPLETE] Looking for field with name: ${fieldName}`);
7869
9572
  const iframe = document.querySelector("iframe");
7870
9573
  if (iframe?.contentDocument) {
7871
- const fieldElement = iframe.contentDocument.querySelector(`[data-element-id="${field.id}"]`);
7872
- if (fieldElement) {
7873
- fieldElement.focus();
7874
- fieldElement.scrollIntoView({ behavior: "smooth", block: "center" });
7875
- logger.info(`[ACK COMPLETE] Focused field in PDF`);
9574
+ if (field.type === "dropdown") {
9575
+ const storedElement = currentAcknowledgementElementRef.current;
9576
+ let selectElement = null;
9577
+ if (storedElement && storedElement.tagName === "SELECT") {
9578
+ selectElement = storedElement;
9579
+ logger.info(`[ACK COMPLETE] Using stored dropdown element`);
9580
+ } else {
9581
+ selectElement = iframe.contentDocument.querySelector(
9582
+ `select[name="${fieldName}"], select[data-element-id="${fieldName}"]`
9583
+ );
9584
+ logger.info(`[ACK COMPLETE] Querying for dropdown element`);
9585
+ }
9586
+ if (selectElement) {
9587
+ selectElement.scrollIntoView({ behavior: "smooth", block: "center" });
9588
+ await new Promise((resolve) => setTimeout(resolve, 200));
9589
+ selectElement.focus();
9590
+ await new Promise((resolve) => setTimeout(resolve, 100));
9591
+ try {
9592
+ if ("showPicker" in selectElement) {
9593
+ selectElement.showPicker();
9594
+ logger.info(`[ACK COMPLETE] Opened dropdown using showPicker() API`);
9595
+ } else {
9596
+ logger.info(`[ACK COMPLETE] showPicker() not supported, dropdown is focused`);
9597
+ }
9598
+ } catch (error) {
9599
+ logger.warn(`[ACK COMPLETE] showPicker() failed (may require user gesture):`, error);
9600
+ }
9601
+ } else {
9602
+ logger.warn(`[ACK COMPLETE] Dropdown element not found with name: ${fieldName}`);
9603
+ }
9604
+ } else if (field.type === "radio") {
9605
+ const storedElement = currentAcknowledgementElementRef.current;
9606
+ if (storedElement && storedElement.getAttribute("type") === "radio") {
9607
+ storedElement.scrollIntoView({ behavior: "smooth", block: "center" });
9608
+ await new Promise((resolve) => setTimeout(resolve, 200));
9609
+ storedElement.click();
9610
+ logger.info(`[ACK COMPLETE] Clicked specific radio option with value: ${storedElement.value}`);
9611
+ } else {
9612
+ const radioElement = iframe.contentDocument.querySelector(
9613
+ `input[type="radio"][name="${fieldName}"], input[type="radio"][data-element-id="${fieldName}"]`
9614
+ );
9615
+ if (radioElement) {
9616
+ radioElement.scrollIntoView({ behavior: "smooth", block: "center" });
9617
+ await new Promise((resolve) => setTimeout(resolve, 200));
9618
+ radioElement.focus();
9619
+ logger.info(`[ACK COMPLETE] Focused radio button group (fallback)`);
9620
+ } else {
9621
+ logger.warn(`[ACK COMPLETE] Radio element not found with name: ${fieldName}`);
9622
+ }
9623
+ }
9624
+ } else {
9625
+ const fieldElement = iframe.contentDocument.querySelector(
9626
+ `input[name="${fieldName}"], input[data-element-id="${fieldName}"], textarea[name="${fieldName}"], textarea[data-element-id="${fieldName}"]`
9627
+ );
9628
+ if (fieldElement) {
9629
+ fieldElement.scrollIntoView({ behavior: "smooth", block: "center" });
9630
+ await new Promise((resolve) => setTimeout(resolve, 200));
9631
+ if (field.type === "checkbox") {
9632
+ fieldElement.click();
9633
+ logger.info(`[ACK COMPLETE] Clicked checkbox field`);
9634
+ } else {
9635
+ fieldElement.focus();
9636
+ logger.info(`[ACK COMPLETE] Focused text field`);
9637
+ }
9638
+ } else {
9639
+ logger.warn(`[ACK COMPLETE] Field element not found with name: ${fieldName}`);
9640
+ }
7876
9641
  }
7877
9642
  }
7878
9643
  } catch (error) {
7879
- logger.warn("[ACK COMPLETE] Failed to focus field:", error);
9644
+ logger.warn("[ACK COMPLETE] Failed to interact with field:", error);
9645
+ } finally {
9646
+ acknowledgementTriggerSourceRef.current = null;
9647
+ currentAcknowledgementElementRef.current = null;
7880
9648
  }
7881
9649
  }
7882
- }, [filteredFields, hasCollectedSignature, getCollectedSignature, setSignature]);
9650
+ }, [filteredFields, hasCollectedSignature, getCollectedSignature, setSignature, setFieldValue, currentAcknowledgementFieldName]);
9651
+ const handleBulkAcknowledgementComplete = useCallback((fieldIds) => {
9652
+ logger.info(`[BULK ACK COMPLETE] Refreshing indicators for ${fieldIds.length} fields:`, fieldIds);
9653
+ fieldIds.forEach((fieldId) => {
9654
+ updateFieldIndicatorRef.current(fieldId);
9655
+ });
9656
+ const hasSignatureFields = fieldIds.some((id) => {
9657
+ const field = filteredFields.find((f) => f.id === id);
9658
+ return field?.type === "signature";
9659
+ });
9660
+ const hasInitialsFields2 = fieldIds.some((id) => {
9661
+ const field = filteredFields.find((f) => f.id === id);
9662
+ return field?.type === "initials";
9663
+ });
9664
+ if (hasSignatureFields) {
9665
+ updateFieldIndicatorRef.current("signature_field_main");
9666
+ }
9667
+ if (hasInitialsFields2) {
9668
+ updateFieldIndicatorRef.current("initials_field_main");
9669
+ }
9670
+ }, [filteredFields]);
7883
9671
  const validateForm = useCallback(async () => {
7884
9672
  const validationErrors2 = [];
7885
9673
  const currentFieldValues = fieldValuesRef.current;
7886
9674
  const currentSignatures = signaturesRef.current;
7887
9675
  const pdfFieldValues = await getFormFieldValues();
7888
9676
  const allFieldValues = { ...pdfFieldValues, ...currentFieldValues };
9677
+ const currentTouched = touchedRef.current;
9678
+ for (const [fieldId, pdfVal] of Object.entries(pdfFieldValues)) {
9679
+ if (pdfVal === "") {
9680
+ const reactVal = currentFieldValues[fieldId];
9681
+ const fieldTouched = currentTouched[fieldId] || false;
9682
+ if (!reactVal || reactVal.trim() === "" || !fieldTouched) {
9683
+ allFieldValues[fieldId] = "";
9684
+ }
9685
+ }
9686
+ }
7889
9687
  console.log("[VALIDATION] === MERGING SIGNATURES INTO allFieldValues ===");
7890
9688
  console.log("[VALIDATION] currentSignatures:", currentSignatures);
7891
9689
  for (const [fieldId, signatureValue] of Object.entries(currentSignatures)) {
@@ -7912,13 +9710,12 @@ function SubmissionForm({
7912
9710
  if (value && value.trim() !== "") {
7913
9711
  const validation = parseAndValidateDate(value);
7914
9712
  if (!validation.isValid) {
7915
- const fieldLabel = field.label || field.id;
7916
9713
  validationErrors2.push(
7917
- `${fieldLabel}: Invalid date format. Please use MM/DD/YYYY, YYYY-MM-DD, or similar standard format.`
9714
+ `${getFieldDisplayName(field)}: Invalid date format. Please use MM/DD/YYYY, YYYY-MM-DD, or similar standard format.`
7918
9715
  );
7919
9716
  }
7920
9717
  } else if (field.required) {
7921
- validationErrors2.push(`${field.label || field.id} is required`);
9718
+ validationErrors2.push(`${getFieldDisplayName(field)} is required`);
7922
9719
  }
7923
9720
  }
7924
9721
  const signatureFields = filteredFields.filter((f) => f.type === "signature");
@@ -7936,12 +9733,21 @@ function SubmissionForm({
7936
9733
  console.log("[VALIDATION] Value from allFieldValues:", value ? "(found, truncated): " + value.substring(0, 50) : "(MISSING)");
7937
9734
  if (!value || value.trim() === "") {
7938
9735
  console.log("[VALIDATION] ERROR: Field is required but missing!");
7939
- validationErrors2.push(`${field.label} is required`);
9736
+ validationErrors2.push(`${getFieldDisplayName(field)} is required`);
7940
9737
  } else {
7941
9738
  console.log("[VALIDATION] OK: Field has value");
7942
9739
  }
7943
9740
  }
7944
9741
  }
9742
+ const alreadyCheckedTypes = /* @__PURE__ */ new Set(["date", "signature", "initials"]);
9743
+ for (const field of filteredFields) {
9744
+ if (!field.required || alreadyCheckedTypes.has(field.type)) continue;
9745
+ if (field.id === "signature_field_main" || field.id === "initials_field_main") continue;
9746
+ const value = allFieldValues[field.id];
9747
+ if (!value || typeof value === "string" && value.trim() === "") {
9748
+ validationErrors2.push(`${getFieldDisplayName(field)} is required`);
9749
+ }
9750
+ }
7945
9751
  const isFieldsValid = validateFields(currentSignatures);
7946
9752
  if (!isFieldsValid) {
7947
9753
  for (const field of filteredFields) {
@@ -8051,6 +9857,16 @@ function SubmissionForm({
8051
9857
  const currentFieldValues = fieldValuesRef.current;
8052
9858
  const currentSignatures = signaturesRef.current;
8053
9859
  const finalFieldValues = { ...pdfFieldValues, ...currentFieldValues };
9860
+ const currentTouchedSubmit = touchedRef.current;
9861
+ for (const [fieldId, pdfVal] of Object.entries(pdfFieldValues)) {
9862
+ if (pdfVal === "") {
9863
+ const reactVal = currentFieldValues[fieldId];
9864
+ const fieldTouched = currentTouchedSubmit[fieldId] || false;
9865
+ if (!reactVal || reactVal.trim() === "" || !fieldTouched) {
9866
+ finalFieldValues[fieldId] = "";
9867
+ }
9868
+ }
9869
+ }
8054
9870
  console.log("[SUBMIT] === MERGING SIGNATURES INTO finalFieldValues FOR FLATTENING ===");
8055
9871
  console.log("[SUBMIT] currentSignatures:", currentSignatures);
8056
9872
  for (const [fieldId, signatureValue] of Object.entries(currentSignatures)) {
@@ -8073,6 +9889,11 @@ function SubmissionForm({
8073
9889
  }
8074
9890
  }
8075
9891
  if (enableAttachments) {
9892
+ if (attachments.length === 0) {
9893
+ setValidationErrors(["At least one attachment is required"]);
9894
+ setIsSubmitting(false);
9895
+ return;
9896
+ }
8076
9897
  const attachmentValidation = validateAttachments();
8077
9898
  if (!attachmentValidation.isValid) {
8078
9899
  setValidationErrors(attachmentValidation.errors);
@@ -8164,28 +9985,34 @@ function SubmissionForm({
8164
9985
  return false;
8165
9986
  }
8166
9987
  }
8167
- return true;
8168
- }
8169
- for (const field of filteredFields) {
8170
- if (!field.required) continue;
8171
- if (field.id === "signature_field_main" || field.id === "initials_field_main") {
8172
- if (!hasSignature(field.id)) return false;
8173
- continue;
8174
- }
8175
- if (field.type === "signature" || field.type === "initials") {
8176
- if (!hasSignature(field.id)) return false;
8177
- } else {
8178
- const value = fieldValues[field.id];
8179
- if (!value || value.trim() === "") return false;
9988
+ } else {
9989
+ for (const field of filteredFields) {
9990
+ if (!field.required) continue;
9991
+ if (field.id === "signature_field_main" || field.id === "initials_field_main") {
9992
+ if (!hasSignature(field.id)) return false;
9993
+ continue;
9994
+ }
9995
+ if (field.type === "signature" || field.type === "initials") {
9996
+ if (!hasSignature(field.id)) return false;
9997
+ } else {
9998
+ const value = fieldValues[field.id];
9999
+ if (!value || value.trim() === "") return false;
10000
+ }
8180
10001
  }
8181
10002
  }
10003
+ if (enableAttachments && attachments.length === 0) {
10004
+ return false;
10005
+ }
8182
10006
  return true;
8183
- }, [filteredFields, fieldValues, hasSignature, showFullFieldsSidebar]);
10007
+ }, [filteredFields, fieldValues, hasSignature, showFullFieldsSidebar, enableAttachments, attachments]);
8184
10008
  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: [
8185
- 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: [
8186
- /* @__PURE__ */ jsx(CardTitle, { className: "text-2xl sm:text-3xl md:text-4xl font-bold text-primary tracking-tight flex-1", children: documentTitle }),
8187
- showPoweredBy && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(PoweredBySigniphi, {}) })
8188
- ] }) }) }),
10009
+ (documentTitle || signingInstructions || showPoweredBy) && /* @__PURE__ */ jsxs("div", { className: "flex-shrink-0", children: [
10010
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row items-start justify-between gap-3 sm:gap-4", children: [
10011
+ documentTitle && /* @__PURE__ */ jsx("h1", { className: "text-2xl sm:text-3xl md:text-4xl font-bold text-primary tracking-tight flex-1", children: documentTitle }),
10012
+ showPoweredBy && /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(PoweredBySigniphi, {}) })
10013
+ ] }),
10014
+ signingInstructions && /* @__PURE__ */ jsx("div", { className: "text-sm md:text-base text-muted-foreground", children: /* @__PURE__ */ jsx(SigningInstructions, { html: signingInstructions }) })
10015
+ ] }),
8189
10016
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col lg:flex-row gap-4", children: [
8190
10017
  showPdfViewer && /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col bg-muted/30 rounded-lg overflow-hidden relative min-w-0 border-2", children: [
8191
10018
  /* @__PURE__ */ jsx(
@@ -8253,8 +10080,7 @@ function SubmissionForm({
8253
10080
  /* @__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" }) }),
8254
10081
  "Sign Document"
8255
10082
  ] }),
8256
- /* @__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" }),
8257
- 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 }) })
10083
+ /* @__PURE__ */ jsx(CardDescription, { className: "text-muted-foreground text-sm md:text-base leading-relaxed text-primary", children: customMessage || defaultSigningMessage })
8258
10084
  ] }),
8259
10085
  /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4 md:space-y-6 px-4 md:px-6", children: [
8260
10086
  validationErrors.length > 0 && /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
@@ -8372,7 +10198,11 @@ function SubmissionForm({
8372
10198
  onCollapseChange: setEditableFieldsCollapsed
8373
10199
  }
8374
10200
  ) }),
8375
- enableAttachments && /* @__PURE__ */ jsxs("div", { className: "mt-4 md:mt-6", children: [
10201
+ enableAttachments && /* @__PURE__ */ jsxs("div", { className: "mt-4 md:mt-6 mb-6 md:mb-8", children: [
10202
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
10203
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: "Attachments" }),
10204
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-destructive", children: "(Required)" })
10205
+ ] }),
8376
10206
  /* @__PURE__ */ jsx(
8377
10207
  AttachmentUpload,
8378
10208
  {
@@ -8382,10 +10212,12 @@ function SubmissionForm({
8382
10212
  isUploading: isUploadingAttachments,
8383
10213
  disabled: readOnly || isSubmitting,
8384
10214
  maxFiles: maxAttachments,
8385
- formatSize
10215
+ formatSize,
10216
+ constraints: attachmentConstraints,
10217
+ validationErrors: attachmentErrors.length > 0 ? attachmentErrors : void 0,
10218
+ onClearErrors: clearAttachmentErrors
8386
10219
  }
8387
- ),
8388
- 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)) }) })
10220
+ )
8389
10221
  ] })
8390
10222
  ] }),
8391
10223
  !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: [
@@ -8453,7 +10285,7 @@ function SubmissionForm({
8453
10285
  AcknowledgementModal,
8454
10286
  {
8455
10287
  open: acknowledgementModalOpen,
8456
- onOpenChange: setAcknowledgementModalOpen,
10288
+ onOpenChange: handleAcknowledgementModalOpenChange,
8457
10289
  field: currentAcknowledgementField,
8458
10290
  onAcknowledge: acknowledgeItem,
8459
10291
  isAcknowledged,
@@ -8470,7 +10302,19 @@ function SubmissionForm({
8470
10302
  unacknowledgedCount: unacknowledgedItems.length
8471
10303
  })),
8472
10304
  onAcknowledge: acknowledgeItem,
8473
- isAcknowledged
10305
+ isAcknowledged,
10306
+ onComplete: handleBulkAcknowledgementComplete
10307
+ }
10308
+ ),
10309
+ /* @__PURE__ */ jsx(
10310
+ DateFieldCalendarPopup,
10311
+ {
10312
+ open: dateCalendarOpen,
10313
+ onOpenChange: setDateCalendarOpen,
10314
+ value: currentDateField ? fieldValues[currentDateField.id] || "" : "",
10315
+ onSelect: handleDateSelect,
10316
+ fieldPosition: dateFieldPosition,
10317
+ fieldLabel: currentDateField?.label || currentDateField?.name
8474
10318
  }
8475
10319
  )
8476
10320
  ] });
@@ -8551,6 +10395,6 @@ function ErrorBoundary2({
8551
10395
  );
8552
10396
  }
8553
10397
 
8554
- export { AcknowledgementModal, AcknowledgementsSidebar, AttachmentUpload, CheckboxRenderer, DateFieldRenderer, DropdownRenderer, EditableFieldsPanel, ErrorBoundary2 as ErrorBoundary, FormFieldRenderer, FormFieldsView, InitialsFieldRenderer, InitialsModal, PdfViewerStyled, PoweredBySigniphi, RadioGroupRenderer, SignatureCanvas, SignatureFieldRenderer, SignatureInitialsBox, SignatureModal, SignatureModalCore, SigningInstructions, SubmissionForm, TextFieldRenderer, TextLabelRenderer, ViewToggleToolbar };
10398
+ export { AcknowledgementModal, AcknowledgementsSidebar, AttachmentUpload, CheckboxRenderer, DateFieldRenderer, DropdownRenderer, EditableFieldsPanel, ErrorBoundary2 as ErrorBoundary, FormFieldRenderer, FormFieldsView, InitialsFieldRenderer, InitialsModal, PdfViewerStyled, PoweredBySigniphi, RadioGroupRenderer, SignatureCanvas, SignatureFieldRenderer, SignatureInitialsBox, SignatureModal, SignatureModalCore, SignatureTypeInput, SigningInstructions, SubmissionForm, TextFieldRenderer, TextLabelRenderer, ViewToggleToolbar };
8555
10399
  //# sourceMappingURL=index.mjs.map
8556
10400
  //# sourceMappingURL=index.mjs.map