import Button from "@/components/Low/Button"; import Checkbox from "@/components/Low/Checkbox"; import Select from "@/components/Low/Select"; import useCodes from "@/hooks/useCodes"; import useUser from "@/hooks/useUser"; import useUsers from "@/hooks/useUsers"; import { Code, User } from "@/interfaces/user"; import { USER_TYPE_LABELS } from "@/resources/user"; import { createColumnHelper, flexRender, getCoreRowModel, useReactTable, } from "@tanstack/react-table"; import axios from "axios"; import moment from "moment"; import { useEffect, useState, useMemo } from "react"; import { BsTrash } from "react-icons/bs"; import { toast } from "react-toastify"; import ReactDatePicker from "react-datepicker"; import clsx from "clsx"; const columnHelper = createColumnHelper(); const CreatorCell = ({ id, users }: { id: string; users: User[] }) => { const [creatorUser, setCreatorUser] = useState(); useEffect(() => { setCreatorUser(users.find((x) => x.id === id)); }, [id, users]); return ( <> {(creatorUser?.type === "corporate" ? creatorUser?.corporateInformation?.companyInformation?.name : creatorUser?.name || "N/A") || "N/A"}{" "} {creatorUser && `(${USER_TYPE_LABELS[creatorUser.type]})`} ); }; export default function CodeList({ user }: { user: User }) { const [selectedCodes, setSelectedCodes] = useState([]); const [filteredCorporate, setFilteredCorporate] = useState( user?.type === "corporate" ? user : undefined ); const [filterAvailability, setFilterAvailability] = useState< "in-use" | "unused" >(); // const [filteredCodes, setFilteredCodes] = useState([]); const { users } = useUsers(); const { codes, reload } = useCodes( user?.type === "corporate" ? user?.id : undefined ); const [startDate, setStartDate] = useState(moment("01/01/2023").toDate()); const [endDate, setEndDate] = useState(moment().endOf("day").toDate()); const filteredCodes = useMemo(() => { return codes.filter((x) => { // TODO: if the expiry date is missing, it does not make sense to filter by date // so we need to find a way to handle this edge case if(startDate && endDate && x.expiryDate) { const date = moment(x.expiryDate); if(date.isBefore(startDate) || date.isAfter(endDate)) { return false; } } if (filteredCorporate && x.creator !== filteredCorporate.id) return false; if (filterAvailability) { if (filterAvailability === "in-use" && !x.userId) return false; if (filterAvailability === "unused" && x.userId) return false; } return true; }); }, [codes, startDate, endDate, filteredCorporate, filterAvailability]); const toggleCode = (id: string) => { setSelectedCodes((prev) => prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id] ); }; const toggleAllCodes = (checked: boolean) => { if (checked) return setSelectedCodes( filteredCodes.filter((x) => !x.userId).map((x) => x.code) ); return setSelectedCodes([]); }; const deleteCodes = async (codes: string[]) => { if ( !confirm(`Are you sure you want to delete these ${codes.length} code(s)?`) ) return; const params = new URLSearchParams(); codes.forEach((code) => params.append("code", code)); axios .delete(`/api/code?${params.toString()}`) .then(() => { toast.success(`Deleted the codes!`); setSelectedCodes([]); }) .catch((reason) => { if (reason.response.status === 404) { toast.error("Code not found!"); return; } if (reason.response.status === 403) { toast.error("You do not have permission to delete this code!"); return; } toast.error("Something went wrong, please try again later."); }) .finally(reload); }; const deleteCode = async (code: Code) => { if (!confirm(`Are you sure you want to delete this "${code.code}" code?`)) return; axios .delete(`/api/code/${code.code}`) .then(() => toast.success(`Deleted the "${code.code}" exam`)) .catch((reason) => { if (reason.response.status === 404) { toast.error("Code not found!"); return; } if (reason.response.status === 403) { toast.error("You do not have permission to delete this code!"); return; } toast.error("Something went wrong, please try again later."); }) .finally(reload); }; const defaultColumns = [ columnHelper.accessor("code", { id: "codeCheckbox", header: () => ( !x.userId).length === 0} isChecked={ selectedCodes.length === filteredCodes.filter((x) => !x.userId).length && filteredCodes.filter((x) => !x.userId).length > 0 } onChange={(checked) => toggleAllCodes(checked)} > {""} ), cell: (info) => !info.row.original.userId ? ( toggleCode(info.getValue())} > {""} ) : null, }), columnHelper.accessor("code", { header: "Code", cell: (info) => info.getValue(), }), columnHelper.accessor("creationDate", { header: "Creation Date", cell: (info) => info.getValue() ? moment(info.getValue()).format("DD/MM/YYYY") : "N/A", }), columnHelper.accessor("email", { header: "Invited E-mail", cell: (info) => info.getValue() || "N/A", }), columnHelper.accessor("creator", { header: "Creator", cell: (info) => , }), columnHelper.accessor("userId", { header: "Availability", cell: (info) => info.getValue() ? (
In Use ) : (
Unused ), }), { header: "", id: "actions", cell: ({ row }: { row: { original: Code } }) => { return (
{!row.original.userId && (
deleteCode(row.original)} >
)}
); }, }, ]; const table = useReactTable({ data: filteredCodes, columns: defaultColumns, getCoreRowModel: getCoreRowModel(), }); return ( <>
setFilterAvailability( value ? (value.value as typeof filterAvailability) : undefined ) } /> moment(date).isSameOrBefore(moment(new Date())) } onChange={([initialDate, finalDate]: [Date, Date]) => { setStartDate(initialDate ?? moment("01/01/2023").toDate()); if (finalDate) { // basicly selecting a final day works as if I'm selecting the first // minute of that day. this way it covers the whole day setEndDate(moment(finalDate).endOf("day").toDate()); return; } setEndDate(null); }} />
{selectedCodes.length} code(s) selected
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( ))} ))} {table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( ))} ))}
{header.isPlaceholder ? null : flexRender( header.column.columnDef.header, header.getContext() )}
{flexRender(cell.column.columnDef.cell, cell.getContext())}
); }