225 lines
6.4 KiB
TypeScript
225 lines
6.4 KiB
TypeScript
import Button from "@/components/Low/Button";
|
|
import Checkbox from "@/components/Low/Checkbox";
|
|
import useUsers from "@/hooks/useUsers";
|
|
import { Code, User } from "@/interfaces/user";
|
|
import { USER_TYPE_LABELS } from "@/resources/user";
|
|
import { createColumnHelper } from "@tanstack/react-table";
|
|
import axios from "axios";
|
|
import moment from "moment";
|
|
import { useState, useMemo } from "react";
|
|
import { BsTrash } from "react-icons/bs";
|
|
import { toast } from "react-toastify";
|
|
import { EntityWithRoles } from "@/interfaces/entity";
|
|
import { isAdmin } from "@/utils/users";
|
|
import { findBy, mapBy } from "@/utils";
|
|
import useEntitiesCodes from "@/hooks/useEntitiesCodes";
|
|
import Table from "@/components/High/Table";
|
|
|
|
type TableData = Code & { entity?: EntityWithRoles; creator?: User };
|
|
const columnHelper = createColumnHelper<TableData>();
|
|
|
|
export default function CodeList({
|
|
user,
|
|
entities,
|
|
canDeleteCodes,
|
|
}: {
|
|
user: User;
|
|
entities: EntityWithRoles[];
|
|
canDeleteCodes?: boolean;
|
|
}) {
|
|
const [selectedCodes, setSelectedCodes] = useState<string[]>([]);
|
|
|
|
const entityIDs = useMemo(() => mapBy(entities, "id"), [entities]);
|
|
|
|
const { users } = useUsers();
|
|
const { codes, reload, isLoading } = useEntitiesCodes(
|
|
isAdmin(user) ? undefined : entityIDs
|
|
);
|
|
|
|
const data: TableData[] = useMemo(
|
|
() =>
|
|
codes.map((code) => ({
|
|
...code,
|
|
entity: findBy(entities, "id", code.entity),
|
|
creator: findBy(users, "id", code.creator),
|
|
})) as TableData[],
|
|
[codes, entities, users]
|
|
);
|
|
|
|
const toggleCode = (id: string) => {
|
|
setSelectedCodes((prev) =>
|
|
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
|
|
);
|
|
};
|
|
|
|
// const toggleAllCodes = (checked: boolean) => {
|
|
// if (checked) return setSelectedCodes(visibleRows.filter((x) => !x.userId).map((x) => x.code));
|
|
|
|
// return setSelectedCodes([]);
|
|
// };
|
|
|
|
const deleteCodes = async (codes: string[]) => {
|
|
if (!canDeleteCodes) return;
|
|
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 (!canDeleteCodes) return;
|
|
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",
|
|
enableSorting: false,
|
|
header: () => "",
|
|
cell: (info) =>
|
|
!info.row.original.userId ? (
|
|
<Checkbox
|
|
isChecked={selectedCodes.includes(info.getValue())}
|
|
onChange={() => toggleCode(info.getValue())}
|
|
>
|
|
{""}
|
|
</Checkbox>
|
|
) : 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: "E-mail",
|
|
cell: (info) => info.getValue() || "N/A",
|
|
}),
|
|
columnHelper.accessor("creator", {
|
|
header: "Creator",
|
|
cell: (info) =>
|
|
info.getValue()
|
|
? `${info.getValue().name} (${
|
|
USER_TYPE_LABELS[info.getValue().type]
|
|
})`
|
|
: "N/A",
|
|
}),
|
|
columnHelper.accessor("entity", {
|
|
header: "Entity",
|
|
cell: (info) => info.getValue()?.label || "N/A",
|
|
}),
|
|
columnHelper.accessor("userId", {
|
|
header: "Availability",
|
|
cell: (info) =>
|
|
info.getValue() ? (
|
|
<span className="flex gap-1 items-center text-mti-green">
|
|
<div className="w-2 h-2 rounded-full bg-mti-green" /> In Use
|
|
</span>
|
|
) : (
|
|
<span className="flex gap-1 items-center text-mti-red">
|
|
<div className="w-2 h-2 rounded-full bg-mti-red" /> Unused
|
|
</span>
|
|
),
|
|
}),
|
|
{
|
|
header: "",
|
|
id: "actions",
|
|
cell: ({ row }: { row: { original: Code } }) => {
|
|
return (
|
|
<div className="flex gap-4">
|
|
{canDeleteCodes && !row.original.userId && (
|
|
<div
|
|
data-tip="Delete"
|
|
className="cursor-pointer tooltip"
|
|
onClick={() => deleteCode(row.original)}
|
|
>
|
|
<BsTrash className="hover:text-mti-purple-light transition ease-in-out duration-300" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
];
|
|
|
|
return (
|
|
<>
|
|
<div className="flex items-center justify-between pb-4 pt-1">
|
|
{canDeleteCodes && (
|
|
<div className="flex gap-4 items-center w-full justify-end">
|
|
<span>{selectedCodes.length} code(s) selected</span>
|
|
<Button
|
|
disabled={selectedCodes.length === 0}
|
|
variant="outline"
|
|
color="red"
|
|
className="!py-1 px-10"
|
|
onClick={() => deleteCodes(selectedCodes)}
|
|
>
|
|
Delete
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<Table<TableData>
|
|
data={data}
|
|
columns={defaultColumns}
|
|
isLoading={isLoading}
|
|
searchFields={[
|
|
["code"],
|
|
["email"],
|
|
["entity", "label"],
|
|
["creator", "name"],
|
|
["creator", "type"],
|
|
]}
|
|
/>
|
|
</>
|
|
);
|
|
}
|