2025-08-06 09:41:20 +02:00

192 lines
5.2 KiB
TypeScript

"use client";
import { useState, useEffect, useMemo } from "react";
import {
Box,
TextField,
IconButton,
InputAdornment,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Checkbox,
Paper,
MenuItem,
InputLabel,
Select,
FormControl,
SelectChangeEvent,
debounce,
} from "@mui/material";
import SearchIcon from "@mui/icons-material/Search";
import { useRouter, useSearchParams } from "next/navigation";
export interface TableColumn<T> {
field: keyof T | string;
headerName: string;
render?: (value: unknown, row: T) => React.ReactNode;
}
interface MenuItemOption {
value: string;
label?: string;
}
interface DynamicTableProps<T extends { id: string | number }> {
data: {
rows: T[];
columns: TableColumn<T>[];
actions: MenuItemOption[];
};
searchParamKey?: string;
}
export function ApproveTable<T extends { id: string | number }>({
data,
searchParamKey = "merchantId",
}: DynamicTableProps<T>) {
const { rows, columns, actions } = data;
const router = useRouter();
const searchParams = useSearchParams();
const [selected, setSelected] = useState<(string | number)[]>([]);
const [search, setSearch] = useState("");
useEffect(() => {
const urlValue = searchParams.get(searchParamKey) ?? "";
setSearch(urlValue);
}, [searchParams, searchParamKey]);
const updateURL = useMemo(
() =>
debounce((value: string) => {
const params = new URLSearchParams(searchParams.toString());
if (value) params.set(searchParamKey, value);
else params.delete(searchParamKey);
router.replace(`?${params.toString()}`, { scroll: false });
}, 400),
[router, searchParams, searchParamKey],
);
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setSearch(value);
updateURL(value);
};
const handleCheckboxChange = (id: string | number, checked: boolean) => {
setSelected((prev) =>
checked ? [...prev, id] : prev.filter((x) => x !== id),
);
};
const handleToggleAll = (checked: boolean) => {
setSelected(checked ? rows.map((r) => r.id) : []);
};
const [action, setAction] = useState("");
const handleActionChange = (e: SelectChangeEvent<string>) => {
const selectedAction = e.target.value;
setAction(selectedAction);
if (selected.length > 0) {
console.log("Selected Ids", selected);
console.log("Selected Action:", selectedAction);
} else {
console.warn("No rows selected for action:", selectedAction);
}
};
return (
<Box p={2}>
<Box
mb={2}
display="flex"
justifyContent="space-between"
alignItems="center"
>
<TextField
variant="outlined"
placeholder="Search..."
size="small"
value={search}
onChange={handleSearchChange}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton>
<SearchIcon />
</IconButton>
</InputAdornment>
),
}}
/>
<Box sx={{ width: 180, display: "flex", justifyContent: "center" }}>
<FormControl fullWidth>
<InputLabel>Action</InputLabel>
<Select
value={action}
label="Action"
onChange={handleActionChange}
size="small"
>
{actions.map((item) => (
<MenuItem key={item.value} value={item.value}>
{item.label ?? item.value}
</MenuItem>
))}
</Select>
</FormControl>
</Box>
</Box>
<TableContainer component={Paper}>
<Table size="small">
<TableHead>
<TableRow>
<TableCell padding="checkbox">
<Checkbox
checked={selected.length === rows.length && rows.length > 0}
indeterminate={
selected.length > 0 && selected.length < rows.length
}
onChange={(e) => handleToggleAll(e.target.checked)}
/>
</TableCell>
{columns.map((col, i) => (
<TableCell key={i}>{col.headerName}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows.map((row, idx) => (
<TableRow key={idx}>
<TableCell padding="checkbox">
<Checkbox
checked={selected.includes(row.id)}
onChange={(e) =>
handleCheckboxChange(row.id, e.target.checked)
}
/>
</TableCell>
{columns.map((col, colIdx) => (
<TableCell key={colIdx}>
{col.render
? col.render(row[col.field as keyof T], row)
: (row[col.field as keyof T] as React.ReactNode)}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Box>
);
}