df-ae-forms-package 1.1.5 → 1.1.7
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/dist/index.d.ts +1 -0
- package/dist/index.esm.js +246 -39
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +246 -39
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.esm.js
CHANGED
|
@@ -332,7 +332,7 @@ const RaiseIssueModal = ({ isOpen, onClose, onSuccess, component, formTemplateId
|
|
|
332
332
|
'Maria Garcia',
|
|
333
333
|
'John Smith',
|
|
334
334
|
'Sarah Johnson'
|
|
335
|
-
], workOrderNumber: initialWorkOrderNumber, assetNumber: initialAssetNumber, isStandalone, isEdit }) => {
|
|
335
|
+
], workOrderNumber: initialWorkOrderNumber, assetNumber: initialAssetNumber, isStandalone, inEditMode = false, isEdit }) => {
|
|
336
336
|
// Determine if workflow actions should be enabled
|
|
337
337
|
// If explicitly provided, use that. Otherwise, default to isEdit (true if editing, false/undefined otherwise).
|
|
338
338
|
// Fallback to false if neither is provided.
|
|
@@ -348,6 +348,7 @@ const RaiseIssueModal = ({ isOpen, onClose, onSuccess, component, formTemplateId
|
|
|
348
348
|
// State to hold both File objects and existing attachments
|
|
349
349
|
const [localAttachments, setLocalAttachments] = useState(attachments || []);
|
|
350
350
|
const [isDragging, setIsDragging] = useState(false);
|
|
351
|
+
const [isViewMode, setIsViewMode] = useState(!!issue && !inEditMode);
|
|
351
352
|
const fileInputRef = React.useRef(null);
|
|
352
353
|
const isEditMode = isEdit !== undefined ? isEdit : !!issue;
|
|
353
354
|
useEffect(() => {
|
|
@@ -369,6 +370,11 @@ const RaiseIssueModal = ({ isOpen, onClose, onSuccess, component, formTemplateId
|
|
|
369
370
|
}
|
|
370
371
|
}
|
|
371
372
|
}, [isOpen, attachments, issue, isEditMode]);
|
|
373
|
+
useEffect(() => {
|
|
374
|
+
if (issue) {
|
|
375
|
+
setIsViewMode(!inEditMode);
|
|
376
|
+
}
|
|
377
|
+
}, [issue, inEditMode]);
|
|
372
378
|
const handleFileChange = (event) => {
|
|
373
379
|
if (event.target.files && event.target.files.length > 0) {
|
|
374
380
|
const newFiles = Array.from(event.target.files);
|
|
@@ -585,6 +591,7 @@ const RaiseIssueModal = ({ isOpen, onClose, onSuccess, component, formTemplateId
|
|
|
585
591
|
await onUpdateIssue(issue._id, updateData);
|
|
586
592
|
}
|
|
587
593
|
toastService.showSuccess('Issue updated successfully');
|
|
594
|
+
setIsViewMode(true);
|
|
588
595
|
if (onSuccess) {
|
|
589
596
|
onSuccess();
|
|
590
597
|
}
|
|
@@ -666,7 +673,7 @@ const RaiseIssueModal = ({ isOpen, onClose, onSuccess, component, formTemplateId
|
|
|
666
673
|
if (!isOpen) {
|
|
667
674
|
return null;
|
|
668
675
|
}
|
|
669
|
-
const modalContent = (jsx("div", { className: "raise-issue-modal-overlay", onClick: onClose, children: jsxs("div", { className: "raise-issue-modal", onClick: (e) => e.stopPropagation(), children: [jsxs("div", { className: "raise-issue-modal-header", children: [jsxs("div", { className: "raise-issue-modal-header-left", children: [jsx(AlertTriangle, { className: "raise-issue-modal-icon", size: 20 }), jsx("div", { className: "raise-issue-modal-header-text", children: jsx("div", { className: "raise-issue-modal-title-main", children: isEditMode ? 'Issue Details' : 'Create Issue' }) })] }), jsx("button", { className: "raise-issue-modal-close", onClick: onClose, "aria-label": "Close", children: jsx(X$1, { size: 18 }) })] }), jsx("div", { className: "raise-issue-modal-content", children: jsxs("div", { className: "raise-issue-fields-grid", children: [isEditMode && issue?.issueNumber && (jsxs("div", { className: "raise-issue-field", children: [jsx("label", { className: "raise-issue-field-label", children: "Issue ID" }), jsx("input", { type: "text", className: "raise-issue-field-input raise-issue-field-readonly", value: issue.issueNumber, readOnly: true })] })), jsxs("div", { className: "raise-issue-field", children: [jsx("label", { className: "raise-issue-field-label", children: "Task" }), jsx("input", { type: "text", className: "raise-issue-field-input raise-issue-field-readonly", value: isEditMode ? (issue?.component?.basic?.label || '') : (component?.basic?.label || ''), readOnly: true })] }), jsxs("div", { className: "raise-issue-field", children: [jsx("label", { className: "raise-issue-field-label", children: "Task Value" }), jsx("input", { type: "text", className: "raise-issue-field-input raise-issue-field-readonly", value: isEditMode ? (issue?.component?.basic?.value || '') : (component?.basic?.value || ''), readOnly: true })] }), jsxs("div", { className: "raise-issue-field", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Title ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsx("input", { type: "text", className: `raise-issue-field-input ${isEditMode ? 'raise-issue-field-readonly' : ''}`, value: title, onChange: (e) => setTitle(e.target.value), placeholder: "Enter issue title", readOnly: isEditMode })] }), !isStandalone && (jsxs("div", { className: "raise-issue-field", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Work Order ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsx("input", { type: "text", className: `raise-issue-field-input ${isEditMode ? 'raise-issue-field-readonly' : ''}`, value: workOrderNumber, onChange: (e) => setWorkOrderNumber(e.target.value), placeholder: "N/A", readOnly: isEditMode })] })), !isStandalone && (jsxs("div", { className: "raise-issue-field", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Asset Number ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsx("input", { type: "text", className: `raise-issue-field-input ${isEditMode ? 'raise-issue-field-readonly' : ''}`, value: assetNumber, onChange: (e) => setAssetNumber(e.target.value), placeholder: "Enter asset number", readOnly: isEditMode })] })), jsxs("div", { className: "raise-issue-field", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Raised By ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsxs("div", { className: "raise-issue-field-value-with-icon", children: [jsx(User, { size: 16 }), jsx("span", { children: user ? `${user.firstName || ''} ${user.lastName || ''}`.trim() || 'User' : 'User' })] })] }), jsxs("div", { className: "raise-issue-field", children: [jsx("label", { className: "raise-issue-field-label", children: "Created On" }), jsxs("div", { className: "raise-issue-field-value-with-icon", children: [jsx(Calendar, { size: 16 }), jsx("span", { children: isEditMode && issue?.createdAt
|
|
676
|
+
const modalContent = (jsx("div", { className: "raise-issue-modal-overlay", onClick: onClose, children: jsxs("div", { className: "raise-issue-modal", onClick: (e) => e.stopPropagation(), children: [jsxs("div", { className: "raise-issue-modal-header", children: [jsxs("div", { className: "raise-issue-modal-header-left", children: [jsx(AlertTriangle, { className: "raise-issue-modal-icon", size: 20 }), jsx("div", { className: "raise-issue-modal-header-text", children: jsx("div", { className: "raise-issue-modal-title-main", children: isEditMode ? 'Issue Details' : 'Create Issue' }) })] }), jsx("button", { className: "raise-issue-modal-close", onClick: onClose, "aria-label": "Close", children: jsx(X$1, { size: 18 }) })] }), jsx("div", { className: "raise-issue-modal-content", children: jsxs("div", { className: "raise-issue-fields-grid", children: [isEditMode && issue?.issueNumber && (jsxs("div", { className: "raise-issue-field", children: [jsx("label", { className: "raise-issue-field-label", children: "Issue ID" }), jsx("input", { type: "text", className: "raise-issue-field-input raise-issue-field-readonly", value: issue.issueNumber, readOnly: true })] })), jsxs("div", { className: "raise-issue-field", children: [jsx("label", { className: "raise-issue-field-label", children: "Task" }), jsx("input", { type: "text", className: "raise-issue-field-input raise-issue-field-readonly", value: isEditMode ? (issue?.component?.basic?.label || '') : (component?.basic?.label || ''), readOnly: true })] }), jsxs("div", { className: "raise-issue-field", children: [jsx("label", { className: "raise-issue-field-label", children: "Task Value" }), jsx("input", { type: "text", className: "raise-issue-field-input raise-issue-field-readonly", value: isEditMode ? (issue?.component?.basic?.value || '') : (component?.basic?.value || ''), readOnly: true })] }), jsxs("div", { className: "raise-issue-field", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Title ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsx("input", { type: "text", className: `raise-issue-field-input ${isEditMode ? 'raise-issue-field-readonly' : ''}`, value: title, onChange: (e) => setTitle(e.target.value), placeholder: "Enter issue title", readOnly: isEditMode && isViewMode })] }), !isStandalone && (jsxs("div", { className: "raise-issue-field", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Work Order ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsx("input", { type: "text", className: `raise-issue-field-input ${isEditMode ? 'raise-issue-field-readonly' : ''}`, value: workOrderNumber, onChange: (e) => setWorkOrderNumber(e.target.value), placeholder: "N/A", readOnly: isEditMode && isViewMode })] })), !isStandalone && (jsxs("div", { className: "raise-issue-field", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Asset Number ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsx("input", { type: "text", className: `raise-issue-field-input ${isEditMode ? 'raise-issue-field-readonly' : ''}`, value: assetNumber, onChange: (e) => setAssetNumber(e.target.value), placeholder: "Enter asset number", readOnly: isEditMode && isViewMode })] })), jsxs("div", { className: "raise-issue-field", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Raised By ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsxs("div", { className: "raise-issue-field-value-with-icon", children: [jsx(User, { size: 16 }), jsx("span", { children: user ? `${user.firstName || ''} ${user.lastName || ''}`.trim() || 'User' : 'User' })] })] }), jsxs("div", { className: "raise-issue-field", children: [jsx("label", { className: "raise-issue-field-label", children: "Created On" }), jsxs("div", { className: "raise-issue-field-value-with-icon", children: [jsx(Calendar, { size: 16 }), jsx("span", { children: isEditMode && issue?.createdAt
|
|
670
677
|
? (safeDate(issue.createdAt) || new Date()).toLocaleString('en-US', {
|
|
671
678
|
year: 'numeric',
|
|
672
679
|
month: 'short',
|
|
@@ -682,7 +689,7 @@ const RaiseIssueModal = ({ isOpen, onClose, onSuccess, component, formTemplateId
|
|
|
682
689
|
hour: '2-digit',
|
|
683
690
|
minute: '2-digit',
|
|
684
691
|
hour12: true
|
|
685
|
-
}) })] })] }), jsxs("div", { className: "raise-issue-field raise-issue-field-full-width", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Description ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsx("textarea", { className: `raise-issue-field-textarea ${isEditMode ? 'raise-issue-field-readonly' : ''}`, value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Enter issue description", rows: 4, readOnly: isEditMode })] }), jsxs("div", { className: "raise-issue-field", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Status ", jsx("span", { className: "raise-issue-required", children: "*" })] }),
|
|
692
|
+
}) })] })] }), jsxs("div", { className: "raise-issue-field raise-issue-field-full-width", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Description ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsx("textarea", { className: `raise-issue-field-textarea ${isEditMode ? 'raise-issue-field-readonly' : ''}`, value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Enter issue description", rows: 4, readOnly: isEditMode && isViewMode })] }), jsxs("div", { className: "raise-issue-field", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Status ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsx("input", { type: "text", className: "raise-issue-field-input raise-issue-field-readonly", value: status, readOnly: true })] }), jsxs("div", { className: "raise-issue-field", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Assign to ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsxs("select", { className: "raise-issue-field-select", value: assignee, onChange: (e) => setAssignee(e.target.value), disabled: isEditMode && isViewMode, children: [jsx("option", { value: "", children: "Unassigned" }), availableUsers.map(userName => (jsx("option", { value: userName, children: userName }, userName)))] })] }), jsxs("div", { className: "raise-issue-field raise-issue-field-full-width", children: [jsxs("label", { className: "raise-issue-field-label", children: ["Priority ", jsx("span", { className: "raise-issue-required", children: "*" })] }), jsxs("div", { className: "raise-issue-priority-buttons", children: [jsx("button", { type: "button", className: `priority-button priority-low ${priority === 'Low' ? 'active' : ''}`, onClick: () => setPriority('Low'), disabled: isEditMode && isViewMode, children: "Low" }), jsx("button", { type: "button", className: `priority-button priority-medium ${priority === 'Medium' ? 'active' : ''}`, onClick: () => setPriority('Medium'), disabled: isEditMode && isViewMode, children: "Medium" }), jsx("button", { type: "button", className: `priority-button priority-high ${priority === 'High' ? 'active' : ''}`, onClick: () => setPriority('High'), disabled: isEditMode && isViewMode, children: "High" })] })] }), jsxs("div", { className: "raise-issue-field raise-issue-field-full-width", children: [jsxs("label", { className: "raise-issue-field-label-with-icon", children: [jsx(MessageSquare, { size: 16 }), jsx("span", { children: "Comments" })] }), !comments && (jsx("div", { className: "raise-issue-no-comments", children: "No comments yet" })), jsx("textarea", { className: `raise-issue-field-textarea raise-issue-comments-textarea ${isEditMode ? 'raise-issue-field-readonly' : ''}`, value: comments, onChange: (e) => setComments(e.target.value), placeholder: "Add a comment...", rows: 4, readOnly: isEditMode })] }), jsxs("div", { className: "raise-issue-field raise-issue-field-full-width", style: { marginTop: '1rem' }, children: [jsx("label", { className: "raise-issue-field-label", children: "Attachments" }), jsxs("div", { className: "raise-issue-attachments-container", children: [!isEditMode && (jsxs("div", { className: `raise-issue-dropzone ${isDragging ? 'dragging' : ''}`, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, onClick: () => fileInputRef.current?.click(), children: [jsx("input", { type: "file", ref: fileInputRef, style: { display: 'none' }, onChange: handleFileChange, multiple: true }), jsx("div", { className: "raise-issue-dropzone-icon", children: jsx(UploadCloud, { size: 24 }) }), jsx("div", { className: "raise-issue-dropzone-text", children: "Click to upload or drag and drop" }), jsx("div", { className: "raise-issue-dropzone-hint", children: "PNG, JPG up to 10MB" })] })), localAttachments.length > 0 && (jsx("div", { className: "raise-issue-attachments-list", children: localAttachments.map((file, index) => (jsxs("div", { className: "raise-issue-attachment-item", children: [jsx("div", { className: "raise-issue-attachment-thumbnail", children: getAttachmentPreview(file) ? (jsx("img", { src: getAttachmentPreview(file), alt: "Thumbnail" })) : (jsx(Paperclip, { size: 20, color: "#9ca3af" })) }), jsxs("div", { className: "raise-issue-attachment-info", children: [jsx("span", { className: "attachment-name", title: getAttachmentName(file), children: getAttachmentName(file) }), file instanceof File && (jsxs("span", { className: "attachment-size", children: [(file.size / 1024).toFixed(1), " KB"] }))] }), !isEditMode && (jsx("button", { type: "button", className: "attachment-remove-btn", onClick: () => handleRemoveAttachment(index), title: "Remove", children: jsx(X$1, { size: 12 }) }))] }, index))) }))] })] })] }) }), jsx("div", { className: "raise-issue-modal-actions", children: jsxs("div", { className: "raise-issue-modal-actions-buttons", children: [isEditMode && issue && !isViewMode && (jsxs(Fragment, { children: [status === EIssueStatus.OPEN && (jsx("button", { className: "raise-issue-modal-button raise-issue-modal-button-accept", onClick: handleAccept, disabled: isSubmitting, children: isSubmitting ? 'Processing...' : 'Accept' })), status === EIssueStatus.IN_PROGRESS && (jsx("button", { className: "raise-issue-modal-button raise-issue-modal-button-resolve", onClick: handleResolve, disabled: isSubmitting, children: isSubmitting ? 'Processing...' : 'Resolve' })), (status === EIssueStatus.OPEN) && (jsx("button", { className: "raise-issue-modal-button raise-issue-modal-button-reject", onClick: handleReject, disabled: isSubmitting, children: isSubmitting ? 'Processing...' : 'Reject' }))] })), !isEditMode && (jsx("button", { className: `raise-issue-modal-button raise-issue-modal-button-save ${!isFormValid ? 'disabled' : ''}`, onClick: handleSubmit, disabled: isSubmitting || !isFormValid, children: isSubmitting ? 'Creating...' : 'Create Issue' }))] }) })] }) }));
|
|
686
693
|
// Render modal using portal to document body for full-page overlay
|
|
687
694
|
return createPortal(modalContent, document.body);
|
|
688
695
|
};
|
|
@@ -818,7 +825,6 @@ const DfFormInput = ({ id, properties, validationErrors = {}, formValue = '', in
|
|
|
818
825
|
// Update ref if ID changes (shouldn't happen, but safety check)
|
|
819
826
|
useEffect(() => {
|
|
820
827
|
if (id !== componentIdRef.current) {
|
|
821
|
-
console.warn(`[DfFormInput] Component ID changed from ${componentIdRef.current} to ${id}`);
|
|
822
828
|
componentIdRef.current = id;
|
|
823
829
|
}
|
|
824
830
|
}, [id]);
|
|
@@ -2996,8 +3002,6 @@ const DfFormLocation = ({ id, properties, validationErrors = {}, formValue = nul
|
|
|
2996
3002
|
}
|
|
2997
3003
|
}
|
|
2998
3004
|
catch (error) {
|
|
2999
|
-
// Capacitor not properly initialized, fall through to web geolocation
|
|
3000
|
-
console.warn('Capacitor Geolocation not available, falling back to web geolocation');
|
|
3001
3005
|
}
|
|
3002
3006
|
}
|
|
3003
3007
|
// Fallback to standard web geolocation API (for browsers)
|
|
@@ -3711,7 +3715,6 @@ const AttachmentThumbnails = ({ attachments, onRemove }) => {
|
|
|
3711
3715
|
newUrls.set(index, url);
|
|
3712
3716
|
}
|
|
3713
3717
|
catch (e) {
|
|
3714
|
-
console.warn('Failed to create object URL for attachment:', e);
|
|
3715
3718
|
}
|
|
3716
3719
|
}
|
|
3717
3720
|
else if (file && file.url && file.type && file.type.startsWith('image/')) {
|
|
@@ -4622,7 +4625,6 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
|
|
|
4622
4625
|
zIndex: 5
|
|
4623
4626
|
}, children: jsxs("button", { onClick: (e) => {
|
|
4624
4627
|
e.stopPropagation();
|
|
4625
|
-
console.log('[TableView] Add Entry button clicked (grid view)', 'entries:', dataEntries.length, 'max:', maxEntries);
|
|
4626
4628
|
onAddEntry();
|
|
4627
4629
|
}, disabled: dataEntries.length >= maxEntries, style: {
|
|
4628
4630
|
padding: '8px 16px',
|
|
@@ -4748,7 +4750,6 @@ formTemplateId, onThresholdActionCompletion, onThresholdIssueRaised, onNotesChan
|
|
|
4748
4750
|
justifyContent: 'center'
|
|
4749
4751
|
}, children: jsxs("button", { onClick: (e) => {
|
|
4750
4752
|
e.stopPropagation();
|
|
4751
|
-
console.log('[TableView] Add Entry button clicked (list view)', 'entries:', dataEntries.length, 'max:', maxEntries);
|
|
4752
4753
|
onAddEntry();
|
|
4753
4754
|
}, disabled: dataEntries.length >= maxEntries, style: {
|
|
4754
4755
|
padding: '8px 16px',
|
|
@@ -5001,12 +5002,8 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
5001
5002
|
// In edit/preview modes, don't handle value changes as components are read-only
|
|
5002
5003
|
}, [mode, onValueChange]);
|
|
5003
5004
|
const handleAddEntry = useCallback(() => {
|
|
5004
|
-
console.log('[DfFormDataGrid] handleAddEntry called - Component ID:', id);
|
|
5005
5005
|
// Safety check: ensure we have entries array
|
|
5006
5006
|
const currentEntries = Array.isArray(properties.entries) ? properties.entries : [];
|
|
5007
|
-
console.log('[DfFormDataGrid] gridComponents:', gridComponents.length);
|
|
5008
|
-
console.log('[DfFormDataGrid] current entries count:', currentEntries.length);
|
|
5009
|
-
console.log('[DfFormDataGrid] onValueChange exists:', !!onValueChange);
|
|
5010
5007
|
// Use timestamp and random string to ensure uniqueness even if entries are deleted
|
|
5011
5008
|
const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
|
|
5012
5009
|
const newEntryId = `entry-${uniqueSuffix}`;
|
|
@@ -5028,11 +5025,8 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
5028
5025
|
}),
|
|
5029
5026
|
styles: {}
|
|
5030
5027
|
};
|
|
5031
|
-
console.log('[DfFormDataGrid] newEntry created:', newEntry.id, 'with', newEntry.components.length, 'components');
|
|
5032
5028
|
const updatedEntries = [...currentEntries, newEntry];
|
|
5033
|
-
console.log('[DfFormDataGrid] updatedEntries count:', updatedEntries.length);
|
|
5034
5029
|
if (onValueChange) {
|
|
5035
|
-
console.log('[DfFormDataGrid] calling onValueChange with updated datagrid structure - Entries:', updatedEntries.length);
|
|
5036
5030
|
onValueChange({
|
|
5037
5031
|
id: gridId, // Use sanitized gridId
|
|
5038
5032
|
value: {
|
|
@@ -5042,13 +5036,9 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
5042
5036
|
});
|
|
5043
5037
|
// Notify parent if callback provided
|
|
5044
5038
|
if (onEntryAdd) {
|
|
5045
|
-
console.log('[DfFormDataGrid] calling onEntryAdd callback');
|
|
5046
5039
|
onEntryAdd();
|
|
5047
5040
|
}
|
|
5048
5041
|
}
|
|
5049
|
-
else {
|
|
5050
|
-
console.warn('[DfFormDataGrid] Cannot add entry: onValueChange is missing');
|
|
5051
|
-
}
|
|
5052
5042
|
}, [onValueChange, properties, gridId, gridComponents, onEntryAdd]);
|
|
5053
5043
|
const handleRemoveEntry = useCallback((entryIndex) => {
|
|
5054
5044
|
// Safety check: ensure we have entries array
|
|
@@ -5057,7 +5047,6 @@ const DfFormDataGrid = ({ id, properties, mode = 'edit', formData = {}, onValueC
|
|
|
5057
5047
|
.filter((_, index) => index !== entryIndex)
|
|
5058
5048
|
.map((entry, index) => ({ ...entry, index })); // Only update index, preserve unique ID
|
|
5059
5049
|
if (onValueChange) {
|
|
5060
|
-
console.log('[DfFormDataGrid] Removing entry at index:', entryIndex, 'New count:', updatedEntries.length);
|
|
5061
5050
|
onValueChange({
|
|
5062
5051
|
id: gridId, // Use sanitized gridId
|
|
5063
5052
|
value: { ...properties, entries: updatedEntries }
|
|
@@ -5445,8 +5434,8 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5445
5434
|
const [validationErrors, setValidationErrors] = useState({});
|
|
5446
5435
|
const [formSubmitted, setFormSubmitted] = useState(false);
|
|
5447
5436
|
const [touchedFields, setTouchedFields] = useState({});
|
|
5448
|
-
//
|
|
5449
|
-
|
|
5437
|
+
// Component visibility state - driven by conditional logic evaluation
|
|
5438
|
+
const [componentVisibility, setComponentVisibility] = useState({});
|
|
5450
5439
|
// Track raised issues for threshold conditions (Set of condition IDs)
|
|
5451
5440
|
const [raisedThresholdIssues, setRaisedThresholdIssues] = useState(new Set());
|
|
5452
5441
|
// Track threshold action completions: Map<conditionId, { notesCompleted, attachmentsCompleted, emailSent }>
|
|
@@ -5544,6 +5533,11 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5544
5533
|
if (component.children && Array.isArray(component.children)) {
|
|
5545
5534
|
initializeComponentValues(component.children, values);
|
|
5546
5535
|
}
|
|
5536
|
+
// CRITICAL for conditional logic: Also store value under the label key
|
|
5537
|
+
// so condition lookups by label (e.g. "Temperature") work during init
|
|
5538
|
+
if (component.basic?.label && componentId && values[componentId] !== undefined) {
|
|
5539
|
+
values[component.basic.label] = values[componentId];
|
|
5540
|
+
}
|
|
5547
5541
|
});
|
|
5548
5542
|
};
|
|
5549
5543
|
const initializeFormState = useCallback(() => {
|
|
@@ -5615,15 +5609,12 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5615
5609
|
const labelPart = safeLabel ? `-${safeLabel}` : '';
|
|
5616
5610
|
validatedComponent.id = `${name}${labelPart}-${index}`;
|
|
5617
5611
|
}
|
|
5618
|
-
console.warn(`[DfFormPreview] Fixed missing/invalid ID: ${validatedComponent.id}`);
|
|
5619
5612
|
}
|
|
5620
5613
|
else {
|
|
5621
5614
|
// ID is a valid string, check for duplicates
|
|
5622
5615
|
if (seenIds.has(validatedComponent.id)) {
|
|
5623
|
-
console.error(`[DfFormPreview] Duplicate component ID detected: ${validatedComponent.id}`);
|
|
5624
5616
|
// Generate a unique ID for duplicate - using index to keep it somewhat stable
|
|
5625
5617
|
validatedComponent.id = `${validatedComponent.id}-dup-${index}`;
|
|
5626
|
-
console.warn(`[DfFormPreview] Generated new unique ID: ${validatedComponent.id}`);
|
|
5627
5618
|
}
|
|
5628
5619
|
}
|
|
5629
5620
|
seenIds.add(validatedComponent.id);
|
|
@@ -5678,7 +5669,6 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5678
5669
|
validatedFormComponents = getValidatedComponents(localFormComponents);
|
|
5679
5670
|
// Synchronize local state if components were mutated (IDs added/fixed)
|
|
5680
5671
|
if (JSON.stringify(validatedFormComponents) !== JSON.stringify(localFormComponents)) {
|
|
5681
|
-
console.log('[DfFormPreview] Synchronizing local components after ID validation');
|
|
5682
5672
|
setLocalFormComponents(validatedFormComponents);
|
|
5683
5673
|
onFormDataChange?.(validatedFormComponents);
|
|
5684
5674
|
}
|
|
@@ -5731,13 +5721,229 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5731
5721
|
// Initialize notes and attachments state
|
|
5732
5722
|
setComponentNotes(prev => ({ ...initialNotes, ...prev }));
|
|
5733
5723
|
setComponentAttachments(prev => ({ ...initialAttachments, ...prev }));
|
|
5734
|
-
//
|
|
5735
|
-
|
|
5724
|
+
// Evaluate conditional logic on initialization
|
|
5725
|
+
evaluateConditionalLogic(initialValues);
|
|
5736
5726
|
}, [initialFormData, localFormComponents]);
|
|
5737
|
-
//
|
|
5738
|
-
|
|
5739
|
-
|
|
5727
|
+
// ======================== CONDITIONAL LOGIC ENGINE ========================
|
|
5728
|
+
// This is a pure visibility layer. It NEVER modifies component data.
|
|
5729
|
+
// It computes a { [componentId]: boolean } map based on each component's
|
|
5730
|
+
// `logic` property and the current form values.
|
|
5731
|
+
// Helper: Flatten all components (including nested) into a flat list
|
|
5732
|
+
const flattenAllComponents = useCallback((components) => {
|
|
5733
|
+
const flat = [];
|
|
5734
|
+
const traverse = (items) => {
|
|
5735
|
+
if (!items || !Array.isArray(items))
|
|
5736
|
+
return;
|
|
5737
|
+
for (const item of items) {
|
|
5738
|
+
flat.push(item);
|
|
5739
|
+
if (item.children && Array.isArray(item.children))
|
|
5740
|
+
traverse(item.children);
|
|
5741
|
+
if (item.cells && Array.isArray(item.cells)) {
|
|
5742
|
+
for (const row of item.cells) {
|
|
5743
|
+
const normalizedRow = normalizeTableRow(row);
|
|
5744
|
+
for (const cell of normalizedRow) {
|
|
5745
|
+
if (cell.components)
|
|
5746
|
+
traverse(cell.components);
|
|
5747
|
+
}
|
|
5748
|
+
}
|
|
5749
|
+
}
|
|
5750
|
+
if (item.entries && Array.isArray(item.entries)) {
|
|
5751
|
+
for (const entry of item.entries) {
|
|
5752
|
+
if (entry.components)
|
|
5753
|
+
traverse(entry.components);
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
if (item.templateComponents && Array.isArray(item.templateComponents)) {
|
|
5757
|
+
traverse(item.templateComponents);
|
|
5758
|
+
}
|
|
5759
|
+
}
|
|
5760
|
+
};
|
|
5761
|
+
traverse(components);
|
|
5762
|
+
return flat;
|
|
5763
|
+
}, []);
|
|
5764
|
+
// Helper: Get the value of a component referenced by a condition's `when` field.
|
|
5765
|
+
// The `when` field is typically a component LABEL (e.g. "Temperature"), not an ID.
|
|
5766
|
+
const getConditionComponentValue = useCallback((conditionWhen, allComponents, currentFormValues) => {
|
|
5767
|
+
// Try to find the component by label, id, or _id
|
|
5768
|
+
const comp = allComponents.find(c => c.basic?.label === conditionWhen ||
|
|
5769
|
+
ensureStringId$1(c.id) === conditionWhen ||
|
|
5770
|
+
ensureStringId$1(c._id) === conditionWhen);
|
|
5771
|
+
if (!comp)
|
|
5772
|
+
return undefined;
|
|
5773
|
+
const compId = ensureStringId$1(comp.id || comp._id);
|
|
5774
|
+
// Check formValues by ID first
|
|
5775
|
+
if (compId && currentFormValues[compId] !== undefined) {
|
|
5776
|
+
return currentFormValues[compId];
|
|
5777
|
+
}
|
|
5778
|
+
// Also check by label (main website stores values by label too)
|
|
5779
|
+
if (comp.basic?.label && currentFormValues[comp.basic.label] !== undefined) {
|
|
5780
|
+
return currentFormValues[comp.basic.label];
|
|
5781
|
+
}
|
|
5782
|
+
// Fall back to component's basic.value or defaultValue
|
|
5783
|
+
if (comp.basic?.value !== undefined && comp.basic?.value !== null && comp.basic?.value !== '') {
|
|
5784
|
+
return comp.basic.value;
|
|
5785
|
+
}
|
|
5786
|
+
if (comp.basic?.defaultValue !== undefined && comp.basic?.defaultValue !== null && comp.basic?.defaultValue !== '') {
|
|
5787
|
+
return comp.basic.defaultValue;
|
|
5788
|
+
}
|
|
5789
|
+
return undefined;
|
|
5790
|
+
}, []);
|
|
5791
|
+
// Helper: Evaluate a single condition
|
|
5792
|
+
const evaluateSingleCondition = useCallback((condition, componentValue) => {
|
|
5793
|
+
const { operator, value } = condition;
|
|
5794
|
+
// isEmpty / isNotEmpty
|
|
5795
|
+
if (operator === 'isEmpty') {
|
|
5796
|
+
if (componentValue == null)
|
|
5797
|
+
return true;
|
|
5798
|
+
if (typeof componentValue === 'string')
|
|
5799
|
+
return componentValue.trim() === '';
|
|
5800
|
+
if (Array.isArray(componentValue))
|
|
5801
|
+
return componentValue.length === 0;
|
|
5802
|
+
return false;
|
|
5803
|
+
}
|
|
5804
|
+
if (operator === 'isNotEmpty') {
|
|
5805
|
+
if (componentValue == null)
|
|
5806
|
+
return false;
|
|
5807
|
+
if (typeof componentValue === 'string')
|
|
5808
|
+
return componentValue.trim() !== '';
|
|
5809
|
+
if (Array.isArray(componentValue))
|
|
5810
|
+
return componentValue.length > 0;
|
|
5811
|
+
return true;
|
|
5812
|
+
}
|
|
5813
|
+
// checked / notChecked (for checkboxes)
|
|
5814
|
+
if (operator === 'checked' || operator === 'notChecked') {
|
|
5815
|
+
let isChecked = false;
|
|
5816
|
+
if (typeof componentValue === 'boolean')
|
|
5817
|
+
isChecked = componentValue;
|
|
5818
|
+
else if (typeof componentValue === 'string')
|
|
5819
|
+
isChecked = componentValue.toLowerCase() === 'true' || componentValue === '1' || componentValue.length > 0;
|
|
5820
|
+
else if (Array.isArray(componentValue))
|
|
5821
|
+
isChecked = componentValue.length > 0;
|
|
5822
|
+
else if (typeof componentValue === 'number')
|
|
5823
|
+
isChecked = componentValue > 0;
|
|
5824
|
+
return operator === 'checked' ? isChecked : !isChecked;
|
|
5825
|
+
}
|
|
5826
|
+
// equals / notEquals
|
|
5827
|
+
if (operator === 'equals' || operator === 'notEquals') {
|
|
5828
|
+
let isEqual = false;
|
|
5829
|
+
// Loose comparison
|
|
5830
|
+
if (componentValue == value)
|
|
5831
|
+
isEqual = true;
|
|
5832
|
+
// String comparison (case-insensitive, trimmed)
|
|
5833
|
+
if (!isEqual && componentValue != null && value != null) {
|
|
5834
|
+
const strA = String(componentValue).trim();
|
|
5835
|
+
const strB = String(value).trim();
|
|
5836
|
+
// Numeric comparison within strings
|
|
5837
|
+
if (strA !== '' && strB !== '' && !isNaN(Number(strA)) && !isNaN(Number(strB))) {
|
|
5838
|
+
isEqual = Number(strA) === Number(strB);
|
|
5839
|
+
}
|
|
5840
|
+
if (!isEqual) {
|
|
5841
|
+
isEqual = strA.toLowerCase() === strB.toLowerCase();
|
|
5842
|
+
}
|
|
5843
|
+
}
|
|
5844
|
+
// Array contains check (for multi-select)
|
|
5845
|
+
if (!isEqual && Array.isArray(componentValue)) {
|
|
5846
|
+
isEqual = componentValue.some(v => String(v).trim().toLowerCase() === String(value).trim().toLowerCase());
|
|
5847
|
+
}
|
|
5848
|
+
return operator === 'equals' ? isEqual : !isEqual;
|
|
5849
|
+
}
|
|
5850
|
+
// contains / notContains
|
|
5851
|
+
if (operator === 'contains' || operator === 'notContains') {
|
|
5852
|
+
let doesContain = false;
|
|
5853
|
+
if (typeof componentValue === 'string' && value != null) {
|
|
5854
|
+
doesContain = componentValue.trim().toLowerCase().includes(String(value).trim().toLowerCase());
|
|
5855
|
+
}
|
|
5856
|
+
else if (Array.isArray(componentValue) && value != null) {
|
|
5857
|
+
doesContain = componentValue.some(v => String(v).trim().toLowerCase() === String(value).trim().toLowerCase());
|
|
5858
|
+
}
|
|
5859
|
+
return operator === 'contains' ? doesContain : !doesContain;
|
|
5860
|
+
}
|
|
5861
|
+
// Numeric comparisons
|
|
5862
|
+
if (['greaterThan', 'lessThan', 'greaterThanOrEqual', 'lessThanOrEqual'].includes(operator)) {
|
|
5863
|
+
const numA = typeof componentValue === 'number' ? componentValue : parseFloat(String(componentValue));
|
|
5864
|
+
const numB = typeof value === 'number' ? value : parseFloat(String(value));
|
|
5865
|
+
if (isNaN(numA) || isNaN(numB))
|
|
5866
|
+
return false;
|
|
5867
|
+
switch (operator) {
|
|
5868
|
+
case 'greaterThan': return numA > numB;
|
|
5869
|
+
case 'lessThan': return numA < numB;
|
|
5870
|
+
case 'greaterThanOrEqual': return numA >= numB;
|
|
5871
|
+
case 'lessThanOrEqual': return numA <= numB;
|
|
5872
|
+
default: return false;
|
|
5873
|
+
}
|
|
5874
|
+
}
|
|
5875
|
+
return false;
|
|
5740
5876
|
}, []);
|
|
5877
|
+
// Main conditional logic evaluation function
|
|
5878
|
+
const evaluateConditionalLogic = useCallback((explicitValues) => {
|
|
5879
|
+
const currentComponents = localFormComponents;
|
|
5880
|
+
const currentValues = explicitValues || formValues;
|
|
5881
|
+
if (!currentComponents || currentComponents.length === 0)
|
|
5882
|
+
return;
|
|
5883
|
+
const allComponents = flattenAllComponents(currentComponents);
|
|
5884
|
+
const visibility = {};
|
|
5885
|
+
allComponents.forEach(component => {
|
|
5886
|
+
const compId = ensureStringId$1(component.id || component._id);
|
|
5887
|
+
if (!compId)
|
|
5888
|
+
return;
|
|
5889
|
+
// Container components are ALWAYS visible — their children handle their own logic
|
|
5890
|
+
if (['table', 'datagrid', 'section', 'heading', 'instructions'].includes(component.name)) {
|
|
5891
|
+
visibility[compId] = true;
|
|
5892
|
+
return;
|
|
5893
|
+
}
|
|
5894
|
+
// Get the logic property (API sends it as `logic`, builder uses `conditional`)
|
|
5895
|
+
const logic = component.logic || component.conditional;
|
|
5896
|
+
// No logic or action is 'always' → always show
|
|
5897
|
+
if (!logic || logic.action === 'always' || !logic.conditions || logic.conditions.length === 0) {
|
|
5898
|
+
visibility[compId] = true;
|
|
5899
|
+
return;
|
|
5900
|
+
}
|
|
5901
|
+
// Evaluate each condition
|
|
5902
|
+
const conditionResults = logic.conditions.map((condition) => {
|
|
5903
|
+
const componentValue = getConditionComponentValue(condition.when, allComponents, currentValues);
|
|
5904
|
+
return evaluateSingleCondition(condition, componentValue);
|
|
5905
|
+
});
|
|
5906
|
+
// Determine if conditions are met based on 'when' (all/any)
|
|
5907
|
+
let conditionsMet;
|
|
5908
|
+
if (logic.when === 'any') {
|
|
5909
|
+
conditionsMet = conditionResults.some((r) => r);
|
|
5910
|
+
}
|
|
5911
|
+
else {
|
|
5912
|
+
// Default to 'all'
|
|
5913
|
+
conditionsMet = conditionResults.every((r) => r);
|
|
5914
|
+
}
|
|
5915
|
+
// Apply action
|
|
5916
|
+
if (logic.action === 'show') {
|
|
5917
|
+
visibility[compId] = conditionsMet;
|
|
5918
|
+
}
|
|
5919
|
+
else if (logic.action === 'hide') {
|
|
5920
|
+
visibility[compId] = !conditionsMet;
|
|
5921
|
+
}
|
|
5922
|
+
else {
|
|
5923
|
+
visibility[compId] = true;
|
|
5924
|
+
}
|
|
5925
|
+
});
|
|
5926
|
+
// Only update state if visibility actually changed (prevent re-render loops)
|
|
5927
|
+
setComponentVisibility(prev => {
|
|
5928
|
+
const prevStr = JSON.stringify(prev);
|
|
5929
|
+
const newStr = JSON.stringify(visibility);
|
|
5930
|
+
if (prevStr === newStr)
|
|
5931
|
+
return prev;
|
|
5932
|
+
return visibility;
|
|
5933
|
+
});
|
|
5934
|
+
}, [localFormComponents, formValues, flattenAllComponents, getConditionComponentValue, evaluateSingleCondition]);
|
|
5935
|
+
// Re-evaluate conditional logic whenever form values change
|
|
5936
|
+
useEffect(() => {
|
|
5937
|
+
evaluateConditionalLogic();
|
|
5938
|
+
}, [formValues, evaluateConditionalLogic]);
|
|
5939
|
+
// Check if a component should be visible
|
|
5940
|
+
const shouldShowComponent = useCallback((componentId) => {
|
|
5941
|
+
// In preview mode (read-only submission view), always show everything
|
|
5942
|
+
if (isPreviewMode)
|
|
5943
|
+
return true;
|
|
5944
|
+
// Default to visible if not explicitly set to false
|
|
5945
|
+
return componentVisibility[componentId] !== false;
|
|
5946
|
+
}, [componentVisibility, isPreviewMode]);
|
|
5741
5947
|
// Handle form value changes and re-evaluate conditional logic
|
|
5742
5948
|
const onFormValueChange = useCallback((change) => {
|
|
5743
5949
|
// CRITICAL: Validate that change.id is valid and unique
|
|
@@ -5789,6 +5995,13 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
5789
5995
|
// Only update formValues for actual value changes, not structure updates
|
|
5790
5996
|
if (!isStructureUpdate) {
|
|
5791
5997
|
newFormValues[change.id] = change.value;
|
|
5998
|
+
// CRITICAL for conditional logic: Also store value under the component's label.
|
|
5999
|
+
// Conditions reference components by label (e.g. "Temperature"), not by ID.
|
|
6000
|
+
const allComponents = flattenAllComponents(localFormComponents);
|
|
6001
|
+
const targetComp = allComponents.find(c => ensureStringId$1(c.id || c._id) === change.id);
|
|
6002
|
+
if (targetComp?.basic?.label) {
|
|
6003
|
+
newFormValues[targetComp.basic.label] = change.value;
|
|
6004
|
+
}
|
|
5792
6005
|
setFormValues(newFormValues);
|
|
5793
6006
|
}
|
|
5794
6007
|
// Clear raised issues for this component when value changes
|
|
@@ -6529,7 +6742,6 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
6529
6742
|
componentCounterRef.current += 1;
|
|
6530
6743
|
cachedId = `generated-${name}-${componentCounterRef.current}-${Math.random().toString(36).substr(2, 9)}`;
|
|
6531
6744
|
componentIdCacheRef.current.set(cacheKey, cachedId);
|
|
6532
|
-
console.warn('[DfFormPreview] Generated and cached ID for component:', cachedId, 'key:', cacheKey);
|
|
6533
6745
|
}
|
|
6534
6746
|
finalComponentId = cachedId;
|
|
6535
6747
|
// Create a new component object with the cached ID (don't mutate original)
|
|
@@ -6545,9 +6757,7 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
6545
6757
|
if (formValue !== undefined) {
|
|
6546
6758
|
// Check if this value is being used by multiple components
|
|
6547
6759
|
const componentsWithSameValue = localFormComponents.filter(comp => comp.id !== componentId && formValues[comp.id] === formValue);
|
|
6548
|
-
if (componentsWithSameValue.length > 0)
|
|
6549
|
-
console.warn(`[DfFormPreview] Component ${componentId} shares form value with other components:`, componentsWithSameValue.map(c => c.id));
|
|
6550
|
-
}
|
|
6760
|
+
if (componentsWithSameValue.length > 0) ;
|
|
6551
6761
|
}
|
|
6552
6762
|
const commonProps = {
|
|
6553
6763
|
id: componentId, // Use the validated component ID
|
|
@@ -6760,10 +6970,8 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
6760
6970
|
}, onValueChange: (change) => {
|
|
6761
6971
|
const changeId = ensureStringId$1(change.id);
|
|
6762
6972
|
const componentId = ensureStringId$1(component.id);
|
|
6763
|
-
console.log(`[DfFormPreview] datagrid onValueChange - Target: ${changeId}, Component: ${componentId}`);
|
|
6764
6973
|
// Handle datagrid value changes (entries updates)
|
|
6765
6974
|
if (changeId === componentId && change.value && typeof change.value === 'object' && 'entries' in change.value) {
|
|
6766
|
-
console.log('[DfFormPreview] datagrid entries update - entries count:', change.value.entries?.length);
|
|
6767
6975
|
// Update localFormComponents with new entries structure
|
|
6768
6976
|
const updatedComponents = localFormComponents.map(comp => {
|
|
6769
6977
|
const currentCompId = ensureStringId$1(comp.id);
|
|
@@ -6806,7 +7014,6 @@ onComponentSelect, onComponentDelete, onComponentEdit, onComponentUpdate, select
|
|
|
6806
7014
|
}
|
|
6807
7015
|
});
|
|
6808
7016
|
if (valuesChanged) {
|
|
6809
|
-
console.log('[DfFormPreview] Initializing form values for new datagrid entries');
|
|
6810
7017
|
setFormValues(newValues);
|
|
6811
7018
|
}
|
|
6812
7019
|
}
|