feat: implement recovery incidents list, audit logs table, and policy engine creation components

This commit is contained in:
azeeee05
2026-08-18 14:14:04 +05:30
parent b25c949d28
commit 2bc8ba9bdc
4 changed files with 56 additions and 27 deletions
+35 -10
View File
@@ -10,7 +10,8 @@ import {
CustomInput,
CustomStatus,
Skeleton,
CustomButton
CustomButton,
CustomDropdown
} from "../../../components/custom";
import type { Column } from "../../../components/custom/CustomTable";
import type { AuditLog, AuditLogFilters } from "../AuditLogsTypes";
@@ -71,6 +72,7 @@ export default function AuditLogsList() {
// Filters
const [search, setSearch] = useState("");
const [daysFilter, setDaysFilter] = useState("7");
// ─── Fetch ─────────────────────────────────────────────────────────────────
@@ -83,6 +85,20 @@ export default function AuditLogsList() {
limit: PAGE_SIZE,
};
if (daysFilter !== "all") {
const now = new Date();
const past = new Date();
if (daysFilter === "today") {
past.setHours(0, 0, 0, 0);
} else {
past.setDate(now.getDate() - parseInt(daysFilter, 10));
}
filters.dateFrom = past.toISOString();
filters.dateTo = now.toISOString();
}
const result = await getAuditLogs(filters);
// Client-side search on entityLabel / entityId
@@ -106,7 +122,7 @@ export default function AuditLogsList() {
setLoading(false);
}
},
[search]
[search, daysFilter]
);
useEffect(() => {
@@ -116,7 +132,7 @@ export default function AuditLogsList() {
// Reset to page 1 when filters change
useEffect(() => {
setCurrentPage(1);
}, [search]);
}, [search, daysFilter]);
const handleExportCSV = () => {
if (logs.length === 0) return;
@@ -272,13 +288,22 @@ export default function AuditLogsList() {
}
rightHeaderActions={
<div className="flex items-center gap-3">
<CustomButton
variant="outlined"
className="flex items-center gap-2 h-[40px] px-4 rounded-[8px] border border-[#1E8E3E] text-[#1E8E3E] text-[13px] font-bold hover:bg-[#E6F4EA] transition-colors cursor-pointer"
>
<CalendarBlank size={16} weight="bold" />
Range: Last 7 Days
</CustomButton>
<div className="w-[180px]">
<CustomDropdown
value={daysFilter}
onChange={setDaysFilter}
leftIcon={<CalendarBlank size={16} weight="bold" className="text-[#1E8E3E]" />}
searchable={false}
options={[
{ label: "Today", value: "today" },
{ label: "Last 7 Days", value: "7" },
{ label: "Last 30 Days", value: "30" },
{ label: "Last 90 Days", value: "90" },
{ label: "All Time", value: "all" },
]}
className="!border-[#1E8E3E] !bg-[#E6F4EA] !h-[40px] hover:!bg-[#E6F4EA]"
/>
</div>
<CustomButton
variant="outlined"
onClick={handleExportCSV}
@@ -769,7 +769,7 @@ export default function AddPolicyEngine() {
<div className="flex flex-col h-full bg-white min-h-screen">
{/* ─── Header ────────────────────────────────────────────────────── */}
<div className="flex items-center justify-between px-8 py-4 bg-white border-b border-gray-200">
<div className="flex items-center justify-between py-4 bg-white border-b border-gray-200">
<div className="flex items-start gap-4">
<button
onClick={() => navigate('/policy-engine')}
@@ -787,7 +787,7 @@ export default function AddPolicyEngine() {
</div>
{/* ─── Body Content ──────────────────────────────────────────────── */}
<div className="flex-1 overflow-y-auto px-8 py-6 pb-32">
<div className="flex-1 overflow-y-auto [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden py-6 pb-32">
<div className="w-full flex flex-col gap-6">
{/* Policy Information Card */}
@@ -1241,7 +1241,7 @@ export default function AddPolicyEngine() {
</div>
{/* ─── Sticky Footer ─────────────────────────────────────────────── */}
<div className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-8 py-4 flex items-center justify-between z-10 shadow-[0_-4px_10px_rgba(0,0,0,0.02)]">
<div className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-4 py-4 flex items-center justify-between z-10 shadow-[0_-4px_10px_rgba(0,0,0,0.02)]">
<div className="flex items-center gap-3">
<span className="text-[14px] font-semibold text-gray-600">Status:</span>
<CustomStatus status={status} />
@@ -233,7 +233,7 @@ export default function RecoveryIncidentsList() {
const headerStatuses = [
{ text: `${pendingCount} Pending`, variant: "warning" as const },
{ text: `${approvedCount} Approved`, variant: "success" as const },
{ text: `${approvedCount} App`, variant: "success" as const },
];
// Calculate sum of values
@@ -264,12 +264,12 @@ export default function RecoveryIncidentsList() {
result.push({
...child,
groupKey: groupKey,
});
} as any);
});
}
});
return result;
return result.map((r, i) => ({ ...r, globalIndex: i }));
}, [filteredIncidents, isGrouped, collapsedGroups]);
const totalItems = displayData.length;
@@ -306,7 +306,7 @@ export default function RecoveryIncidentsList() {
{
header: <HeaderLabel text="Recovery ID" />,
accessor: (row) => (
<div>
<div className={row.isGroupHeader ? "-ml-[40px]" : ""}>
<PrimaryText text={row.recoveryCode} />
<SecondaryText text={formatDate(row.date)} />
</div>
@@ -344,7 +344,7 @@ export default function RecoveryIncidentsList() {
{
header: <HeaderLabel text="Category" />,
accessor: (row) =>
row.category ? <BadgeLabel text={row.category} /> : null,
row.isGroupHeader ? null : row.category ? <BadgeLabel text={row.category} /> : null,
},
{
header: <HeaderLabel text="Status" />,
@@ -353,11 +353,9 @@ export default function RecoveryIncidentsList() {
return (
<div className="flex items-center gap-2">
{(row.statuses || []).map((status, idx) => (
<CustomStatus
key={idx}
status={status.text}
variant={status.variant}
/>
<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>
))}
</div>
);
@@ -389,11 +387,10 @@ export default function RecoveryIncidentsList() {
},
{
header: <HeaderLabel text="Action" />,
className: "text-right",
accessor: (row) => {
const key = (row as any).groupKey || row.flightNumber || row.recoveryCode;
return (
<div className="flex justify-end pr-2">
<div className="flex items-center pl-2">
{row.isGroupHeader ? (
<div
className="cursor-pointer text-gray-500 hover:text-gray-900 transition-colors p-1"
@@ -561,7 +558,12 @@ export default function RecoveryIncidentsList() {
navigate(`/recovery/${row.id}`);
}
}}
rowClassName={(row) => row.isGroupHeader ? "bg-white border-b border-gray-100 hover:bg-gray-50/60" : "bg-[#F9FAFB] border-transparent cursor-pointer hover:bg-[#F3F4F6]"}
rowClassName={(row: any) => {
if (row.isGroupHeader) {
return "bg-white border-b border-gray-100 cursor-pointer hover:bg-gray-50/60";
}
return "bg-[#F9FAFB] border-transparent cursor-pointer hover:bg-[#F3F4F6]";
}}
/>
{/* Modal */}
+3 -1
View File
@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { useParams, useNavigate } from 'react-router-dom';
import { User, Checks, X, ClockCounterClockwiseIcon, ArrowLeftIcon, ArrowsClockwiseIcon, AirplaneTiltIcon } from '@phosphor-icons/react';
import { CustomButton, CustomTabs, CustomBackButton, CustomStatus } from '../../../components/custom';
import SummaryTab from './SummaryTab';
@@ -22,6 +22,7 @@ function getStatusVariant(status?: string): "success" | "error" | "warning" | "i
export default function RecoveryIncidentTabs() {
const { id } = useParams();
const navigate = useNavigate();
const [activeTab, setActiveTab] = useState('Summary');
const [incident, setIncident] = useState<RecoveryIncident | null>(null);
const [loading, setLoading] = useState(true);
@@ -50,6 +51,7 @@ export default function RecoveryIncidentTabs() {
try {
const updated = await updateIncidentStatus(id, newStatus);
setIncident(updated);
navigate('/recovery');
} catch (err) {
console.error("Failed to update incident status:", err);
} finally {