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