190 lines
5.9 KiB
TypeScript
190 lines
5.9 KiB
TypeScript
import { useState } from "react";
|
||
import { Box, HStack, Image, Stack, Table, Text } from "@chakra-ui/react";
|
||
import {
|
||
PaginationItems,
|
||
PaginationNextTrigger,
|
||
PaginationPrevTrigger,
|
||
PaginationRoot,
|
||
} from "./ui/pagination";
|
||
import EmptyFile from "../assets/empty-file.png";
|
||
// import {
|
||
// PaginationItems,
|
||
// PaginationNextTrigger,
|
||
// PaginationPrevTrigger,
|
||
// PaginationRoot,
|
||
// } from "./ui/pagination";
|
||
|
||
interface TableProps {
|
||
tableHeadRow: string[];
|
||
data: Record<string, any>[];
|
||
sortableColumns?: string[]; // Specify which columns are sortable
|
||
paginationData?: {
|
||
current_page: number;
|
||
last_page: number;
|
||
per_page: number;
|
||
total: number;
|
||
};
|
||
onPageChange?: (page: number) => void;
|
||
isLoading?: boolean;
|
||
isError?: boolean;
|
||
}
|
||
|
||
const DataTable: React.FC<TableProps> = ({
|
||
tableHeadRow,
|
||
data,
|
||
sortableColumns = [],
|
||
paginationData,
|
||
onPageChange,
|
||
isLoading,
|
||
isError
|
||
}: TableProps) => {
|
||
const { current_page = 1, last_page = 1 } = paginationData || {};
|
||
const [sortConfig, setSortConfig] = useState<{
|
||
key: string;
|
||
direction: "asc" | "desc";
|
||
} | null>(null);
|
||
|
||
const handlePageChange = (details: { page: number }) => {
|
||
const newPage = details.page;
|
||
if (newPage >= 1 && newPage <= last_page) {
|
||
onPageChange?.(newPage);
|
||
}
|
||
};
|
||
|
||
const handleSort = (column: string) => {
|
||
if (!sortableColumns.includes(column)) return;
|
||
|
||
let direction: "asc" | "desc" = "asc";
|
||
if (
|
||
sortConfig &&
|
||
sortConfig.key === column &&
|
||
sortConfig.direction === "asc"
|
||
) {
|
||
direction = "desc";
|
||
}
|
||
|
||
const newSortConfig = { key: column, direction };
|
||
setSortConfig(newSortConfig);
|
||
onPageChange?.(1);
|
||
};
|
||
|
||
return (
|
||
<Stack mt={0} color={"#000000CC"}>
|
||
<Table.ScrollArea mb={3}>
|
||
<Table.Root size="sm" variant={"line"} stickyHeader>
|
||
<Table.Header>
|
||
<Table.Row bg={"#02A0A0"}>
|
||
{tableHeadRow.map((item, index) => (
|
||
<Table.ColumnHeader
|
||
color="white"
|
||
fontSize={"xs"}
|
||
fontWeight={600}
|
||
px={4}
|
||
p={3}
|
||
textAlign={
|
||
index === tableHeadRow.length - 1 ? "center" : "left"
|
||
}
|
||
key={index}
|
||
border={"none"}
|
||
onClick={() => handleSort(item)}
|
||
cursor={
|
||
sortableColumns.includes(item) ? "pointer" : "default"
|
||
}
|
||
_hover={
|
||
sortableColumns.includes(item)
|
||
? { textDecoration: "underline" }
|
||
: {}
|
||
}
|
||
>
|
||
{item}
|
||
{sortableColumns.includes(item) &&
|
||
sortConfig?.key === item && (
|
||
<span style={{ marginLeft: "4px" }}>
|
||
{sortConfig.direction === "asc" ? "▲" : "▼"}
|
||
</span>
|
||
)}
|
||
</Table.ColumnHeader>
|
||
))}
|
||
</Table.Row>
|
||
</Table.Header>
|
||
{isLoading ? (
|
||
<Box textAlign={"center"} py={20} position="absolute" w="84%">
|
||
<Text fontSize={"18px"} fontWeight={500} mt={2}>
|
||
Loading...
|
||
</Text>
|
||
</Box>
|
||
) : isError ? (
|
||
<Box textAlign={"center"} py={20} position="absolute" w="84%">
|
||
<Image src={EmptyFile} alt="No data" maxW="65px" mx="auto" />
|
||
<Text fontSize={"18px"} fontWeight={500} mt={2}>
|
||
No records found — they’ll appear here if available.
|
||
</Text>
|
||
</Box>
|
||
) : (
|
||
<Table.Body h="100%">
|
||
{data.length === 0 && (
|
||
<Box textAlign={"center"} py={20} position="absolute" w="84%">
|
||
<Image src={EmptyFile} alt="No data" maxW="65px" mx="auto" />
|
||
<Text fontSize={"18px"} fontWeight={500} mt={2}>
|
||
No records found — they’ll appear here if available.
|
||
</Text>
|
||
</Box>
|
||
)}
|
||
{data.map((item: any, index) => (
|
||
<Table.Row
|
||
key={index}
|
||
bg={index % 2 === 0 ? "#fff" : "#007F3310"}
|
||
>
|
||
{tableHeadRow.map((heading, colIndex) => (
|
||
<Table.Cell
|
||
px={4}
|
||
p={2}
|
||
key={`${index}-${colIndex}`}
|
||
fontSize={"xs"}
|
||
fontWeight={500}
|
||
border={"none"}
|
||
>
|
||
{(() => {
|
||
const words = item[heading]?.toString().split(" ") || [];
|
||
return words.length > 5
|
||
? `${words.slice(0, 5).join(" ")}...`
|
||
: item[heading];
|
||
})()}
|
||
</Table.Cell>
|
||
))}
|
||
</Table.Row>
|
||
))}
|
||
</Table.Body>
|
||
)}
|
||
|
||
</Table.Root>
|
||
</Table.ScrollArea>
|
||
{last_page > 1 && !isLoading && !isError && (
|
||
<PaginationRoot
|
||
count={paginationData?.total ?? 0}
|
||
pageSize={paginationData?.per_page ?? 0}
|
||
page={current_page}
|
||
onPageChange={handlePageChange}
|
||
size={"xs"}
|
||
siblingCount={1}
|
||
mb={4}
|
||
>
|
||
<HStack justifyContent="flex-end">
|
||
<PaginationPrevTrigger
|
||
onClick={() => handlePageChange({ page: current_page - 1 })}
|
||
disabled={current_page === 1}
|
||
/>
|
||
<PaginationItems />
|
||
<PaginationNextTrigger
|
||
onClick={() => handlePageChange({ page: current_page + 1 })}
|
||
disabled={current_page === last_page}
|
||
/>
|
||
</HStack>
|
||
</PaginationRoot>
|
||
)}
|
||
</Stack>
|
||
);
|
||
};
|
||
|
||
export default DataTable;
|