From d1b9a2b9857c34acd4c7e26dee1ee7c1668b6d8c Mon Sep 17 00:00:00 2001 From: azeeee05 Date: Thu, 20 Aug 2026 16:36:23 +0530 Subject: [PATCH 1/6] feat: implement policy management listing and creation interface --- .../components/AddPolicyEngine.tsx | 22 +++++-------------- .../components/PolicyEngineList.tsx | 10 ++++++++- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/app/policyEngine/components/AddPolicyEngine.tsx b/src/app/policyEngine/components/AddPolicyEngine.tsx index 5c31b1f..163ccf6 100644 --- a/src/app/policyEngine/components/AddPolicyEngine.tsx +++ b/src/app/policyEngine/components/AddPolicyEngine.tsx @@ -17,7 +17,6 @@ import { CustomLoader, Skeleton, } from '../../../components/custom'; -import CustomSuccessModal from '../../../components/custom/CustomSuccessModal'; import { getJurisdictionOptions, getCohortOptions, @@ -153,7 +152,6 @@ export default function AddPolicyEngine() { const [loadingPolicy, setLoadingPolicy] = useState(false); const [isSaving, setIsSaving] = useState(false); - const [showSuccessModal, setShowSuccessModal] = useState(false); // Dynamic Options State (loaded directly from PolicyEngineApi) const [jurisdictionOptions, setJurisdictionOptions] = useState([]); @@ -621,7 +619,11 @@ export default function AddPolicyEngine() { await createPolicy(payload); } - setShowSuccessModal(true); + const successMsg = isEditMode + ? `Policy "${payload.policyName}" updated successfully!` + : `Policy "${payload.policyName}" created successfully!`; + + navigate('/policy-engine', { state: { successMsg } }); } catch (err) { console.error('Failed to save policy', err); alert('Failed to save policy. Please check input values and try again.'); @@ -1358,20 +1360,6 @@ export default function AddPolicyEngine() { - {/* ─── Success Modal ──────────────────────────────────────────────── */} - { - setShowSuccessModal(false); - navigate('/policy-engine'); - }} - title={isEditMode ? 'Policy Updated Successfully.' : 'Policy Created Successfully.'} - label="POLICY NAME" - cohortName={policyName} - cohortStatus={status} - cohortDescription={description} - /> - ); } diff --git a/src/app/policyEngine/components/PolicyEngineList.tsx b/src/app/policyEngine/components/PolicyEngineList.tsx index 6415489..6878e56 100644 --- a/src/app/policyEngine/components/PolicyEngineList.tsx +++ b/src/app/policyEngine/components/PolicyEngineList.tsx @@ -7,7 +7,7 @@ import { PencilSimpleIcon, ChecksIcon, } from "@phosphor-icons/react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate, useLocation } from "react-router-dom"; import { CustomTable, CustomInput, @@ -65,6 +65,14 @@ export default function PolicyEngineList() { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [successMsg, setSuccessMsg] = useState(null); + const location = useLocation(); + + useEffect(() => { + if (location.state?.successMsg) { + setSuccessMsg(location.state.successMsg); + window.history.replaceState({}, document.title); + } + }, [location]); // Pagination const [currentPage, setCurrentPage] = useState(1); From 43f9c08b1e4f1b30e1785f05af0764bd687d3c08 Mon Sep 17 00:00:00 2001 From: azeeee05 Date: Thu, 20 Aug 2026 16:55:43 +0530 Subject: [PATCH 2/6] feat: implement AddPolicyEngine component for dynamic rule and action configuration --- src/app/policyEngine/components/AddPolicyEngine.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/policyEngine/components/AddPolicyEngine.tsx b/src/app/policyEngine/components/AddPolicyEngine.tsx index 163ccf6..6374399 100644 --- a/src/app/policyEngine/components/AddPolicyEngine.tsx +++ b/src/app/policyEngine/components/AddPolicyEngine.tsx @@ -619,9 +619,12 @@ export default function AddPolicyEngine() { await createPolicy(payload); } - const successMsg = isEditMode - ? `Policy "${payload.policyName}" updated successfully!` - : `Policy "${payload.policyName}" created successfully!`; + let actionWord = isDeploy ? 'created' : 'saved as draft'; + if (isEditMode) { + actionWord = isDeploy ? 'updated' : 'saved as draft'; + } + + const successMsg = `Policy "${payload.policyName}" ${actionWord} successfully!`; navigate('/policy-engine', { state: { successMsg } }); } catch (err) { From 39ee42c3eb29aed204f9a97c1d10128f145da1f7 Mon Sep 17 00:00:00 2001 From: azeeee05 Date: Thu, 20 Aug 2026 17:03:53 +0530 Subject: [PATCH 3/6] feat: implement RecoveryIncidentsList component with filtering, grouping, and status management functionality --- .../components/RecoveryIncidentsList.tsx | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/app/recoveryIncidents/components/RecoveryIncidentsList.tsx b/src/app/recoveryIncidents/components/RecoveryIncidentsList.tsx index a6d6f17..43f87c0 100644 --- a/src/app/recoveryIncidents/components/RecoveryIncidentsList.tsx +++ b/src/app/recoveryIncidents/components/RecoveryIncidentsList.tsx @@ -106,8 +106,22 @@ export default function RecoveryIncidentsList() { // Filters const [search, setSearch] = useState(""); - const [isGrouped, setIsGrouped] = useState(false); - const [collapsedGroups, setCollapsedGroups] = useState>(new Set()); + const [isGrouped, setIsGrouped] = useState(() => { + return sessionStorage.getItem("recovery_isGrouped") === "true"; + }); + const [collapsedGroups, setCollapsedGroups] = useState>(() => { + const saved = sessionStorage.getItem("recovery_collapsedGroups"); + return saved ? new Set(JSON.parse(saved)) : new Set(); + }); + + useEffect(() => { + sessionStorage.setItem("recovery_isGrouped", String(isGrouped)); + }, [isGrouped]); + + useEffect(() => { + sessionStorage.setItem("recovery_collapsedGroups", JSON.stringify(Array.from(collapsedGroups))); + }, [collapsedGroups]); + const [isModalOpen, setIsModalOpen] = useState(false); const [editingIncident, setEditingIncident] = useState(null); @@ -143,8 +157,13 @@ export default function RecoveryIncidentsList() { useEffect(() => { if (incidents.length > 0) { - const keys = new Set(incidents.map((i) => i.flightNumber || "Other")); - setCollapsedGroups(keys); + setCollapsedGroups((prev) => { + const hasSaved = sessionStorage.getItem("recovery_hasSavedGroups"); + if (hasSaved) return prev; + + sessionStorage.setItem("recovery_hasSavedGroups", "true"); + return new Set(incidents.map((i) => i.flightNumber || "Other")); + }); } }, [incidents]); From 1192d3c1785948847b792cb6d4a3fdd6015bb3bb Mon Sep 17 00:00:00 2001 From: azeeee05 Date: Fri, 21 Aug 2026 10:47:27 +0530 Subject: [PATCH 4/6] feat: add policy engine creation form and recovery incident list component --- .../components/AddPolicyEngine.tsx | 47 ++++++++++++++++--- .../components/RecoveryIncidentsList.tsx | 12 ++--- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/app/policyEngine/components/AddPolicyEngine.tsx b/src/app/policyEngine/components/AddPolicyEngine.tsx index 6374399..55cb704 100644 --- a/src/app/policyEngine/components/AddPolicyEngine.tsx +++ b/src/app/policyEngine/components/AddPolicyEngine.tsx @@ -530,9 +530,44 @@ export default function AddPolicyEngine() { .finally(() => setLoadingPolicy(false)); }, [policyId]); + const isFormValid = (() => { + if (!policyName.trim()) return false; + + if (audienceType === 'Selected Cohorts' && (!selectedCohorts || selectedCohorts.length === 0)) return false; + + if (!rules || rules.length === 0) return false; + + for (const rule of rules) { + if (!rule.category) return false; + + if (!rule.conditions || rule.conditions.length === 0) return false; + for (const cond of rule.conditions) { + if (!cond.condition || !cond.operator || cond.value === '' || cond.value === undefined || cond.value === null) return false; + } + + if (!rule.actions || rule.actions.length === 0) return false; + for (const act of rule.actions) { + if (!act.actionCategoryId || !act.actionTypeId) return false; + + const fields = actionTypeFieldsMap[act.actionTypeId] || []; + for (const field of fields) { + if (field.isRequired) { + const val = act.fieldValues?.[field.fieldCode]; + if (val === undefined || val === null || val === '') return false; + if (Array.isArray(val) && val.length === 0) return false; + if (typeof val === 'object' && !Array.isArray(val)) { + if (val.amount === undefined || val.amount === null || val.amount === '') return false; + } + } + } + } + } + + return true; + })(); + const handleSavePolicy = async (isDeploy: boolean) => { - if (!policyName.trim()) { - alert('Please enter a Policy Name.'); + if (!isFormValid) { return; } @@ -1337,9 +1372,9 @@ export default function AddPolicyEngine() { {!isEditMode && ( handleSavePolicy(false)} - disabled={isSaving} + disabled={isSaving || !isFormValid} > {isSaving ? 'Saving...' : 'Save Draft'} @@ -1354,9 +1389,9 @@ export default function AddPolicyEngine() { handleSavePolicy(true)} - disabled={isSaving} + disabled={isSaving || !isFormValid} > {isSaving ? 'Deploying...' : isEditMode ? 'Update & Deploy Policy' : 'Deploy Policy'} diff --git a/src/app/recoveryIncidents/components/RecoveryIncidentsList.tsx b/src/app/recoveryIncidents/components/RecoveryIncidentsList.tsx index 43f87c0..43ca927 100644 --- a/src/app/recoveryIncidents/components/RecoveryIncidentsList.tsx +++ b/src/app/recoveryIncidents/components/RecoveryIncidentsList.tsx @@ -95,12 +95,6 @@ export default function RecoveryIncidentsList() { const [error, setError] = useState(null); const [successMsg, setSuccessMsg] = useState(null); - useEffect(() => { - getRecoveryMetrics() - .then((data) => setMetrics(data)) - .catch((err) => console.error("Failed to fetch recovery metrics:", err)); - }, []); - // Pagination const [currentPage, setCurrentPage] = useState(1); @@ -143,6 +137,9 @@ export default function RecoveryIncidentsList() { try { const data = await getRecoveryIncidents(); setIncidents(data); + getRecoveryMetrics() + .then((m) => setMetrics(m)) + .catch((err) => console.error("Failed to fetch recovery metrics:", err)); } catch (error) { console.error("Failed to fetch recovery incidents", error); setIncidents([]); @@ -213,10 +210,9 @@ export default function RecoveryIncidentsList() { try { setError(null); setSuccessMsg(null); - const { updateIncidentStatus, getRecoveryMetrics } = await import('../RecoveryIncidentsApi'); + const { updateIncidentStatus } = await import('../RecoveryIncidentsApi'); await updateIncidentStatus(incident.id, text); fetchIncidents(); - getRecoveryMetrics().then((data) => setMetrics(data)).catch(() => { }); setSuccessMsg(`Incident status updated to ${text} successfully.`); } catch (error) { console.error("Failed to update status", error); From cd5fcc3084d8f31f2a53dd753e1f0fbb07a4fc88 Mon Sep 17 00:00:00 2001 From: azeeee05 Date: Fri, 21 Aug 2026 11:08:24 +0530 Subject: [PATCH 5/6] feat: implement AddPolicyEngine component for dynamic policy and rule configuration --- .../components/AddPolicyEngine.tsx | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/app/policyEngine/components/AddPolicyEngine.tsx b/src/app/policyEngine/components/AddPolicyEngine.tsx index 55cb704..019861f 100644 --- a/src/app/policyEngine/components/AddPolicyEngine.tsx +++ b/src/app/policyEngine/components/AddPolicyEngine.tsx @@ -336,7 +336,7 @@ export default function AddPolicyEngine() { // Policy Info State const [policyName, setPolicyName] = useState(''); const [jurisdiction, setJurisdiction] = useState<(string | number)[]>([]); - const [status, setStatus] = useState<'Active' | 'Inactive'>('Active'); + const [status, setStatus] = useState<'Active' | 'Inactive' | 'Draft'>('Active'); const [description, setDescription] = useState(''); // Target Audience State @@ -379,7 +379,8 @@ export default function AddPolicyEngine() { setJurisdiction(singleJur ? [singleJur] : []); } setStatus( - data.status?.toLowerCase() === 'active' ? 'Active' : 'Inactive' + data.status?.toLowerCase() === 'active' ? 'Active' : + data.status?.toLowerCase() === 'draft' ? 'Draft' : 'Inactive' ); setDescription(data.description || ''); @@ -566,8 +567,13 @@ export default function AddPolicyEngine() { return true; })(); + const isDraftValid = policyName.trim().length > 0; + const handleSavePolicy = async (isDeploy: boolean) => { - if (!isFormValid) { + if (isDeploy && !isFormValid) { + return; + } + if (!isDeploy && !isDraftValid) { return; } @@ -578,7 +584,7 @@ export default function AddPolicyEngine() { jurisdictionId: Array.isArray(jurisdiction) && jurisdiction.length > 0 ? String(jurisdiction[0]) : undefined, jurisdictionIds: Array.isArray(jurisdiction) ? jurisdiction.map(String) : [], description: description || undefined, - status: isDeploy ? status.toLowerCase() : 'draft', + status: isDeploy ? (status === 'Draft' ? 'active' : status.toLowerCase()) : 'draft', audienceType: audienceType === 'Selected Cohorts' ? 'COHORT' : 'ALL', targetAudiences: audienceType === 'Selected Cohorts' @@ -1369,16 +1375,14 @@ export default function AddPolicyEngine() {
- {!isEditMode && ( - handleSavePolicy(false)} - disabled={isSaving || !isFormValid} - > - {isSaving ? 'Saving...' : 'Save Draft'} - - )} + handleSavePolicy(false)} + disabled={isSaving || !isDraftValid} + > + {isSaving ? 'Saving...' : 'Save Draft'} + Date: Fri, 21 Aug 2026 11:23:30 +0530 Subject: [PATCH 6/6] feat: add AddPolicyEngine component with dynamic rule and action configuration logic --- .../components/AddPolicyEngine.tsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/app/policyEngine/components/AddPolicyEngine.tsx b/src/app/policyEngine/components/AddPolicyEngine.tsx index 019861f..ae47969 100644 --- a/src/app/policyEngine/components/AddPolicyEngine.tsx +++ b/src/app/policyEngine/components/AddPolicyEngine.tsx @@ -929,6 +929,7 @@ export default function AddPolicyEngine() {
- +
setDescription(e.target.value)} placeholder="Enter description..." @@ -989,6 +991,7 @@ export default function AddPolicyEngine() {
handleCategoryChange(rule.id, val)} @@ -1042,7 +1046,7 @@ export default function AddPolicyEngine() { />
- + {(() => { const prevPriority = getPrevPriority(rule); const nextPriority = getNextPriority(rule); @@ -1090,6 +1094,7 @@ export default function AddPolicyEngine() {
handleConditionFieldSelect(rule.id, condition.id, val)} @@ -1100,6 +1105,7 @@ export default function AddPolicyEngine() {
handleUpdateCondition(rule.id, condition.id, { operator: val })} @@ -1110,6 +1116,7 @@ export default function AddPolicyEngine() { {isLookup ? ( handleUpdateCondition(rule.id, condition.id, { value: val })} @@ -1118,6 +1125,7 @@ export default function AddPolicyEngine() { ) : isBoolean ? ( handleUpdateCondition(rule.id, condition.id, { value: e.target.value })} @@ -1138,7 +1147,7 @@ export default function AddPolicyEngine() {
{cIdx < rule.conditions.length - 1 ? (
- + handleUpdateCondition(rule.id, condition.id, { logic: val })} @@ -1186,6 +1195,7 @@ export default function AddPolicyEngine() {
handleActionCategoryChange(rule.id, action.id, val)} @@ -1196,6 +1206,7 @@ export default function AddPolicyEngine() {
handleActionTypeChange(rule.id, action.id, val)} @@ -1206,7 +1217,7 @@ export default function AddPolicyEngine() { {aIdx < rule.actions.length - 1 ? (
- +
) : (