From c352d8ff64fab1923df8113651cc74e8b0b80aaf Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Tue, 24 Feb 2026 04:05:47 +0530 Subject: [PATCH] feat: Add field validation and UI configuration management, and enable editing of field assignments. --- .../dashboard/profile-sections/[id]/page.tsx | 262 +++++++++++++++--- 1 file changed, 229 insertions(+), 33 deletions(-) diff --git a/src/app/dashboard/profile-sections/[id]/page.tsx b/src/app/dashboard/profile-sections/[id]/page.tsx index b6ed552..1961e1e 100644 --- a/src/app/dashboard/profile-sections/[id]/page.tsx +++ b/src/app/dashboard/profile-sections/[id]/page.tsx @@ -15,12 +15,14 @@ import { FieldType, FieldOption, RangeConfig, + FieldValidation, + FieldUiConfig, FIELD_TYPES, getErrorMessage, } from '@/services'; type FieldModalType = 'create' | 'edit' | 'delete' | null; -type AssignModalType = 'assign' | null; +type AssignModalType = 'assign' | 'editAssignment' | null; const REQUIRES_OPTIONS: FieldType[] = ['SELECT', 'RADIO', 'MULTI_SELECT', 'CHECKBOX_GROUP']; @@ -58,6 +60,8 @@ export default function SectionDetailPage() { isSearchableOnly: false, options: [], rangeConfig: undefined, + validation: undefined, + uiConfig: undefined, }); // Options editor state @@ -114,6 +118,8 @@ export default function SectionDetailPage() { isSearchableOnly: false, options: [], rangeConfig: undefined, + validation: undefined, + uiConfig: undefined, }); setOptionInput({ value: '', label: '' }); setModalError(''); @@ -135,6 +141,8 @@ export default function SectionDetailPage() { isSearchableOnly: field.isSearchableOnly, options: field.options || [], rangeConfig: field.rangeConfig || undefined, + validation: field.validation || undefined, + uiConfig: field.uiConfig || undefined, }); setOptionInput({ value: '', label: '' }); setModalError(''); @@ -217,6 +225,30 @@ export default function SectionDetailPage() { }); }; + // Validation management + const updateValidation = (key: keyof FieldValidation, value: number | string) => { + const current = fieldForm.validation || {}; + const parsed = typeof value === 'string' && value === '' ? undefined : typeof value === 'string' ? (isNaN(Number(value)) ? value : Number(value)) : value; + const updated = { ...current, [key]: parsed }; + // Remove undefined keys + Object.keys(updated).forEach((k) => { + if (updated[k as keyof FieldValidation] === undefined || updated[k as keyof FieldValidation] === '') { + delete updated[k as keyof FieldValidation]; + } + }); + setFieldForm({ ...fieldForm, validation: Object.keys(updated).length > 0 ? updated : undefined }); + }; + + // UI Config management + const updateUiConfig = (key: keyof FieldUiConfig, value: number | string | boolean) => { + const current = fieldForm.uiConfig || {}; + const updated = { ...current, [key]: value }; + if (value === '' || value === undefined) { + delete updated[key as keyof FieldUiConfig]; + } + setFieldForm({ ...fieldForm, uiConfig: Object.keys(updated).length > 0 ? updated : undefined }); + }; + // Range config management const updateRangeConfig = (key: keyof RangeConfig, value: number | string) => { setFieldForm({ @@ -257,6 +289,34 @@ export default function SectionDetailPage() { setModalError(''); }; + const openEditAssignmentModal = (ats: { agentTypeId: string; sortOrder: number; isRequired: boolean }) => { + setAssignForm({ + agentTypeId: ats.agentTypeId, + sortOrder: ats.sortOrder, + isRequired: ats.isRequired, + }); + setModalError(''); + setAssignModalType('editAssignment'); + }; + + const handleUpdateAssignment = async (e: React.FormEvent) => { + e.preventDefault(); + setIsSubmitting(true); + setModalError(''); + try { + await profileSectionsService.updateAssignment(sectionId, assignForm.agentTypeId, { + sortOrder: assignForm.sortOrder, + isRequired: assignForm.isRequired, + }); + closeAssignModal(); + fetchData(); + } catch (err) { + setModalError(getErrorMessage(err)); + } finally { + setIsSubmitting(false); + } + }; + const handleAssign = async (e: React.FormEvent) => { e.preventDefault(); setIsSubmitting(true); @@ -533,15 +593,26 @@ export default function SectionDetailPage() { {ats.isRequired ? 'Required' : 'Optional'} • Order: {ats.sortOrder}

- +
+ + +
); })} @@ -826,6 +897,120 @@ export default function SectionDetailPage() { )} + + {/* Validation Rules */} +
+ +
+ {['TEXT', 'TEXTAREA', 'TAG_INPUT'].includes(fieldForm.fieldType) && ( + <> +
+ + updateValidation('minLength', e.target.value)} + placeholder="No min" + min={0} + className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:outline-none focus:border-[#f5a623] text-sm text-[#00293d] bg-white placeholder:text-[#666666]" + /> +
+
+ + updateValidation('maxLength', e.target.value)} + placeholder="No max" + min={0} + className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:outline-none focus:border-[#f5a623] text-sm text-[#00293d] bg-white placeholder:text-[#666666]" + /> +
+ + )} + {['NUMBER', 'RANGE'].includes(fieldForm.fieldType) && ( + <> +
+ + updateValidation('min', e.target.value)} + placeholder="No min" + className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:outline-none focus:border-[#f5a623] text-sm text-[#00293d] bg-white placeholder:text-[#666666]" + /> +
+
+ + updateValidation('max', e.target.value)} + placeholder="No max" + className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:outline-none focus:border-[#f5a623] text-sm text-[#00293d] bg-white placeholder:text-[#666666]" + /> +
+ + )} +
+ + updateValidation('pattern', e.target.value)} + placeholder="e.g., ^[a-zA-Z]+$" + className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:outline-none focus:border-[#f5a623] text-sm text-[#00293d] bg-white placeholder:text-[#666666]" + /> +
+
+
+ + {/* UI Configuration */} +
+ +
+
+
+ + updateUiConfig('columns', e.target.value ? Number(e.target.value) : '')} + placeholder="Auto" + min={1} + max={4} + className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:outline-none focus:border-[#f5a623] text-sm text-[#00293d] bg-white placeholder:text-[#666666]" + /> +
+
+ updateUiConfig('showInPreview', e.target.checked)} + className="rounded border-[#e5e7eb] text-[#f5a623] focus:ring-0" + /> + +
+
+
+ + updateUiConfig('helpText', e.target.value)} + placeholder="Additional help text shown below the field" + className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:outline-none focus:border-[#f5a623] text-sm text-[#00293d] bg-white placeholder:text-[#666666]" + /> +
+
+