Files
Project-Management-V2/frontend/app/main/configuration/company-data/components/role-table/index.tsx
Your NamebaishaliHolocron b9ac5ae0b2 first commit
2026-06-15 12:57:03 +05:30

151 lines
5.4 KiB
TypeScript

"use client";
import { useEffect, useMemo, useRef, useState } from "react";
import { Search, Briefcase } from "lucide-react";
import {
ColumnDef,
DataTableLayout,
Card,
Input,
} from "ikoncomponents";
import { getAllRoles } from "@/app/utils/api/companyData/roleApi.ts";
interface RoleData {
id: string;
role: string;
}
function RoleDataTable() {
const [roleTableData, setRoleTableData] = useState<RoleData[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [search, setSearch] = useState("");
// DataTableLayout defaults to list view and has no prop to start in grid, so
// once mounted we click its built-in "Grid View" toggle once to default to cards.
const tableContainerRef = useRef<HTMLDivElement>(null);
const defaultedToGrid = useRef(false);
const filteredRoleTableData = useMemo(() => {
if (!search.trim()) return roleTableData;
return roleTableData.filter((roleData) =>
roleData.role?.toLowerCase().includes(search.toLowerCase()),
);
}, [roleTableData, search]);
const fetchRoleTableData = async () => {
setIsLoading(true);
try {
const roleData = await getAllRoles();
setRoleTableData(roleData || []);
} catch (error) {
console.error("Error fetching role data:", error);
} finally {
setIsLoading(false);
}
};
useEffect(() => {
fetchRoleTableData();
}, []);
// Switch DataTableLayout into grid (card) view as soon as it mounts, once.
useEffect(() => {
if (isLoading || defaultedToGrid.current) return;
const gridButton =
tableContainerRef.current?.querySelector<HTMLButtonElement>(
'button[title="Grid View"]',
);
if (gridButton) {
gridButton.click();
defaultedToGrid.current = true;
}
}, [isLoading]);
// ikoncomponents passes the row object directly to cell(), not { row }.
const columns: ColumnDef<RoleData>[] = [
{
accessorKey: "role",
header: "Role",
cell: (row) => <span>{row.role || "n/a"}</span>,
},
];
return (
<div className="space-y-5 p-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-lg font-semibold">Roles</h1>
<p className="mt-1 text-xs text-muted-foreground">
{filteredRoleTableData.length} of {roleTableData.length} roles
</p>
</div>
</div>
<div ref={tableContainerRef}>
<DataTableLayout
data={filteredRoleTableData}
columns={columns}
extraTools={{
keyExtractor: (row: RoleData) => row.id,
totalPages: 1,
currentPage: 1,
isLoading,
onReload: fetchRoleTableData,
actionNode: (
<div className="flex w-full min-w-[260px] flex-1 items-center gap-2 rounded-lg border px-3 h-9">
<Search className="h-4 w-4 shrink-0 text-muted-foreground" />
<div className="h-4 w-px shrink-0 bg-border" />
<Input
placeholder="Search roles..."
className="h-8 border-none p-0 text-sm shadow-none placeholder:text-muted-foreground focus-visible:ring-0"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
),
gridComponent: (data: RoleData[]) => (
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4">
{data.length > 0 ? (
data.map((roleItem) => (
<Card
key={roleItem.id}
// flex-row + py-4 explicitly override the base Card's
// `flex flex-col gap-6 py-6`, otherwise the icon stacks on
// top and the content centers with a lot of empty space.
className="group relative flex flex-row items-center gap-3.5 overflow-hidden rounded-xl border py-4! px-4 shadow-sm transition-all duration-200 hover:-translate-y-0.5 hover:border-primary/40 hover:shadow-md"
>
{/* Left accent rail */}
<span className="absolute inset-y-0 left-0 w-1 bg-primary/60 opacity-0 transition-opacity duration-200 group-hover:opacity-100" />
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary transition-transform duration-200 group-hover:scale-105">
<Briefcase className="h-5 w-5" />
</div>
<div className="min-w-0">
<p className="text-[10px] font-semibold uppercase tracking-[0.08em] text-muted-foreground">
Role
</p>
<p
className="truncate text-sm font-semibold leading-tight text-foreground"
title={roleItem.role}
>
{roleItem.role || "-"}
</p>
</div>
</Card>
))
) : (
<div className="col-span-2 flex items-center justify-center rounded-lg border border-dashed p-8 text-muted-foreground sm:col-span-3 lg:col-span-4">
No roles found.
</div>
)}
</div>
),
}}
/>
</div>
</div>
);
}
export default RoleDataTable;