diff --git a/src/app/configuration/actionBuilder/components/ActionTypeFormModal.tsx b/src/app/configuration/actionBuilder/components/ActionTypeFormModal.tsx index 2541f4a..a62285f 100644 --- a/src/app/configuration/actionBuilder/components/ActionTypeFormModal.tsx +++ b/src/app/configuration/actionBuilder/components/ActionTypeFormModal.tsx @@ -1,4 +1,4 @@ -import { XCircleIcon } from '@phosphor-icons/react'; +import { useEffect } from 'react'; import { CustomModal, @@ -7,6 +7,7 @@ import { CustomTextArea, CustomSwitch, CustomButton, + CustomAlertBanner, } from '../../../../components/custom'; import type { ActionCategory, ActionType, ActionTypeFormData } from '../ActionBuilderTypes'; @@ -17,6 +18,7 @@ interface ActionTypeFormModalProps { formData: ActionTypeFormData; setFormData: React.Dispatch>; error: string | null; + setError?: (err: string | null) => void; onClose: () => void; onSubmit: (e: React.FormEvent) => void; } @@ -28,9 +30,21 @@ export function ActionTypeFormModal({ formData, setFormData, error, + setError, onClose, onSubmit, }: ActionTypeFormModalProps) { + useEffect(() => { + if (!error || !setError) return; + if (error === 'Please select a Category.' && formData.categoryId) { + setError(null); + } else if (error === 'Action Type Name is required.' && formData.name.trim()) { + setError(null); + } else if (error === 'Action Type Code is required.' && formData.code.trim()) { + setError(null); + } + }, [formData.categoryId, formData.name, formData.code, error, setError]); + return ( {error && ( -
- - {error} -
+ setError?.(null)} + autoClose={false} + /> )} -
+
@@ -73,6 +94,9 @@ export function ActionTypeFormModal({ name: nameVal, code: editingType ? prev.code : nameVal.toLowerCase().replace(/\s+/g, '_'), })); + if (setError && nameVal.trim()) { + setError(null); + } }} placeholder="e.g. Award Miles" /> @@ -84,7 +108,13 @@ export function ActionTypeFormModal({ setFormData((prev) => ({ ...prev, code: e.target.value }))} + onChange={(e) => { + const codeVal = e.target.value; + setFormData((prev) => ({ ...prev, code: codeVal })); + if (setError && codeVal.trim()) { + setError(null); + } + }} placeholder="e.g. award_miles" />
diff --git a/src/app/configuration/actionBuilder/components/ActionTypesColumn.tsx b/src/app/configuration/actionBuilder/components/ActionTypesColumn.tsx index 88b06d0..6ee8066 100644 --- a/src/app/configuration/actionBuilder/components/ActionTypesColumn.tsx +++ b/src/app/configuration/actionBuilder/components/ActionTypesColumn.tsx @@ -88,9 +88,23 @@ export function ActionTypesColumn({ : 'bg-slate-50/80 hover:bg-slate-100/70 border border-gray-100 text-slate-800' }`} > -
-
- {t.name} +
+
+ + {t.name} + +
{t.code} diff --git a/src/app/configuration/actionBuilder/components/CategoriesColumn.tsx b/src/app/configuration/actionBuilder/components/CategoriesColumn.tsx index 0ea9b72..b850684 100644 --- a/src/app/configuration/actionBuilder/components/CategoriesColumn.tsx +++ b/src/app/configuration/actionBuilder/components/CategoriesColumn.tsx @@ -75,13 +75,26 @@ export function CategoriesColumn({ key={cat.id} onClick={() => onSelectCategory(cat.id)} className={`group relative p-3.5 rounded-[14px] flex items-center justify-between cursor-pointer transition-all duration-150 ${isSelected - ? 'bg-[#1E7D5C] text-white shadow-sm' - : 'bg-slate-50/80 hover:bg-slate-100/70 border border-gray-100 text-slate-800' + ? 'bg-[#1E7D5C] text-white shadow-sm' + : 'bg-slate-50/80 hover:bg-slate-100/70 border border-gray-100 text-slate-800' }`} > -
-
- {cat.name} +
+
+ + {cat.name} + +
{cat.code} @@ -114,8 +127,8 @@ export function CategoriesColumn({ {/* Count Badge */}
{childCount} diff --git a/src/app/configuration/actionBuilder/components/CategoryFormModal.tsx b/src/app/configuration/actionBuilder/components/CategoryFormModal.tsx index 5bebd02..fc8c900 100644 --- a/src/app/configuration/actionBuilder/components/CategoryFormModal.tsx +++ b/src/app/configuration/actionBuilder/components/CategoryFormModal.tsx @@ -1,4 +1,4 @@ -import { XCircleIcon } from '@phosphor-icons/react'; +import { useEffect } from 'react'; import { CustomModal, @@ -6,6 +6,7 @@ import { CustomTextArea, CustomSwitch, CustomButton, + CustomAlertBanner, } from '../../../../components/custom'; import type { ActionCategory, ActionCategoryFormData } from '../ActionBuilderTypes'; @@ -15,6 +16,7 @@ interface CategoryFormModalProps { formData: ActionCategoryFormData; setFormData: React.Dispatch>; error: string | null; + setError?: (err: string | null) => void; onClose: () => void; onSubmit: (e: React.FormEvent) => void; } @@ -25,9 +27,19 @@ export function CategoryFormModal({ formData, setFormData, error, + setError, onClose, onSubmit, }: CategoryFormModalProps) { + useEffect(() => { + if (!error || !setError) return; + if (error === 'Category Name is required.' && formData.name.trim()) { + setError(null); + } else if (error === 'Category Code is required.' && formData.code.trim()) { + setError(null); + } + }, [formData.name, formData.code, error, setError]); + return ( {error && ( -
- - {error} -
+ setError?.(null)} + autoClose={false} + /> )} - +
setFormData((prev) => ({ ...prev, code: e.target.value }))} + onChange={(e) => { + const codeVal = e.target.value; + setFormData((prev) => ({ ...prev, code: codeVal })); + if (setError && codeVal.trim()) { + setError(null); + } + }} placeholder="e.g. refunds" />
diff --git a/src/app/configuration/actionBuilder/components/FieldDefinitionFormModal.tsx b/src/app/configuration/actionBuilder/components/FieldDefinitionFormModal.tsx index e888c53..2b57fd7 100644 --- a/src/app/configuration/actionBuilder/components/FieldDefinitionFormModal.tsx +++ b/src/app/configuration/actionBuilder/components/FieldDefinitionFormModal.tsx @@ -1,4 +1,4 @@ -import { XCircleIcon } from '@phosphor-icons/react'; +import { useEffect } from 'react'; import { CustomModal, @@ -7,6 +7,7 @@ import { CustomTextArea, CustomSwitch, CustomButton, + CustomAlertBanner, } from '../../../../components/custom'; import type { FieldDefinition, @@ -52,6 +53,7 @@ interface FieldDefinitionFormModalProps { formData: FieldDefinitionFormData; setFormData: React.Dispatch>; error: string | null; + setError?: (err: string | null) => void; onClose: () => void; onSubmit: (e: React.FormEvent) => void; } @@ -64,9 +66,24 @@ export function FieldDefinitionFormModal({ formData, setFormData, error, + setError, onClose, onSubmit, }: FieldDefinitionFormModalProps) { + useEffect(() => { + if (!error || !setError) return; + if (error === 'Field Name is required.' && formData.fieldName.trim()) { + setError(null); + } else if (error === 'Field Code is required.' && formData.fieldCode.trim()) { + setError(null); + } else if ( + error === 'Master Data Lookup Source is required for Dropdown or Multi-Select control types.' && + formData.lookupSource?.trim() + ) { + setError(null); + } + }, [formData.fieldName, formData.fieldCode, formData.lookupSource, error, setError]); + const dependentOptions = [ { label: 'None (No Dependency)', value: '' }, ...fields @@ -86,187 +103,187 @@ export function FieldDefinitionFormModal({ size="lg" > {error && ( -
- - {error} -
+ setError?.(null)} + autoClose={false} + /> )} - +
-
- - { - const val = e.target.value; - setFormData((prev) => ({ - ...prev, - fieldName: val, - fieldCode: editingField ? prev.fieldCode : val.toLowerCase().replace(/[^a-z0-9_]+/g, '_'), - })); - }} - placeholder="e.g. Field Name" - /> -
+ { + const val = e.target.value; + setFormData((prev) => ({ + ...prev, + fieldName: val, + fieldCode: editingField ? prev.fieldCode : val.toLowerCase().replace(/[^a-z0-9_]+/g, '_'), + })); + if (setError && val.trim()) { + setError(null); + } + }} + placeholder="e.g. Field Name" + /> -
- - setFormData((prev) => ({ ...prev, fieldCode: e.target.value }))} - placeholder="e.g. field_code" - /> -
+ { + const val = e.target.value; + setFormData((prev) => ({ ...prev, fieldCode: val })); + if (setError && val.trim()) { + setError(null); + } + }} + placeholder="e.g. field_code" + /> -
- - setFormData((prev) => ({ ...prev, fieldType: val as FieldType }))} - placeholder="Select Control Type..." - /> -
-
- -
-
- - setFormData((prev) => ({ ...prev, lookupSource: val }))} - placeholder="Select Lookup Source..." - /> -
- -
- - setFormData((prev) => ({ ...prev, width: val as FieldWidth }))} - /> -
- -
- - setFormData((prev) => ({ ...prev, section: e.target.value }))} - placeholder="e.g. General Information" - /> -
-
- -
-
- - setFormData((prev) => ({ ...prev, placeholder: e.target.value }))} - placeholder="e.g. Enter value..." - /> -
- -
- - setFormData((prev) => ({ ...prev, defaultValue: e.target.value }))} - placeholder="e.g. Default" - /> -
- -
- - setFormData((prev) => ({ ...prev, displayOrder: parseInt(e.target.value) || 1 }))} - /> -
-
- -
- - setFormData((prev) => ({ ...prev, helpText: e.target.value }))} - placeholder="e.g. Instructions for end users filling this field" - rows={2} + { + const newType = val as FieldType; + const isLookupType = newType === 'dropdown' || newType === 'multi_select'; + setFormData((prev) => ({ + ...prev, + fieldType: newType, + lookupSource: isLookupType ? prev.lookupSource : undefined, + })); + if (setError && !isLookupType) { + setError(null); + } + }} + placeholder="Select Control Type..." />
+
+ {(formData.fieldType === 'dropdown' || formData.fieldType === 'multi_select') && ( + { + setFormData((prev) => ({ ...prev, lookupSource: val })); + if (setError && val?.trim()) { + setError(null); + } + }} + placeholder="Select Lookup Source..." + /> + )} + + setFormData((prev) => ({ ...prev, width: val as FieldWidth }))} + /> + + setFormData((prev) => ({ ...prev, section: e.target.value }))} + placeholder="e.g. General Information" + /> +
+ +
+ setFormData((prev) => ({ ...prev, placeholder: e.target.value }))} + placeholder="e.g. Enter value..." + /> + + setFormData((prev) => ({ ...prev, defaultValue: e.target.value }))} + placeholder="e.g. Default" + /> + + setFormData((prev) => ({ ...prev, displayOrder: parseInt(e.target.value) || 1 }))} + /> +
+ + setFormData((prev) => ({ ...prev, helpText: e.target.value }))} + placeholder="e.g. Instructions for end users filling this field" + rows={2} + /> + {/* Validation JSON Rules */}
Validation Rules (validation_json)
-
- - - setFormData((prev) => ({ - ...prev, - validationJson: { - ...prev.validationJson, - min: e.target.value !== '' ? Number(e.target.value) : undefined, - }, - })) - } - placeholder="e.g. 312" - /> -
+ + setFormData((prev) => ({ + ...prev, + validationJson: { + ...prev.validationJson, + min: e.target.value !== '' ? Number(e.target.value) : undefined, + }, + })) + } + placeholder="e.g. 312" + /> -
- - - setFormData((prev) => ({ - ...prev, - validationJson: { - ...prev.validationJson, - max: e.target.value !== '' ? Number(e.target.value) : undefined, - }, - })) - } - placeholder="e.g. 245" - /> -
+ + setFormData((prev) => ({ + ...prev, + validationJson: { + ...prev.validationJson, + max: e.target.value !== '' ? Number(e.target.value) : undefined, + }, + })) + } + placeholder="e.g. 245" + /> -
- - - setFormData((prev) => ({ - ...prev, - validationJson: { - ...prev.validationJson, - regex: e.target.value, - }, - })) - } - placeholder="e.g. ^[A-Z0-9]+$" - /> -
+ + setFormData((prev) => ({ + ...prev, + validationJson: { + ...prev.validationJson, + regex: e.target.value, + }, + })) + } + placeholder="e.g. ^[A-Z0-9]+$" + />
@@ -274,61 +291,55 @@ export function FieldDefinitionFormModal({
Conditional Visibility (visibility_condition_json)
-
- - - setFormData((prev) => ({ - ...prev, - visibilityConditionJson: { - ...(prev.visibilityConditionJson || { operator: 'equals', value: '' }), - field: val, - }, - })) - } - placeholder="Select dependent field..." - /> -
+ + setFormData((prev) => ({ + ...prev, + visibilityConditionJson: { + ...(prev.visibilityConditionJson || { operator: 'equals', value: '' }), + field: val, + }, + })) + } + placeholder="Select dependent field..." + /> -
- - - setFormData((prev) => ({ - ...prev, - visibilityConditionJson: { - ...(prev.visibilityConditionJson || { field: '', value: '' }), - operator: val as any, - }, - })) - } - /> -
+ + setFormData((prev) => ({ + ...prev, + visibilityConditionJson: { + ...(prev.visibilityConditionJson || { field: '', value: '' }), + operator: val as any, + }, + })) + } + /> -
- - - setFormData((prev) => ({ - ...prev, - visibilityConditionJson: { - ...(prev.visibilityConditionJson || { field: '', operator: 'equals' }), - value: e.target.value, - }, - })) - } - placeholder="e.g. DGYRHTFUJKYHUSDERTYUIK" - /> -
+ + setFormData((prev) => ({ + ...prev, + visibilityConditionJson: { + ...(prev.visibilityConditionJson || { field: '', operator: 'equals' }), + value: e.target.value, + }, + })) + } + placeholder="e.g. DGYRHTFUJKYHUSDERTYUIK" + />
@@ -353,6 +364,7 @@ export function FieldDefinitionFormModal({
setIsCategoryModalOpen(false)} + setError={setCategoryError} + onClose={() => { + setCategoryError(null); + setIsCategoryModalOpen(false); + }} onSubmit={handleSaveCategory} /> @@ -598,7 +609,11 @@ export default function ActionBuilderPage() { formData={typeFormData} setFormData={setTypeFormData} error={typeError} - onClose={() => setIsTypeModalOpen(false)} + setError={setTypeError} + onClose={() => { + setTypeError(null); + setIsTypeModalOpen(false); + }} onSubmit={handleSaveType} /> @@ -610,7 +625,11 @@ export default function ActionBuilderPage() { formData={fieldFormData} setFormData={setFieldFormData} error={fieldError} - onClose={() => setIsFieldModalOpen(false)} + setError={setFieldError} + onClose={() => { + setFieldError(null); + setIsFieldModalOpen(false); + }} onSubmit={handleSaveField} /> diff --git a/src/app/configuration/masterData/components/MasterItemFormModal.tsx b/src/app/configuration/masterData/components/MasterItemFormModal.tsx index 3d7c5ec..c77104c 100644 --- a/src/app/configuration/masterData/components/MasterItemFormModal.tsx +++ b/src/app/configuration/masterData/components/MasterItemFormModal.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { XCircleIcon } from '@phosphor-icons/react'; import { CustomModal, CustomInput, CustomSwitch, CustomButton, + CustomAlertBanner, } from '../../../../components/custom'; import type { MasterDataCategoryItem, @@ -21,6 +21,7 @@ interface MasterItemFormModalProps { setFormData: React.Dispatch>; onSubmit: (e: React.FormEvent) => void; errorMsg: string | null; + setErrorMsg?: (err: string | null) => void; } export const MasterItemFormModal: React.FC = ({ @@ -32,7 +33,15 @@ export const MasterItemFormModal: React.FC = ({ setFormData, onSubmit, errorMsg, + setErrorMsg, }) => { + React.useEffect(() => { + if (!errorMsg || !setErrorMsg) return; + if (errorMsg === 'Value / Name is required.' && formData.value.trim()) { + setErrorMsg(null); + } + }, [formData.value, errorMsg, setErrorMsg]); + return ( = ({ size="md" > {errorMsg && ( -
- - {errorMsg} -
+ setErrorMsg?.(null)} + autoClose={false} + /> )} - +
setFormData((prev) => ({ ...prev, code: e.target.value }))} + onChange={(e) => { + const codeVal = e.target.value; + setFormData((prev) => ({ ...prev, code: codeVal })); + if (setErrorMsg && codeVal.trim()) { + setErrorMsg(null); + } + }} placeholder="e.g. platinum_tier" />
diff --git a/src/app/configuration/masterData/index.tsx b/src/app/configuration/masterData/index.tsx index f7f70db..965cb9a 100644 --- a/src/app/configuration/masterData/index.tsx +++ b/src/app/configuration/masterData/index.tsx @@ -247,6 +247,7 @@ export default function MasterDataManagement() { setFormData={setFormData} onSubmit={handleSubmit} errorMsg={errorMsg} + setErrorMsg={setErrorMsg} /> {/* Delete Confirmation Modal */} diff --git a/src/app/policyEngine/PolicyEngineApi.ts b/src/app/policyEngine/PolicyEngineApi.ts index 340fffb..1101d9c 100644 --- a/src/app/policyEngine/PolicyEngineApi.ts +++ b/src/app/policyEngine/PolicyEngineApi.ts @@ -26,6 +26,12 @@ export interface ActionTypeField { section?: string; displayOrder?: number; isActive: boolean; + validationJson?: any; + visibilityConditionJson?: { + field: string; + operator: 'equals' | 'not_equals' | 'contains'; + value: any; + }; } // ─── Local Direct Api Helpers for Self-Containment ─────────────────────────── diff --git a/src/app/policyEngine/components/AddPolicyEngine.tsx b/src/app/policyEngine/components/AddPolicyEngine.tsx index 019527d..ca638c8 100644 --- a/src/app/policyEngine/components/AddPolicyEngine.tsx +++ b/src/app/policyEngine/components/AddPolicyEngine.tsx @@ -45,8 +45,113 @@ const WIDTH_GRID_MAP: Record = { two_thirds: 'col-span-12 md:col-span-8', }; +function parseVisibilityCondition(visCond: any): { field?: string; operator?: string; value?: any } | null { + if (!visCond) return null; + if (typeof visCond === 'string') { + try { + return JSON.parse(visCond); + } catch { + return null; + } + } + if (typeof visCond === 'object') { + return visCond; + } + return null; +} +function isActionFieldVisible( + field: ActionTypeField, + fieldValues?: Record, + allFields?: ActionTypeField[], + fieldLookupOptionsMap?: Record +): boolean { + const visCond = parseVisibilityCondition( + field.visibilityConditionJson || (field as any).visibility_condition_json + ); + if (!visCond || !visCond.field) return true; + const targetFieldCode = visCond.field; + const operator = visCond.operator || 'equals'; + const expectedValue = visCond.value; + + const targetField = allFields?.find((f) => f.fieldCode === targetFieldCode); + const rawActual = + fieldValues?.[targetFieldCode] !== undefined && fieldValues?.[targetFieldCode] !== null + ? fieldValues[targetFieldCode] + : targetField?.defaultValue; + + const actualValue = + rawActual && typeof rawActual === 'object' && 'amount' in rawActual + ? rawActual.amount + : rawActual; + + const candidateValues: string[] = []; + if (actualValue !== undefined && actualValue !== null) { + if (Array.isArray(actualValue)) { + actualValue.forEach((v) => candidateValues.push(String(v))); + } else { + candidateValues.push(String(actualValue)); + } + + if (fieldLookupOptionsMap) { + Object.values(fieldLookupOptionsMap).forEach((opts) => { + opts.forEach((opt) => { + if ( + candidateValues.some( + (c) => + c.toLowerCase() === String(opt.value || '').toLowerCase() || + c.toLowerCase() === String(opt.id || '').toLowerCase() || + c.toLowerCase() === String(opt.code || '').toLowerCase() || + c.toLowerCase() === String(opt.label || '').toLowerCase() + ) + ) { + if (opt.value) candidateValues.push(String(opt.value)); + if (opt.code) candidateValues.push(String(opt.code)); + if (opt.label) candidateValues.push(String(opt.label)); + if (opt.id) candidateValues.push(String(opt.id)); + } + }); + }); + } + } + + const normalizeStr = (s: any) => + String(s ?? '') + .trim() + .toLowerCase() + .replace(/[\s_-]+/g, ''); + + const normExpected = normalizeStr(expectedValue); + + if (operator === 'equals') { + if (typeof expectedValue === 'boolean') { + return Boolean(actualValue) === expectedValue; + } + if (expectedValue === '' || expectedValue === undefined || expectedValue === null) { + return actualValue === '' || actualValue === undefined || actualValue === null; + } + return candidateValues.some((c) => normalizeStr(c) === normExpected); + } + + if (operator === 'not_equals') { + if (typeof expectedValue === 'boolean') { + return Boolean(actualValue) !== expectedValue; + } + return !candidateValues.some((c) => normalizeStr(c) === normExpected); + } + + if (operator === 'contains') { + if (typeof expectedValue === 'boolean') { + return Boolean(actualValue) === expectedValue; + } + return candidateValues.some( + (c) => normalizeStr(c).includes(normExpected) || normExpected.includes(normalizeStr(c)) + ); + } + + return true; +} // ─── Types ─────────────────────────────────────────────────────────────────── @@ -634,7 +739,18 @@ export default function AddPolicyEngine() { const curr = typeof val === 'object' ? val.currency : undefined; valObj.numberValue = parseFloat(amt) || 0; if (curr) { - valObj.currencyCodeId = curr; + const lookupOpts = + fieldLookupOptionsMap[fieldDef?.lookupSource || 'currency'] || + fieldLookupOptionsMap['currency'] || + []; + const matchedCurr = lookupOpts.find( + (o) => o.value === curr || o.id === curr || o.code === curr + ); + valObj.currencyCodeId = + matchedCurr?.id || + (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(String(curr)) + ? String(curr) + : undefined); } } else if (fieldType === 'checkbox' || fieldType === 'switch' || typeof val === 'boolean') { valObj.booleanValue = !!val; @@ -825,7 +941,7 @@ export default function AddPolicyEngine() { {/* ─── Body Content Skeleton ──────────────────────────────────────── */}
- + {/* Policy Information Card */}
@@ -1255,116 +1371,136 @@ export default function AddPolicyEngine() {
- ) : (actionTypeFieldsMap[action.actionTypeId] || []).length === 0 ? ( -

No dynamic fields configured for this Action Type.

- ) : ( -
- {(actionTypeFieldsMap[action.actionTypeId] || []).map((field) => { - const widthClass = WIDTH_GRID_MAP[field.width || 'full'] || 'col-span-12'; - const fieldVal = action.fieldValues?.[field.fieldCode]; - const lookupOpts = fieldLookupOptionsMap[field.lookupSource || ''] || []; + ) : (() => { + const allFields = actionTypeFieldsMap[action.actionTypeId] || []; + const visibleFields = allFields.filter((f) => + isActionFieldVisible(f, action.fieldValues, allFields, fieldLookupOptionsMap) + ); - return ( -
- + if (allFields.length === 0) { + return

No dynamic fields configured for this Action Type.

; + } - {field.fieldType === 'textarea' ? ( - handleFieldValueChange(rule.id, action.id, field.fieldCode, e.target.value)} - placeholder={field.placeholder || 'Enter details...'} - /> - ) : field.fieldType === 'currency' ? ( -
-
- + if (visibleFields.length === 0) { + return null; + } + + return ( +
+ {visibleFields.map((field) => { + const widthClass = WIDTH_GRID_MAP[field.width || 'full'] || 'col-span-12'; + const fieldVal = action.fieldValues?.[field.fieldCode]; + const lookupOpts = fieldLookupOptionsMap[field.lookupSource || ''] || []; + const helpText = field.helpText || (field as any).help_text; + + return ( +
+ + + {field.fieldType === 'textarea' ? ( + handleFieldValueChange(rule.id, action.id, field.fieldCode, e.target.value)} + placeholder={field.placeholder || 'Enter details...'} + /> + ) : field.fieldType === 'currency' ? ( +
+
+ + handleFieldValueChange(rule.id, action.id, field.fieldCode, { + ...(fieldVal || {}), + amount: e.target.value, + }) + } + placeholder={field.placeholder || 'Amount'} + /> +
+ handleFieldValueChange(rule.id, action.id, field.fieldCode, { ...(fieldVal || {}), - amount: e.target.value, + currency: val, }) } - placeholder={field.placeholder || 'Amount'} />
+ ) : field.fieldType === 'dropdown' ? ( - handleFieldValueChange(rule.id, action.id, field.fieldCode, { - ...(fieldVal || {}), - currency: val, - }) - } + value={fieldVal || ''} + onChange={(val) => handleFieldValueChange(rule.id, action.id, field.fieldCode, val)} + placeholder={field.placeholder || 'Select option...'} /> -
- ) : field.fieldType === 'dropdown' ? ( - handleFieldValueChange(rule.id, action.id, field.fieldCode, val)} - placeholder={field.placeholder || 'Select option...'} - /> - ) : field.fieldType === 'multi_select' ? ( - handleFieldValueChange(rule.id, action.id, field.fieldCode, vals)} - placeholder={field.placeholder || 'Select multiple options...'} - /> - ) : field.fieldType === 'checkbox' ? ( - handleFieldValueChange(rule.id, action.id, field.fieldCode, e.target.checked)} - label={field.helpText || field.fieldName} - /> - ) : field.fieldType === 'switch' ? ( -
- handleFieldValueChange(rule.id, action.id, field.fieldCode, vals)} + placeholder={field.placeholder || 'Select multiple options...'} + /> + ) : field.fieldType === 'checkbox' ? ( + handleFieldValueChange(rule.id, action.id, field.fieldCode, e.target.checked)} + label={field.fieldName} /> - - {fieldVal ? 'Enabled' : 'Disabled'} - -
- ) : ( - handleFieldValueChange(rule.id, action.id, field.fieldCode, e.target.value)} - placeholder={field.placeholder || 'Enter value...'} - min={ - field.fieldType === 'date' - ? new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().split('T')[0] - : field.fieldType === 'datetime' - ? new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().slice(0, 16) - : undefined - } - /> - )} -
- ); - })} -
- )} + ) : field.fieldType === 'switch' ? ( +
+ handleFieldValueChange(rule.id, action.id, field.fieldCode, e.target.checked)} + /> + + {fieldVal ? 'Enabled' : 'Disabled'} + +
+ ) : ( + handleFieldValueChange(rule.id, action.id, field.fieldCode, e.target.value)} + placeholder={field.placeholder || 'Enter value...'} + min={ + field.fieldType === 'date' + ? new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().split('T')[0] + : field.fieldType === 'datetime' + ? new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString().slice(0, 16) + : undefined + } + /> + )} + + {helpText && ( +

+ {helpText} +

+ )} +
+ ); + })} +
+ ); + })()}
)}
diff --git a/src/app/simulation/components/SimulationTerminal.tsx b/src/app/simulation/components/SimulationTerminal.tsx index dc3f0d6..0c50520 100644 --- a/src/app/simulation/components/SimulationTerminal.tsx +++ b/src/app/simulation/components/SimulationTerminal.tsx @@ -75,7 +75,6 @@ const SIMULATION_PASSENGER_POOL: Array 3 Hours (Long Haul)', value: 'Delay > 3 Hours' }, { label: 'Delay > 4 Hours (Standard)', value: 'Delay > 4 Hours' }, { label: 'Short Notice Cancellation (< 14 Days)', value: 'Short Notice Cancellation' }, diff --git a/src/components/custom/CustomInput.tsx b/src/components/custom/CustomInput.tsx index 80c0ed6..4861bfe 100644 --- a/src/components/custom/CustomInput.tsx +++ b/src/components/custom/CustomInput.tsx @@ -22,7 +22,7 @@ const CustomInput = forwardRef( type = "text", leftIcon, rightIcon, - maxLength, + maxLength = 100, phonePrefix, disabled, className = "", diff --git a/src/components/custom/CustomTextArea.tsx b/src/components/custom/CustomTextArea.tsx index c69a4d7..8a8b274 100644 --- a/src/components/custom/CustomTextArea.tsx +++ b/src/components/custom/CustomTextArea.tsx @@ -16,7 +16,7 @@ const CustomTextArea = forwardRef( label, leftIcon, rightIcon, - maxLength, + maxLength = 500, disabled, className = "", rows = 4,