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