import * as React from "react"; import { Typography, Button, CircularProgress, Sheet, Table, Chip, Avatar, Box, IconButton, Checkbox, FormControl, FormLabel, Link, Tooltip, Select, Option, } from "@mui/joy"; import { useNavigate } from "@tanstack/react-router"; import { useTranslation } from "react-i18next"; import AddIcon from "@mui/icons-material/Add"; import DeleteIcon from "@mui/icons-material/Delete"; import FilterListIcon from "@mui/icons-material/FilterList"; import KeyboardArrowLeftIcon from "@mui/icons-material/KeyboardArrowLeft"; import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight"; import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { getProducts, deleteSelectedProducts } from "../utils/uxFncs"; import { visuallyHidden } from "@mui/utils"; import { formatDate } from "../utils/uxFncs"; import Cookies from "js-cookie"; type Order = "asc" | "desc"; type ProductRow = { id: string; uuid: string; name: string; description: string; imageUrl?: string; price: string; stock: string; stockLabel: string; stockStatus: "ok" | "low" | "missing"; location: string; locationDetail: string; expiryDate: string; refillDate: string; }; const descendingComparator = (a: T, b: T, orderBy: keyof T) => { const aValue = (a[orderBy] ?? "") as string | number; const bValue = (b[orderBy] ?? "") as string | number; if (bValue < aValue) { return -1; } if (bValue > aValue) { return 1; } return 0; }; const getComparator = (order: Order, orderBy: keyof ProductRow) => { return order === "desc" ? (a: ProductRow, b: ProductRow) => descendingComparator(a, b, orderBy) : (a: ProductRow, b: ProductRow) => -descendingComparator(a, b, orderBy); }; type EnhancedTableHeadProps = { numSelected: number; onRequestSort: ( event: React.MouseEvent, property: keyof ProductRow, ) => void; onSelectAllClick: (event: React.ChangeEvent) => void; order: Order; orderBy: string; rowCount: number; }; const EnhancedTableHead = (props: EnhancedTableHeadProps) => { const { t } = useTranslation(); const { onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort, } = props; const createSortHandler = (property: keyof ProductRow) => (event: React.MouseEvent) => { onRequestSort(event, property); }; const headCells: readonly { id: keyof ProductRow; label: string; numeric: boolean; }[] = [ { id: "name", label: t("product-name"), numeric: false }, { id: "price", label: t("price"), numeric: true }, { id: "stock", label: t("stock"), numeric: true }, { id: "location", label: t("storage-place"), numeric: false }, { id: "expiryDate", label: t("expiry-date"), numeric: false }, { id: "refillDate", label: t("bottling-date"), numeric: false }, ]; return ( 0 && numSelected < rowCount} checked={rowCount > 0 && numSelected === rowCount} onChange={onSelectAllClick} slotProps={{ input: { "aria-label": "select all products" } }} sx={{ verticalAlign: "sub" }} /> {headCells.map((headCell) => { const active = orderBy === headCell.id; return ( ) : null } endDecorator={ !headCell.numeric ? ( ) : null } sx={{ fontWeight: "lg", "& svg": { transition: "0.2s", transform: active && order === "desc" ? "rotate(0deg)" : "rotate(180deg)", }, "&:hover": { "& svg": { opacity: 1 } }, }} > {headCell.label} {active ? ( {order === "desc" ? "sorted descending" : "sorted ascending"} ) : null} ); })} {t("actions")} ); }; const EnhancedTableToolbar = ({ numSelected, selected, }: { numSelected: number; selected: readonly string[]; }) => { const { t } = useTranslation(); const queryClient = useQueryClient(); const { mutate } = useMutation({ mutationFn: (values: string[]) => deleteSelectedProducts(values), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["products"] }); }, }); return ( 0 && { bgcolor: "background.level1", }, ]} className="text-slate-700" > {numSelected > 0 ? ( {numSelected} {t("selected")} ) : ( {t("inventory")} )} {numSelected > 0 ? ( mutate([...selected])} size="sm" color="danger" variant="solid" > ) : ( )} ); }; export const InventoryPage = () => { const { t } = useTranslation(); const navigate = useNavigate(); const labelDisplayedRows = ({ from, to, count, }: { from: number; to: number; count: number; }) => `${from}-${to} ${t("of")} ${count}`; const { data: productsData, isLoading: productsIsLoading } = useQuery({ queryKey: ["products"], queryFn: getProducts, }); const rows: ProductRow[] = (productsData ?? []).map( (product: any, index: number) => ({ id: String(product?.uuid ?? index), uuid: String(product?.uuid ?? ""), name: product?.name ?? t("product-name"), description: product?.description ?? "", imageUrl: product?.picture ?? undefined, price: product?.price ?? "-", stock: `${product?.amount ?? 0} Stk.`, location: product?.storage_location_name ?? "-", locationDetail: "", expiryDate: formatDate(product?.expiry_date), refillDate: formatDate(product?.bottling_date), }), ); const [order, setOrder] = React.useState("asc"); const [orderBy, setOrderBy] = React.useState("name"); const [selected, setSelected] = React.useState([]); const [page, setPage] = React.useState(0); const [rowsPerPage, setRowsPerPage] = React.useState(5); const handleRequestSort = ( _event: React.MouseEvent, property: keyof ProductRow, ) => { const isAsc = orderBy === property && order === "asc"; setOrder(isAsc ? "desc" : "asc"); setOrderBy(property); }; const handleSelectAllClick = (event: React.ChangeEvent) => { if (event.target.checked) { const newSelected = rows.map((row) => row.id); setSelected(newSelected); return; } setSelected([]); }; const handleClick = (_event: React.MouseEvent, id: string) => { const selectedIndex = selected.indexOf(id); let newSelected: readonly string[] = []; if (selectedIndex === -1) { newSelected = newSelected.concat(selected, id); } else if (selectedIndex === 0) { newSelected = newSelected.concat(selected.slice(1)); } else if (selectedIndex === selected.length - 1) { newSelected = newSelected.concat(selected.slice(0, -1)); } else if (selectedIndex > 0) { newSelected = newSelected.concat( selected.slice(0, selectedIndex), selected.slice(selectedIndex + 1), ); } setSelected(newSelected); }; const handleChangePage = (newPage: number) => { setPage(newPage); }; const handleChangeRowsPerPage = (_event: any, newValue: number | null) => { setRowsPerPage(parseInt(newValue!.toString(), 10)); setPage(0); }; const getLabelDisplayedRowsTo = () => { if (rows.length === -1) { return (page + 1) * rowsPerPage; } return rowsPerPage === -1 ? rows.length : Math.min(rows.length, (page + 1) * rowsPerPage); }; const emptyRows = page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0; return ( <> {t("inventory")} {t("inventory-subtitle")}
{productsIsLoading && }
theme.vars.palette.success.softBg, "& thead th:nth-child(1)": { width: "40px", }, "& thead th:nth-child(2)": { width: "30%", }, "& tr > *:nth-child(n+3)": { textAlign: "left" }, }} > {[...rows] .sort(getComparator(order, orderBy)) .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) .map((row, index) => { const isItemSelected = selected.includes(row.id); const labelId = `enhanced-table-checkbox-${index}`; return ( handleClick(event, row.id)} role="checkbox" aria-checked={isItemSelected} tabIndex={-1} className="border-t border-slate-200" style={ isItemSelected ? ({ "--TableCell-dataBackground": "var(--TableCell-selectedBackground)", "--TableCell-headBackground": "var(--TableCell-selectedBackground)", } as React.CSSProperties) : {} } > ); })} {emptyRows > 0 && ( )}
{row.name} {row.description}
{row.price} {Cookies.get("currency")} {row.stock} {row.location} {row.locationDetail} {row.expiryDate} {row.refillDate}
{t("rows-per-page")} {labelDisplayedRows({ from: rows.length === 0 ? 0 : page * rowsPerPage + 1, to: getLabelDisplayedRowsTo(), count: rows.length === -1 ? -1 : rows.length, })} handleChangePage(page - 1)} sx={{ bgcolor: "background.surface" }} > = Math.ceil(rows.length / rowsPerPage) - 1 : false } onClick={() => handleChangePage(page + 1)} sx={{ bgcolor: "background.surface" }} >
); };