feat: add policy engine creation form and recovery incident list component
This commit is contained in:
@@ -530,9 +530,44 @@ export default function AddPolicyEngine() {
|
|||||||
.finally(() => setLoadingPolicy(false));
|
.finally(() => setLoadingPolicy(false));
|
||||||
}, [policyId]);
|
}, [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) => {
|
const handleSavePolicy = async (isDeploy: boolean) => {
|
||||||
if (!policyName.trim()) {
|
if (!isFormValid) {
|
||||||
alert('Please enter a Policy Name.');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1337,9 +1372,9 @@ export default function AddPolicyEngine() {
|
|||||||
{!isEditMode && (
|
{!isEditMode && (
|
||||||
<CustomButton
|
<CustomButton
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="!text-[#1E7D5C] !bg-[#E8F3EF] hover:!bg-[#d9ece4] !border-none font-semibold px-6"
|
className="!text-[#1E7D5C] !bg-[#E8F3EF] hover:!bg-[#d9ece4] !border-none font-semibold px-6 disabled:opacity-50"
|
||||||
onClick={() => handleSavePolicy(false)}
|
onClick={() => handleSavePolicy(false)}
|
||||||
disabled={isSaving}
|
disabled={isSaving || !isFormValid}
|
||||||
>
|
>
|
||||||
{isSaving ? 'Saving...' : 'Save Draft'}
|
{isSaving ? 'Saving...' : 'Save Draft'}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
@@ -1354,9 +1389,9 @@ export default function AddPolicyEngine() {
|
|||||||
</CustomButton>
|
</CustomButton>
|
||||||
<CustomButton
|
<CustomButton
|
||||||
variant="primary"
|
variant="primary"
|
||||||
className="!bg-[#1E7D5C] hover:!bg-[#17664B] font-semibold px-6 shadow-sm"
|
className="!bg-[#1E7D5C] hover:!bg-[#17664B] font-semibold px-6 shadow-sm disabled:opacity-50"
|
||||||
onClick={() => handleSavePolicy(true)}
|
onClick={() => handleSavePolicy(true)}
|
||||||
disabled={isSaving}
|
disabled={isSaving || !isFormValid}
|
||||||
>
|
>
|
||||||
{isSaving ? 'Deploying...' : isEditMode ? 'Update & Deploy Policy' : 'Deploy Policy'}
|
{isSaving ? 'Deploying...' : isEditMode ? 'Update & Deploy Policy' : 'Deploy Policy'}
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
|
|||||||
@@ -95,12 +95,6 @@ export default function RecoveryIncidentsList() {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [successMsg, setSuccessMsg] = useState<string | null>(null);
|
const [successMsg, setSuccessMsg] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getRecoveryMetrics()
|
|
||||||
.then((data) => setMetrics(data))
|
|
||||||
.catch((err) => console.error("Failed to fetch recovery metrics:", err));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Pagination
|
// Pagination
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
@@ -143,6 +137,9 @@ export default function RecoveryIncidentsList() {
|
|||||||
try {
|
try {
|
||||||
const data = await getRecoveryIncidents();
|
const data = await getRecoveryIncidents();
|
||||||
setIncidents(data);
|
setIncidents(data);
|
||||||
|
getRecoveryMetrics()
|
||||||
|
.then((m) => setMetrics(m))
|
||||||
|
.catch((err) => console.error("Failed to fetch recovery metrics:", err));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch recovery incidents", error);
|
console.error("Failed to fetch recovery incidents", error);
|
||||||
setIncidents([]);
|
setIncidents([]);
|
||||||
@@ -213,10 +210,9 @@ export default function RecoveryIncidentsList() {
|
|||||||
try {
|
try {
|
||||||
setError(null);
|
setError(null);
|
||||||
setSuccessMsg(null);
|
setSuccessMsg(null);
|
||||||
const { updateIncidentStatus, getRecoveryMetrics } = await import('../RecoveryIncidentsApi');
|
const { updateIncidentStatus } = await import('../RecoveryIncidentsApi');
|
||||||
await updateIncidentStatus(incident.id, text);
|
await updateIncidentStatus(incident.id, text);
|
||||||
fetchIncidents();
|
fetchIncidents();
|
||||||
getRecoveryMetrics().then((data) => setMetrics(data)).catch(() => { });
|
|
||||||
setSuccessMsg(`Incident status updated to ${text} successfully.`);
|
setSuccessMsg(`Incident status updated to ${text} successfully.`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to update status", error);
|
console.error("Failed to update status", error);
|
||||||
|
|||||||
Reference in New Issue
Block a user