feat: implement RecoveryPlanTab and update incident detail view with status management logic

This commit is contained in:
Syed Waseem
2026-08-10 15:31:12 +05:30
parent 750286ddea
commit 26a3f68b99
5 changed files with 150 additions and 108 deletions
+4 -1
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 608 B

@@ -61,6 +61,10 @@ export function updateRecoveryIncident(id: string, data: Partial<RecoveryInciden
return ApiClient.patch<any, RecoveryIncident>(`/recovery-incidents/${id}`, data);
}
export function updateIncidentStatus(id: string, status: string): Promise<RecoveryIncident> {
return ApiClient.patch<any, RecoveryIncident>(`/recovery-incidents/${id}/status`, { status });
}
export function deleteRecoveryIncident(id: string): Promise<void> {
return ApiClient.delete<any, void>(`/recovery-incidents/${id}`);
}
@@ -180,11 +180,10 @@ export default function RecoveryIncidentsList() {
const handleStatusChange = async (incident: RecoveryIncident, text: string) => {
try {
const { updateRecoveryIncident } = await import('../RecoveryIncidentsApi');
await updateRecoveryIncident(incident.id, {
status: text
});
const { updateIncidentStatus, getRecoveryMetrics } = await import('../RecoveryIncidentsApi');
await updateIncidentStatus(incident.id, text);
fetchIncidents();
getRecoveryMetrics().then((data) => setMetrics(data)).catch(() => {});
} catch (error) {
console.error("Failed to update status", error);
}
+56 -20
View File
@@ -1,37 +1,62 @@
import { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import {User, Checks, X, ClockCounterClockwiseIcon, ArrowLeftIcon, ArrowsClockwiseIcon, AirplaneTiltIcon } from '@phosphor-icons/react';
import { User, Checks, X, ClockCounterClockwiseIcon, ArrowLeftIcon, ArrowsClockwiseIcon, AirplaneTiltIcon } from '@phosphor-icons/react';
import { CustomButton, CustomTabs, CustomBackButton, CustomStatus } from '../../../components/custom';
import SummaryTab from './SummaryTab';
import CaseDetailsTab from './CaseDetailsTab';
import RecoveryPlanTab from './RecoveryPlanTab';
import AuditTrailTab from './AuditTrailTab';
import { SparkleIcon } from 'lucide-react';
import { getRecoveryIncident } from '../RecoveryIncidentsApi';
import { getRecoveryIncident, updateIncidentStatus } from '../RecoveryIncidentsApi';
import type { RecoveryIncident } from '../RecoveryIncidentsTypes';
function getStatusVariant(status?: string): "success" | "error" | "warning" | "info" | "neutral" {
if (!status) return "neutral";
const s = status.toLowerCase();
if (s.includes("appr") || s.includes("active") || s.includes("success")) return "success";
if (s.includes("reject") || s.includes("denied") || s.includes("error")) return "error";
if (s.includes("pend") || s.includes("review") || s.includes("warn")) return "warning";
if (s.includes("new") || s.includes("info")) return "info";
return "neutral";
}
export default function RecoveryIncidentTabs() {
const { id } = useParams();
const [activeTab, setActiveTab] = useState('Summary');
const [incident, setIncident] = useState<RecoveryIncident | null>(null);
const [loading, setLoading] = useState(true);
const [updating, setUpdating] = useState(false);
const fetchIncident = async () => {
if (!id) return;
setLoading(true);
try {
const data = await getRecoveryIncident(id);
setIncident(data);
} catch (err) {
console.error("Failed to load incident:", err);
} finally {
setLoading(false);
}
};
useEffect(() => {
if (id) {
setLoading(true);
getRecoveryIncident(id)
.then(data => {
setIncident(data);
})
.catch(err => {
console.error("Failed to load incident:", err);
})
.finally(() => {
setLoading(false);
});
}
fetchIncident();
}, [id]);
const handleStatusChange = async (newStatus: string) => {
if (!id || updating) return;
setUpdating(true);
try {
const updated = await updateIncidentStatus(id, newStatus);
setIncident(updated);
} catch (err) {
console.error("Failed to update incident status:", err);
} finally {
setUpdating(false);
}
};
const tabItems = [
{
id: 'Summary',
@@ -59,6 +84,8 @@ export default function RecoveryIncidentTabs() {
return <div className="p-8 text-center text-gray-500">Loading incident...</div>;
}
const currentStatus = incident?.status || "Pending";
return (
<div className="w-full flex flex-col h-full relative">
{/* Header */}
@@ -67,8 +94,11 @@ export default function RecoveryIncidentTabs() {
<div className="flex items-center gap-3">
<CustomBackButton />
<h1 className="text-xl font-bold text-gray-900">{incident?.recoveryCode || id}</h1>
<CustomStatus status="UNDER REVIEW" variant="warning" className="uppercase !text-[11px] !px-2.5 !py-1 !tracking-wide" />
<CustomStatus status="HIGH PRIORITY" variant="error" className="uppercase !text-[11px] !px-2.5 !py-1 !bg-red-100 !text-red-800 !tracking-wide" />
<CustomStatus
status={currentStatus}
variant={getStatusVariant(currentStatus)}
className="uppercase !text-[11px] !px-2.5 !py-1 !tracking-wide"
/>
</div>
<div className="flex items-center gap-3 text-sm text-gray-500 ml-8">
<div className="flex items-center gap-1.5"><User size={16} /> {incident?.passengerName || 'Unknown'}</div>
@@ -177,22 +207,28 @@ export default function RecoveryIncidentTabs() {
<div className="flex gap-4">
<CustomButton
variant="text"
disabled={updating}
onClick={() => handleStatusChange("Rejected")}
leftIcon={<X size={18} weight="bold" />}
className="!bg-[#FDE8E8] !text-[#E02424] !font-bold !rounded-lg !border !border-[#E02424] hover:!bg-red-100"
className="!bg-[#FDE8E8] !text-[#E02424] !font-bold !rounded-lg !border !border-[#E02424] hover:!bg-red-100 disabled:opacity-50"
>
REJECT RECOVERY
</CustomButton>
<CustomButton
variant="text"
disabled={updating}
onClick={() => handleStatusChange("Under Review")}
leftIcon={<ClockCounterClockwiseIcon size={18} weight="bold" />}
className="!bg-[#FEF3C7] !text-[#B45309] !font-bold !rounded-lg !border !border-[#B45309] hover:!bg-[#FDE68A]"
className="!bg-[#FEF3C7] !text-[#B45309] !font-bold !rounded-lg !border !border-[#B45309] hover:!bg-[#FDE68A] disabled:opacity-50"
>
MARK FOR REVIEW
</CustomButton>
<CustomButton
variant="primary"
disabled={updating}
onClick={() => handleStatusChange("Approved")}
leftIcon={<Checks size={18} weight="bold" />}
className="!bg-[#1B9869] !bg-none !text-white !font-bold !rounded-lg !border !border-[#1B9869] hover:!bg-[#14704E]"
className="!bg-[#1B9869] !bg-none !text-white !font-bold !rounded-lg !border !border-[#1B9869] hover:!bg-[#14704E] disabled:opacity-50"
>
APPROVE RECOVERY
</CustomButton>