MPEDA / src /pages /JdApprovalQueue.tsx
sarveshpatel's picture
Upload 139 files
f305a41 verified
import { useMemo, useState, useCallback } from "react";
import { hatcheryApplications, farmApplications, getStatusColor, formatStatus } from "@/data/dummyData";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { ResponsiveTable } from "@/components/ui/responsive-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { ApprovalFlow } from "@/components/ApprovalFlow";
import { applyCertificationOverrides, updateCertificationOverride, EntityType } from "@/lib/certificationLocal";
interface FlatAppRow {
id: string;
applicationNo: string;
entityName: string;
entityType: "hatchery" | "farm";
ownerName: string;
status: string;
currentStage: string;
submittedDate: string;
}
const JdApprovalQueue = () => {
const baseRows = useMemo<FlatAppRow[]>(() => {
const hatcheryRows: FlatAppRow[] = hatcheryApplications.map((h) => ({
id: h.id,
applicationNo: h.applicationNo,
entityName: h.hatcheryName,
entityType: "hatchery" as const,
ownerName: h.ownerName,
status: h.status,
currentStage: h.currentStage,
submittedDate: h.submittedDate,
}));
const farmRows: FlatAppRow[] = farmApplications.map((f) => ({
id: f.id,
applicationNo: f.applicationNo,
entityName: f.farmName,
entityType: "farm" as const,
ownerName: f.farmerName,
status: f.status,
currentStage: f.currentStage,
submittedDate: f.submittedDate,
}));
const all = [...hatcheryRows, ...farmRows];
return all;
}, []);
const [refreshToken, setRefreshToken] = useState(0);
const [selectedRow, setSelectedRow] = useState<FlatAppRow | null>(null);
const allRows = useMemo<FlatAppRow[]>(
() => applyCertificationOverrides(baseRows),
[baseRows, refreshToken],
);
const rows = useMemo<FlatAppRow[]>(
() =>
allRows.filter(
(row) =>
row.currentStage === "jd_approval" &&
row.status !== "rejected" &&
row.status !== "certified",
),
[allRows],
);
const stats = useMemo(() => {
const total = allRows.length;
const atCc = allRows.filter(
(row) => row.currentStage === "cc_review" && row.status !== "rejected",
).length;
const atJd = allRows.filter(
(row) =>
row.currentStage === "jd_approval" &&
row.status !== "rejected" &&
row.status !== "certified",
).length;
const atDirector = allRows.filter(
(row) =>
row.currentStage === "director_approval" &&
row.status !== "rejected" &&
row.status !== "certified",
).length;
const certified = allRows.filter((row) => row.status === "certified").length;
return { total, atCc, atJd, atDirector, certified };
}, [allRows]);
const handleJdAction = useCallback(
(row: FlatAppRow, action: "forward" | "reject") => {
const entityType = row.entityType as EntityType;
if (action === "forward") {
updateCertificationOverride(entityType, row.id, {
currentStage: "director_approval",
status: "pending_approval",
});
} else {
updateCertificationOverride(entityType, row.id, {
status: "rejected",
});
}
setRefreshToken((v) => v + 1);
},
[],
);
return (
<div className="space-y-6 animate-fade-in">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div>
<h1 className="text-2xl font-bold font-display">JD Approval Queue</h1>
<p className="text-muted-foreground text-sm">
Manage applications waiting at Joint Director approval stage.
</p>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<Card className="p-4">
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-primary/10" />
<div>
<p className="text-sm text-muted-foreground">Total in certification pipeline</p>
<p className="text-2xl font-bold">{stats.total}</p>
</div>
</div>
</Card>
<Card className="p-4">
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-orange-100" />
<div>
<p className="text-sm text-muted-foreground">Waiting at JD</p>
<p className="text-2xl font-bold text-orange-600">{stats.atJd}</p>
</div>
</div>
</Card>
<Card className="p-4">
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-blue-100" />
<div>
<p className="text-sm text-muted-foreground">With Director</p>
<p className="text-2xl font-bold text-blue-600">{stats.atDirector}</p>
</div>
</div>
</Card>
<Card className="p-4">
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-emerald-100" />
<div>
<p className="text-sm text-muted-foreground">Certified</p>
<p className="text-2xl font-bold text-emerald-600">{stats.certified}</p>
</div>
</div>
</Card>
</div>
<div className="grid grid-cols-1 lg:grid-cols-[minmax(0,2fr)_minmax(0,1.1fr)] gap-6">
<Card>
<CardHeader>
<CardTitle className="text-base">Applications pending JD approval</CardTitle>
</CardHeader>
<CardContent className="p-0 sm:p-6">
<ResponsiveTable>
<Table>
<TableHeader>
<TableRow>
<TableHead>Application</TableHead>
<TableHead>Entity</TableHead>
<TableHead>Type</TableHead>
<TableHead>Owner</TableHead>
<TableHead>Submitted</TableHead>
<TableHead>Stage</TableHead>
<TableHead>Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map((row) => (
<TableRow
key={`${row.entityType}-${row.id}`}
className="table-row-hover cursor-pointer"
onClick={() => setSelectedRow(row)}
>
<TableCell className="font-mono text-sm font-medium">
{row.applicationNo}
</TableCell>
<TableCell>{row.entityName}</TableCell>
<TableCell>
<Badge variant="outline" className="capitalize">
{row.entityType}
</Badge>
</TableCell>
<TableCell>{row.ownerName}</TableCell>
<TableCell className="text-sm text-muted-foreground">
{row.submittedDate}
</TableCell>
<TableCell>
<Badge variant="outline" className="capitalize">
{formatStatus(row.currentStage)}
</Badge>
</TableCell>
<TableCell>
<Badge className={getStatusColor(row.status)}>
{formatStatus(row.status)}
</Badge>
</TableCell>
</TableRow>
))}
{rows.length === 0 && (
<TableRow>
<TableCell
colSpan={7}
className="py-8 text-center text-sm text-muted-foreground"
>
No applications currently at JD approval stage.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</ResponsiveTable>
</CardContent>
</Card>
<Card className="h-full">
<CardHeader>
<CardTitle className="text-base">
{selectedRow ? `Application ${selectedRow.applicationNo}` : "Application details"}
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{!selectedRow ? (
<p className="text-sm text-muted-foreground">
Select an application from the list to view details and take action.
</p>
) : (
<>
<div className="grid grid-cols-1 gap-2 text-sm">
<div className="flex justify-between gap-4">
<span className="text-muted-foreground">Entity</span>
<span className="font-medium text-right">
{selectedRow.entityName}
</span>
</div>
<div className="flex justify-between gap-4">
<span className="text-muted-foreground">Type</span>
<span className="font-medium capitalize text-right">
{selectedRow.entityType}
</span>
</div>
<div className="flex justify-between gap-4">
<span className="text-muted-foreground">Owner</span>
<span className="font-medium text-right">
{selectedRow.ownerName}
</span>
</div>
<div className="flex justify-between gap-4">
<span className="text-muted-foreground">Submitted</span>
<span className="font-medium text-right">
{selectedRow.submittedDate}
</span>
</div>
<div className="flex justify-between gap-4">
<span className="text-muted-foreground">Current stage</span>
<span className="font-medium text-right">
{formatStatus(selectedRow.currentStage)}
</span>
</div>
<div className="flex justify-between gap-4">
<span className="text-muted-foreground">Status</span>
<span className="font-medium text-right">
{formatStatus(selectedRow.status)}
</span>
</div>
</div>
<div className="space-y-2">
<p className="text-xs text-muted-foreground uppercase tracking-wide">
Approval flow
</p>
<ApprovalFlow currentStage={selectedRow.currentStage} />
</div>
<div className="flex justify-end gap-2">
<Button
size="sm"
variant="outline"
onClick={() => {
handleJdAction(selectedRow, "forward");
setSelectedRow(null);
}}
>
Approve &amp; Send to Director
</Button>
<Button
size="sm"
variant="outline"
className="text-red-600 border-red-200"
onClick={() => {
handleJdAction(selectedRow, "reject");
setSelectedRow(null);
}}
>
Reject at JD
</Button>
</div>
</>
)}
</CardContent>
</Card>
</div>
</div>
);
};
export default JdApprovalQueue;