diff --git a/public/favicon.svg b/public/favicon.svg index 6893eb1..6f40541 100644 --- a/public/favicon.svg +++ b/public/favicon.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + \ No newline at end of file diff --git a/src/app/recoveryIncidents/RecoveryIncidentsApi.ts b/src/app/recoveryIncidents/RecoveryIncidentsApi.ts index 9f064ee..0d827fd 100644 --- a/src/app/recoveryIncidents/RecoveryIncidentsApi.ts +++ b/src/app/recoveryIncidents/RecoveryIncidentsApi.ts @@ -61,6 +61,10 @@ export function updateRecoveryIncident(id: string, data: Partial(`/recovery-incidents/${id}`, data); } +export function updateIncidentStatus(id: string, status: string): Promise { + return ApiClient.patch(`/recovery-incidents/${id}/status`, { status }); +} + export function deleteRecoveryIncident(id: string): Promise { return ApiClient.delete(`/recovery-incidents/${id}`); } diff --git a/src/app/recoveryIncidents/components/RecoveryIncidentsList.tsx b/src/app/recoveryIncidents/components/RecoveryIncidentsList.tsx index 40e3d1d..14c9e1d 100644 --- a/src/app/recoveryIncidents/components/RecoveryIncidentsList.tsx +++ b/src/app/recoveryIncidents/components/RecoveryIncidentsList.tsx @@ -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); } diff --git a/src/app/recoveryIncidents/tabs/RecoveryPlanTab.tsx b/src/app/recoveryIncidents/tabs/RecoveryPlanTab.tsx index afdab3c..4522487 100644 --- a/src/app/recoveryIncidents/tabs/RecoveryPlanTab.tsx +++ b/src/app/recoveryIncidents/tabs/RecoveryPlanTab.tsx @@ -9,7 +9,7 @@ export default function RecoveryPlanTab() {

FINANCIAL REFUND

- +
REFUND AMOUNT @@ -32,7 +32,7 @@ export default function RecoveryPlanTab() {

COMPENSATION & PERKS

- +
CASH COMPENSATION @@ -55,7 +55,7 @@ export default function RecoveryPlanTab() {

PASSENGER CARE

- +
MEAL VOUCHERS diff --git a/src/app/recoveryIncidents/tabs/index.tsx b/src/app/recoveryIncidents/tabs/index.tsx index 2c627cd..b24fb9c 100644 --- a/src/app/recoveryIncidents/tabs/index.tsx +++ b/src/app/recoveryIncidents/tabs/index.tsx @@ -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(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
Loading incident...
; } + const currentStatus = incident?.status || "Pending"; + return (
{/* Header */} @@ -67,8 +94,11 @@ export default function RecoveryIncidentTabs() {

{incident?.recoveryCode || id}

- - +
{incident?.passengerName || 'Unknown'}
@@ -87,14 +117,14 @@ export default function RecoveryIncidentTabs() { {/* Tabs Row */}
- +
- +
Share with Finance @@ -109,94 +139,100 @@ export default function RecoveryIncidentTabs() {
{/* Left Column - Tab Content */}
- +
{/* Right Column - Sidebar */}
- - {/* Blur Overlay */} -
-

"Coming soon"

-
- {/* Sidebar Content */} -
-
- -

AI RECOMMENDATION

-
- -
-
- SATISFACTION PREDICT - 84% -
-
-
-
-
+ {/* Blur Overlay */} +
+

"Coming soon"

+
-
-
- ESCALATION RISK - 12% -
-
-
-
-
+ {/* Sidebar Content */} +
+
+ +

AI RECOMMENDATION

+
-
-

NEXT RECOMMENDED ACTION

-
-

Approve the automated recovery payout of [250 EUR]. This will prevent a regulatory complaint and retain this high-value Platinum member.

-
- - } - className="w-full !py-3 !bg-[#1B9869]/50 !text-white !font-semibold !rounded-lg" - > - EXECUTE RECOMMENDATION - -
-
+
+
+ SATISFACTION PREDICT + 84% +
+
+
+
+
+ +
+
+ ESCALATION RISK + 12% +
+
+
+
+
+ +
+

NEXT RECOMMENDED ACTION

+
+

Approve the automated recovery payout of [250 EUR]. This will prevent a regulatory complaint and retain this high-value Platinum member.

+
+ + } + className="w-full !py-3 !bg-[#1B9869]/50 !text-white !font-semibold !rounded-lg" + > + EXECUTE RECOMMENDATION + +
+
{/* Bottom Sticky Action Bar */}
-
{/* Spacer */} -
- } - className="!bg-[#FDE8E8] !text-[#E02424] !font-bold !rounded-lg !border !border-[#E02424] hover:!bg-red-100" - > - REJECT RECOVERY - - } - className="!bg-[#FEF3C7] !text-[#B45309] !font-bold !rounded-lg !border !border-[#B45309] hover:!bg-[#FDE68A]" - > - MARK FOR REVIEW - - } - className="!bg-[#1B9869] !bg-none !text-white !font-bold !rounded-lg !border !border-[#1B9869] hover:!bg-[#14704E]" - > - APPROVE RECOVERY - -
+
{/* Spacer */} +
+ handleStatusChange("Rejected")} + leftIcon={} + className="!bg-[#FDE8E8] !text-[#E02424] !font-bold !rounded-lg !border !border-[#E02424] hover:!bg-red-100 disabled:opacity-50" + > + REJECT RECOVERY + + handleStatusChange("Under Review")} + leftIcon={} + className="!bg-[#FEF3C7] !text-[#B45309] !font-bold !rounded-lg !border !border-[#B45309] hover:!bg-[#FDE68A] disabled:opacity-50" + > + MARK FOR REVIEW + + handleStatusChange("Approved")} + leftIcon={} + className="!bg-[#1B9869] !bg-none !text-white !font-bold !rounded-lg !border !border-[#1B9869] hover:!bg-[#14704E] disabled:opacity-50" + > + APPROVE RECOVERY + +
);