Merge pull request 'azeem' (#52) from azeem into development
Reviewed-on: https://gitea.maskantech.in/gitea_admin/aeroresolve_frontend/pulls/52
This commit is contained in:
@@ -280,11 +280,12 @@ export default function AuditLogsList() {
|
||||
// ─── Render ─────────────────────────────────────────────────────────────────
|
||||
|
||||
const displayedLogs = search
|
||||
? logs.filter((l) =>
|
||||
l.entityLabel?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
l.entityId?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
l.module?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
l.performedBy?.toLowerCase().includes(search.toLowerCase())
|
||||
? logs.filter(
|
||||
(l) =>
|
||||
l.entityLabel?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
l.entityId?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
l.module?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
l.performedBy?.toLowerCase().includes(search.toLowerCase()),
|
||||
)
|
||||
: logs;
|
||||
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import AddCohart from './AddCohart';
|
||||
import { PlusIcon, TrashIcon, MagnifyingGlassIcon, CheckIcon, XIcon, PencilSimpleIcon } from '@phosphor-icons/react';
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import AddCohart from "./AddCohart";
|
||||
import {
|
||||
PlusIcon,
|
||||
TrashIcon,
|
||||
MagnifyingGlassIcon,
|
||||
CheckIcon,
|
||||
XIcon,
|
||||
PencilSimpleIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
import {
|
||||
CustomTable,
|
||||
CustomInput,
|
||||
@@ -12,38 +19,40 @@ import {
|
||||
CustomConfirmationModal,
|
||||
CustomAlertBanner,
|
||||
Skeleton,
|
||||
} from '../../../components/custom';
|
||||
import type { Column } from '../../../components/custom/CustomTable';
|
||||
} from "../../../components/custom";
|
||||
import type { Column } from "../../../components/custom/CustomTable";
|
||||
import {
|
||||
listCoharts,
|
||||
deleteCohart,
|
||||
updateCohartStatus,
|
||||
} from '../CohartManageApi';
|
||||
import type { CohartResponse } from '../CohartManageTypes';
|
||||
} from "../CohartManageApi";
|
||||
import type { CohartResponse } from "../CohartManageTypes";
|
||||
|
||||
// ─── Constants ───────────────────────────────────────────────────────────────
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
const STATUS_OPTIONS = [
|
||||
{ label: 'All Statuses', value: '' },
|
||||
{ label: 'Active', value: 'Active' },
|
||||
{ label: 'Inactive', value: 'Inactive' },
|
||||
{ label: 'Draft', value: 'Draft' },
|
||||
{ label: "All Statuses", value: "" },
|
||||
{ label: "Active", value: "Active" },
|
||||
{ label: "Inactive", value: "Inactive" },
|
||||
{ label: "Draft", value: "Draft" },
|
||||
];
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
function HeaderLabel({ text }: { text: string }) {
|
||||
return (
|
||||
<span className="text-[14px] font-semibold text-[#6C766D] tracking-[0px]">{text}</span>
|
||||
<span className="text-[14px] font-semibold text-[#6C766D] tracking-[0px]">
|
||||
{text}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function CellText({ text }: { text: string | null | undefined }) {
|
||||
return (
|
||||
<span style={{ fontSize: '14px', color: '#676767', fontWeight: 500 }}>
|
||||
{text || '—'}
|
||||
<span style={{ fontSize: "14px", color: "#676767", fontWeight: 500 }}>
|
||||
{text || "—"}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -62,12 +71,13 @@ export default function CohortList() {
|
||||
const [totalPages, setTotalPages] = useState(1);
|
||||
|
||||
// Client-side filters (applied on top of server data)
|
||||
const [search, setSearch] = useState('');
|
||||
const [statusFilter, setStatusFilter] = useState('');
|
||||
const [search, setSearch] = useState("");
|
||||
const [statusFilter, setStatusFilter] = useState("");
|
||||
|
||||
// Modal state
|
||||
const [deleteTarget, setDeleteTarget] = useState<CohartResponse | null>(null);
|
||||
const [deactivateTarget, setDeactivateTarget] = useState<CohartResponse | null>(null);
|
||||
const [deactivateTarget, setDeactivateTarget] =
|
||||
useState<CohartResponse | null>(null);
|
||||
const [editTarget, setEditTarget] = useState<CohartResponse | null>(null);
|
||||
const [deleteLoading, setDeleteLoading] = useState(false);
|
||||
const [statusLoading, setStatusLoading] = useState(false);
|
||||
@@ -84,7 +94,7 @@ export default function CohortList() {
|
||||
setTotalItems(res.total);
|
||||
setTotalPages(res.totalPages);
|
||||
} catch {
|
||||
setError('Failed to load cohorts. Please try again.');
|
||||
setError("Failed to load cohorts. Please try again.");
|
||||
setCohorts([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -122,7 +132,7 @@ export default function CohortList() {
|
||||
setDeleteTarget(null);
|
||||
fetchCohorts(currentPage);
|
||||
} catch {
|
||||
setError('Failed to delete cohort. Please try again.');
|
||||
setError("Failed to delete cohort. Please try again.");
|
||||
} finally {
|
||||
setDeleteLoading(false);
|
||||
}
|
||||
@@ -134,13 +144,14 @@ export default function CohortList() {
|
||||
setError(null);
|
||||
setSuccessMsg(null);
|
||||
try {
|
||||
const newStatus = deactivateTarget.status === 'Active' ? 'Inactive' : 'Active';
|
||||
const newStatus =
|
||||
deactivateTarget.status === "Active" ? "Inactive" : "Active";
|
||||
await updateCohartStatus(deactivateTarget.id, { status: newStatus });
|
||||
setSuccessMsg(`Cohort "${deactivateTarget.name}" is now ${newStatus}.`);
|
||||
setDeactivateTarget(null);
|
||||
fetchCohorts(currentPage);
|
||||
} catch {
|
||||
setError('Failed to update cohort status. Please try again.');
|
||||
setError("Failed to update cohort status. Please try again.");
|
||||
} finally {
|
||||
setStatusLoading(false);
|
||||
}
|
||||
@@ -149,8 +160,8 @@ export default function CohortList() {
|
||||
// ─── Client-side filter on top of server data ──────────────────────────────
|
||||
|
||||
const displayedCohorts = cohorts
|
||||
.filter(c => c.name.toLowerCase().includes(search.toLowerCase()))
|
||||
.filter(c => (statusFilter ? c.status === statusFilter : true));
|
||||
.filter((c) => c.name.toLowerCase().includes(search.toLowerCase()))
|
||||
.filter((c) => (statusFilter ? c.status === statusFilter : true));
|
||||
|
||||
const startIndex = totalItems > 0 ? (currentPage - 1) * PAGE_SIZE + 1 : 0;
|
||||
const endIndex = Math.min(currentPage * PAGE_SIZE, totalItems);
|
||||
@@ -160,7 +171,7 @@ export default function CohortList() {
|
||||
const columns: Column<CohartResponse>[] = [
|
||||
{
|
||||
header: <HeaderLabel text="Cohort Name" />,
|
||||
accessor: row => (
|
||||
accessor: (row) => (
|
||||
<span className="text-[13px] font-semibold text-[#0F172B] leading-[18px] tracking-[0px]">
|
||||
{row.name}
|
||||
</span>
|
||||
@@ -168,18 +179,18 @@ export default function CohortList() {
|
||||
},
|
||||
{
|
||||
header: <HeaderLabel text="Description" />,
|
||||
accessor: row => <CellText text={row.description} />,
|
||||
accessor: (row) => <CellText text={row.description} />,
|
||||
},
|
||||
{
|
||||
header: <HeaderLabel text="Status" />,
|
||||
accessor: row => <CustomStatus status={row.status} />,
|
||||
accessor: (row) => <CustomStatus status={row.status} />,
|
||||
},
|
||||
{
|
||||
header: <HeaderLabel text="Action" />,
|
||||
className: 'text-right',
|
||||
accessor: row => (
|
||||
className: "text-right",
|
||||
accessor: (row) => (
|
||||
<CustomActionMenu>
|
||||
{row.status !== 'Active' && (
|
||||
{row.status !== "Active" && (
|
||||
<CustomActionItem
|
||||
icon={<CheckIcon size={15} />}
|
||||
variant="success"
|
||||
@@ -188,7 +199,7 @@ export default function CohortList() {
|
||||
Activate
|
||||
</CustomActionItem>
|
||||
)}
|
||||
{row.status === 'Active' && (
|
||||
{row.status === "Active" && (
|
||||
<CustomActionItem
|
||||
icon={<XIcon size={15} />}
|
||||
onClick={() => setDeactivateTarget(row)}
|
||||
@@ -262,7 +273,7 @@ export default function CohortList() {
|
||||
<CustomInput
|
||||
placeholder="Search cohorts..."
|
||||
value={search}
|
||||
onChange={e => handleSearchChange(e.target.value)}
|
||||
onChange={(e) => handleSearchChange(e.target.value)}
|
||||
leftIcon={<MagnifyingGlassIcon size={16} />}
|
||||
className="!bg-[#F3F6F5] !rounded-[10px] !h-[38px] !border !border-[#E5E7EB]"
|
||||
containerClassName="!gap-0"
|
||||
@@ -318,13 +329,19 @@ export default function CohortList() {
|
||||
isOpen={!!deactivateTarget}
|
||||
onClose={() => setDeactivateTarget(null)}
|
||||
onConfirm={handleToggleStatus}
|
||||
title={deactivateTarget?.status === 'Active' ? 'Deactivate Cohort' : 'Activate Cohort'}
|
||||
title={
|
||||
deactivateTarget?.status === "Active"
|
||||
? "Deactivate Cohort"
|
||||
: "Activate Cohort"
|
||||
}
|
||||
description={
|
||||
deactivateTarget?.status === 'Active'
|
||||
deactivateTarget?.status === "Active"
|
||||
? `"${deactivateTarget?.name}" will be deactivated and removed from active targeting.`
|
||||
: `"${deactivateTarget?.name}" will be reactivated and available for targeting.`
|
||||
}
|
||||
confirmText={deactivateTarget?.status === 'Active' ? 'Deactivate' : 'Activate'}
|
||||
confirmText={
|
||||
deactivateTarget?.status === "Active" ? "Deactivate" : "Activate"
|
||||
}
|
||||
cancelText="Cancel"
|
||||
variant="warning"
|
||||
isLoading={statusLoading}
|
||||
@@ -346,7 +363,10 @@ export default function CohortList() {
|
||||
<AddCohart
|
||||
isOpen={!!editTarget}
|
||||
onClose={() => setEditTarget(null)}
|
||||
onSuccess={() => { setEditTarget(null); fetchCohorts(currentPage); }}
|
||||
onSuccess={() => {
|
||||
setEditTarget(null);
|
||||
fetchCohorts(currentPage);
|
||||
}}
|
||||
editData={editTarget}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -7,7 +7,9 @@ interface RecentIncidentsTableProps {
|
||||
incidents: RecentIncident[];
|
||||
}
|
||||
|
||||
export const RecentIncidentsTable: React.FC<RecentIncidentsTableProps> = ({ incidents }) => {
|
||||
export const RecentIncidentsTable: React.FC<RecentIncidentsTableProps> = ({
|
||||
incidents,
|
||||
}) => {
|
||||
const columns: Column<RecentIncident>[] = [
|
||||
{
|
||||
header: "Recovery ID",
|
||||
@@ -48,9 +50,7 @@ export const RecentIncidentsTable: React.FC<RecentIncidentsTableProps> = ({ inci
|
||||
{
|
||||
header: "Value",
|
||||
accessor: (row) => (
|
||||
<span className="text-[13px] font-bold text-gray-900">
|
||||
{row.value}
|
||||
</span>
|
||||
<span className="text-[13px] font-bold text-gray-900">{row.value}</span>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
CustomLoader,
|
||||
Skeleton,
|
||||
} from '../../../components/custom';
|
||||
import CustomSuccessModal from '../../../components/custom/CustomSuccessModal';
|
||||
import {
|
||||
getJurisdictionOptions,
|
||||
getCohortOptions,
|
||||
@@ -257,6 +258,8 @@ export default function AddPolicyEngine() {
|
||||
|
||||
const [loadingPolicy, setLoadingPolicy] = useState(false);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [showSuccessModal, setShowSuccessModal] = useState(false);
|
||||
const [successTitle, setSuccessTitle] = useState('');
|
||||
|
||||
// Dynamic Options State (loaded directly from PolicyEngineApi)
|
||||
const [jurisdictionOptions, setJurisdictionOptions] = useState<OptionItem[]>([]);
|
||||
@@ -776,14 +779,13 @@ export default function AddPolicyEngine() {
|
||||
await createPolicy(payload);
|
||||
}
|
||||
|
||||
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 } });
|
||||
setSuccessTitle(isDeploy
|
||||
? (isEditMode ? "Policy Updated Successfully." : "Policy Created Successfully.")
|
||||
: "Policy Saved as Draft.");
|
||||
setShowSuccessModal(true);
|
||||
} catch (err) {
|
||||
console.error('Failed to save policy', err);
|
||||
alert('Failed to save policy. Please check input values and try again.');
|
||||
@@ -1549,6 +1551,18 @@ export default function AddPolicyEngine() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CustomSuccessModal
|
||||
isOpen={showSuccessModal}
|
||||
onClose={() => {
|
||||
setShowSuccessModal(false);
|
||||
navigate('/policy-engine');
|
||||
}}
|
||||
title={successTitle}
|
||||
label="POLICY NAME"
|
||||
cohortName={policyName}
|
||||
cohortStatus={status}
|
||||
cohortDescription={description}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -91,27 +91,24 @@ export default function PolicyEngineList() {
|
||||
|
||||
// ─── Fetch data ─────────────────────────────────────────────
|
||||
|
||||
const fetchPolicies = useCallback(
|
||||
(page: number) => {
|
||||
setLoading(true);
|
||||
getPolicies(page, PAGE_SIZE)
|
||||
.then((res) => {
|
||||
setPolicies(res.data || []);
|
||||
setTotalItems(res.total || 0);
|
||||
setTotalPages(res.totalPages || 1);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Failed to fetch policies:", err);
|
||||
setPolicies([]);
|
||||
setTotalItems(0);
|
||||
setTotalPages(1);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
const fetchPolicies = useCallback((page: number) => {
|
||||
setLoading(true);
|
||||
getPolicies(page, PAGE_SIZE)
|
||||
.then((res) => {
|
||||
setPolicies(res.data || []);
|
||||
setTotalItems(res.total || 0);
|
||||
setTotalPages(res.totalPages || 1);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Failed to fetch policies:", err);
|
||||
setPolicies([]);
|
||||
setTotalItems(0);
|
||||
setTotalPages(1);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchPolicies(currentPage);
|
||||
@@ -134,7 +131,9 @@ export default function PolicyEngineList() {
|
||||
setSuccessMsg(null);
|
||||
try {
|
||||
await deletePolicy(deleteTarget.id);
|
||||
setSuccessMsg(`Policy "${deleteTarget.policyName}" deleted successfully.`);
|
||||
setSuccessMsg(
|
||||
`Policy "${deleteTarget.policyName}" deleted successfully.`,
|
||||
);
|
||||
setDeleteTarget(null);
|
||||
fetchPolicies(currentPage);
|
||||
} catch (err) {
|
||||
@@ -151,7 +150,9 @@ export default function PolicyEngineList() {
|
||||
const nextStatus =
|
||||
deactivateTarget.status === "Active" ? "inactive" : "active";
|
||||
await updatePolicyStatus(deactivateTarget.id, nextStatus);
|
||||
setSuccessMsg(`Policy "${deactivateTarget.policyName}" is now ${nextStatus === 'active' ? 'Active' : 'Inactive'}.`);
|
||||
setSuccessMsg(
|
||||
`Policy "${deactivateTarget.policyName}" is now ${nextStatus === "active" ? "Active" : "Inactive"}.`,
|
||||
);
|
||||
setDeactivateTarget(null);
|
||||
fetchPolicies(currentPage);
|
||||
} catch (err) {
|
||||
@@ -160,10 +161,11 @@ export default function PolicyEngineList() {
|
||||
}
|
||||
};
|
||||
|
||||
const displayedPolicies = search
|
||||
? policies.filter(p =>
|
||||
p.policyName?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
p.jurisdiction?.toLowerCase().includes(search.toLowerCase())
|
||||
const displayedPolicies = search
|
||||
? policies.filter(
|
||||
(p) =>
|
||||
p.policyName?.toLowerCase().includes(search.toLowerCase()) ||
|
||||
p.jurisdiction?.toLowerCase().includes(search.toLowerCase()),
|
||||
)
|
||||
: policies;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import type { RecoveryIncident } from '../RecoveryIncidentsTypes';
|
||||
interface AddRecoveryIncidentsProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSuccess?: (createdCount: number, flightNumber: string, passengerName: string) => void;
|
||||
onSuccess?: (createdCount: number, flightNumber: string, passengerName: string, isEdit?: boolean) => void;
|
||||
incident?: RecoveryIncident | null;
|
||||
}
|
||||
|
||||
@@ -353,7 +353,8 @@ export default function AddRecoveryIncidents({ isOpen, onClose, onSuccess, incid
|
||||
onSuccess(
|
||||
selectedPassengers.length,
|
||||
formData.flightNumber || "TBD",
|
||||
selectedPassengers[0]?.passengerName || "Unknown"
|
||||
selectedPassengers[0]?.passengerName || "Unknown",
|
||||
!!incident
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,10 +26,16 @@ import {
|
||||
CustomSuccessModal,
|
||||
} from "../../../components/custom";
|
||||
import type { Column } from "../../../components/custom/CustomTable";
|
||||
import type { RecoveryIncident, MetricCardData } from "../RecoveryIncidentsTypes";
|
||||
import type {
|
||||
RecoveryIncident,
|
||||
MetricCardData,
|
||||
} from "../RecoveryIncidentsTypes";
|
||||
import AddRecoveryIncidents from "./AddRecoveryIncidents";
|
||||
import { MetricCard } from "./MetricCard";
|
||||
import { getRecoveryIncidents, getRecoveryMetrics } from "../RecoveryIncidentsApi";
|
||||
import {
|
||||
getRecoveryIncidents,
|
||||
getRecoveryMetrics,
|
||||
} from "../RecoveryIncidentsApi";
|
||||
import { formatDate } from "../../../utils/formatDate";
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
@@ -75,12 +81,17 @@ function BadgeLabel({ text }: { text: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
function getStatusVariant(status?: string): "success" | "error" | "warning" | "info" | "neutral" | "brand" {
|
||||
function getStatusVariant(
|
||||
status?: string,
|
||||
): "success" | "error" | "warning" | "info" | "neutral" | "brand" {
|
||||
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("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";
|
||||
}
|
||||
@@ -113,11 +124,15 @@ export default function RecoveryIncidentsList() {
|
||||
}, [isGrouped]);
|
||||
|
||||
useEffect(() => {
|
||||
sessionStorage.setItem("recovery_collapsedGroups", JSON.stringify(Array.from(collapsedGroups)));
|
||||
sessionStorage.setItem(
|
||||
"recovery_collapsedGroups",
|
||||
JSON.stringify(Array.from(collapsedGroups)),
|
||||
);
|
||||
}, [collapsedGroups]);
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [editingIncident, setEditingIncident] = useState<RecoveryIncident | null>(null);
|
||||
const [editingIncident, setEditingIncident] =
|
||||
useState<RecoveryIncident | null>(null);
|
||||
|
||||
// Selection
|
||||
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
||||
@@ -128,7 +143,8 @@ export default function RecoveryIncidentsList() {
|
||||
count: number;
|
||||
flightNumber: string;
|
||||
passengerName: string;
|
||||
}>({ isOpen: false, count: 0, flightNumber: '', passengerName: '' });
|
||||
isEdit?: boolean;
|
||||
}>({ isOpen: false, count: 0, flightNumber: "", passengerName: "", isEdit: false });
|
||||
|
||||
// ─── Fetch data ─────────────────────────────────────────────
|
||||
|
||||
@@ -139,7 +155,9 @@ export default function RecoveryIncidentsList() {
|
||||
setIncidents(data);
|
||||
getRecoveryMetrics()
|
||||
.then((m) => setMetrics(m))
|
||||
.catch((err) => console.error("Failed to fetch recovery metrics:", err));
|
||||
.catch((err) =>
|
||||
console.error("Failed to fetch recovery metrics:", err),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch recovery incidents", error);
|
||||
setIncidents([]);
|
||||
@@ -157,7 +175,7 @@ export default function RecoveryIncidentsList() {
|
||||
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"));
|
||||
});
|
||||
@@ -206,11 +224,14 @@ export default function RecoveryIncidentsList() {
|
||||
});
|
||||
};
|
||||
|
||||
const handleStatusChange = async (incident: RecoveryIncident, text: string) => {
|
||||
const handleStatusChange = async (
|
||||
incident: RecoveryIncident,
|
||||
text: string,
|
||||
) => {
|
||||
try {
|
||||
setError(null);
|
||||
setSuccessMsg(null);
|
||||
const { updateIncidentStatus } = await import('../RecoveryIncidentsApi');
|
||||
const { updateIncidentStatus } = await import("../RecoveryIncidentsApi");
|
||||
await updateIncidentStatus(incident.id, text);
|
||||
fetchIncidents();
|
||||
setSuccessMsg(`Incident status updated to ${text} successfully.`);
|
||||
@@ -253,11 +274,13 @@ export default function RecoveryIncidentsList() {
|
||||
const firstItem = groupItems[0];
|
||||
|
||||
// Calculate status summary for group header
|
||||
const pendingCount = groupItems.filter(i => {
|
||||
const pendingCount = groupItems.filter((i) => {
|
||||
const s = (i.status || "").toLowerCase();
|
||||
return s.includes("pending") || s.includes("review") || s.includes("new");
|
||||
return (
|
||||
s.includes("pending") || s.includes("review") || s.includes("new")
|
||||
);
|
||||
}).length;
|
||||
const approvedCount = groupItems.filter(i => {
|
||||
const approvedCount = groupItems.filter((i) => {
|
||||
const s = (i.status || "").toLowerCase();
|
||||
return s.includes("approved") || s.includes("active");
|
||||
}).length;
|
||||
@@ -326,13 +349,14 @@ export default function RecoveryIncidentsList() {
|
||||
/>
|
||||
),
|
||||
className: "w-[40px] pr-0",
|
||||
accessor: (row) => row.isGroupHeader ? null : (
|
||||
<CustomCheckBox
|
||||
checked={selectedIds.has(row.id)}
|
||||
onChange={() => toggleSelectOne(row.id)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
),
|
||||
accessor: (row) =>
|
||||
row.isGroupHeader ? null : (
|
||||
<CustomCheckBox
|
||||
checked={selectedIds.has(row.id)}
|
||||
onChange={() => toggleSelectOne(row.id)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: <HeaderLabel text="Recovery ID" />,
|
||||
@@ -375,7 +399,9 @@ export default function RecoveryIncidentsList() {
|
||||
{
|
||||
header: <HeaderLabel text="Category" />,
|
||||
accessor: (row) =>
|
||||
row.isGroupHeader ? null : row.category ? <BadgeLabel text={row.category} /> : null,
|
||||
row.isGroupHeader ? null : row.category ? (
|
||||
<BadgeLabel text={row.category} />
|
||||
) : null,
|
||||
},
|
||||
{
|
||||
header: <HeaderLabel text="Status" />,
|
||||
@@ -384,7 +410,10 @@ export default function RecoveryIncidentsList() {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
{(row.statuses || []).map((status, idx) => (
|
||||
<div key={idx} className={`px-2.5 py-1 rounded-full text-[11px] font-semibold leading-[16.5px] ${status.variant === 'warning' ? 'bg-[#FFFBE6] text-[#D48806]' : 'bg-[#E4FAE7] text-[#1B9869]'}`}>
|
||||
<div
|
||||
key={idx}
|
||||
className={`px-2.5 py-1 rounded-full text-[11px] font-semibold leading-[16.5px] ${status.variant === "warning" ? "bg-[#FFFBE6] text-[#D48806]" : "bg-[#E4FAE7] text-[#1B9869]"}`}
|
||||
>
|
||||
{status.text}
|
||||
</div>
|
||||
))}
|
||||
@@ -419,7 +448,8 @@ export default function RecoveryIncidentsList() {
|
||||
{
|
||||
header: <HeaderLabel text="Action" />,
|
||||
accessor: (row) => {
|
||||
const key = (row as any).groupKey || row.flightNumber || row.recoveryCode;
|
||||
const key =
|
||||
(row as any).groupKey || row.flightNumber || row.recoveryCode;
|
||||
return (
|
||||
<div className="flex items-center pl-2">
|
||||
{row.isGroupHeader ? (
|
||||
@@ -446,12 +476,16 @@ export default function RecoveryIncidentsList() {
|
||||
setEditingIncident(row);
|
||||
setIsModalOpen(true);
|
||||
}}
|
||||
icon={<PencilSimpleIcon size={16} className="text-yellow-500" />}
|
||||
icon={
|
||||
<PencilSimpleIcon size={16} className="text-yellow-500" />
|
||||
}
|
||||
>
|
||||
Edit Incident
|
||||
</CustomActionItem>
|
||||
<CustomActionItem
|
||||
icon={<CheckCircleIcon size={16} className="text-green-500" />}
|
||||
icon={
|
||||
<CheckCircleIcon size={16} className="text-green-500" />
|
||||
}
|
||||
onClick={() => handleStatusChange(row, "Approved")}
|
||||
>
|
||||
Approve
|
||||
@@ -480,7 +514,7 @@ export default function RecoveryIncidentsList() {
|
||||
const isAnyGroupExpanded = useMemo(() => {
|
||||
if (!isGrouped) return true;
|
||||
const allGroupKeys = Array.from(
|
||||
new Set(incidents.map((i) => i.flightNumber || "Other"))
|
||||
new Set(incidents.map((i) => i.flightNumber || "Other")),
|
||||
);
|
||||
return allGroupKeys.some((key) => !collapsedGroups.has(key));
|
||||
}, [isGrouped, incidents, collapsedGroups]);
|
||||
@@ -491,7 +525,9 @@ export default function RecoveryIncidentsList() {
|
||||
}
|
||||
return columns.filter((col) => {
|
||||
const headerText =
|
||||
typeof col.header === "object" && col.header !== null && "props" in col.header
|
||||
typeof col.header === "object" &&
|
||||
col.header !== null &&
|
||||
"props" in col.header
|
||||
? (col.header as any).props.text
|
||||
: "";
|
||||
return (
|
||||
@@ -597,7 +633,8 @@ export default function RecoveryIncidentsList() {
|
||||
itemName="Recovery Incidents"
|
||||
onRowClick={(row) => {
|
||||
if (row.isGroupHeader) {
|
||||
const key = (row as any).groupKey || row.flightNumber || row.recoveryCode;
|
||||
const key =
|
||||
(row as any).groupKey || row.flightNumber || row.recoveryCode;
|
||||
toggleGroup(key);
|
||||
} else {
|
||||
navigate(`/recovery/${row.id}`);
|
||||
@@ -620,12 +657,13 @@ export default function RecoveryIncidentsList() {
|
||||
setEditingIncident(null);
|
||||
fetchIncidents(); // Refresh list
|
||||
}}
|
||||
onSuccess={(createdCount, flightNumber, passengerName) => {
|
||||
onSuccess={(createdCount, flightNumber, passengerName, isEdit) => {
|
||||
setSuccessModalData({
|
||||
isOpen: true,
|
||||
count: createdCount,
|
||||
flightNumber,
|
||||
passengerName,
|
||||
isEdit,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -633,18 +671,24 @@ export default function RecoveryIncidentsList() {
|
||||
{/* Success Modal */}
|
||||
<CustomSuccessModal
|
||||
isOpen={successModalData.isOpen}
|
||||
onClose={() => setSuccessModalData((prev) => ({ ...prev, isOpen: false }))}
|
||||
onClose={() =>
|
||||
setSuccessModalData((prev) => ({ ...prev, isOpen: false }))
|
||||
}
|
||||
title={
|
||||
successModalData.count > 1
|
||||
? "Recovery Incidents Created."
|
||||
: "Recovery Incident Created."
|
||||
successModalData.isEdit
|
||||
? (successModalData.count > 1 ? "Recovery Incidents Updated." : "Recovery Incident Updated.")
|
||||
: (successModalData.count > 1 ? "Recovery Incidents Created." : "Recovery Incident Created.")
|
||||
}
|
||||
label="FLIGHT & PASSENGER DETAILS"
|
||||
cohortName={`Flight ${successModalData.flightNumber}`}
|
||||
cohortDescription={
|
||||
successModalData.count > 1
|
||||
? `Successfully logged incidents for ${successModalData.count} passengers, including ${successModalData.passengerName}.`
|
||||
: `Successfully logged incident for ${successModalData.passengerName}.`
|
||||
successModalData.isEdit
|
||||
? (successModalData.count > 1
|
||||
? `Successfully updated incidents for ${successModalData.count} passengers, including ${successModalData.passengerName}.`
|
||||
: `Successfully updated incident for ${successModalData.passengerName}.`)
|
||||
: (successModalData.count > 1
|
||||
? `Successfully logged incidents for ${successModalData.count} passengers, including ${successModalData.passengerName}.`
|
||||
: `Successfully logged incident for ${successModalData.passengerName}.`)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user