- ENCOA-3: Added the ability to delete multiple codes at once;
- ENCOA-5 Added a column for the Creator on the code list;
This commit is contained in:
@@ -62,8 +62,8 @@ export default function Button({
|
||||
onClick={onClick}
|
||||
className={clsx(
|
||||
"py-4 px-6 rounded-full transition ease-in-out duration-300 disabled:cursor-not-allowed cursor-pointer",
|
||||
className,
|
||||
colorClassNames[color][variant],
|
||||
className,
|
||||
)}
|
||||
disabled={disabled || isLoading}>
|
||||
{!isLoading && children}
|
||||
|
||||
@@ -1,15 +1,69 @@
|
||||
import Button from "@/components/Low/Button";
|
||||
import Checkbox from "@/components/Low/Checkbox";
|
||||
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 {useEffect, useState} from "react";
|
||||
import {BsTrash} from "react-icons/bs";
|
||||
import {toast} from "react-toastify";
|
||||
|
||||
const columnHelper = createColumnHelper<Code>();
|
||||
|
||||
const CreatorCell = ({id, users}: {id: string; users: User[]}) => {
|
||||
const [creatorUser, setCreatorUser] = useState<User>();
|
||||
|
||||
useEffect(() => {
|
||||
console.log(id);
|
||||
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<string[]>([]);
|
||||
|
||||
const {users} = useUsers();
|
||||
const {codes, reload} = useCodes(user?.type === "corporate" ? user?.id : undefined);
|
||||
|
||||
const toggleCode = (id: string) => {
|
||||
setSelectedCodes((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]));
|
||||
};
|
||||
|
||||
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!`))
|
||||
.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;
|
||||
|
||||
@@ -33,6 +87,14 @@ export default function CodeList({user}: {user: User}) {
|
||||
};
|
||||
|
||||
const defaultColumns = [
|
||||
columnHelper.accessor("code", {
|
||||
header: "",
|
||||
cell: (info) => (
|
||||
<Checkbox isChecked={selectedCodes.includes(info.getValue())} onChange={() => toggleCode(info.getValue())}>
|
||||
{""}
|
||||
</Checkbox>
|
||||
),
|
||||
}),
|
||||
columnHelper.accessor("code", {
|
||||
header: "Code",
|
||||
cell: (info) => info.getValue(),
|
||||
@@ -41,6 +103,10 @@ export default function CodeList({user}: {user: User}) {
|
||||
header: "Invited E-mail",
|
||||
cell: (info) => info.getValue() || "N/A",
|
||||
}),
|
||||
columnHelper.accessor("creator", {
|
||||
header: "Creator",
|
||||
cell: (info) => <CreatorCell id={info.getValue()} users={users} />,
|
||||
}),
|
||||
columnHelper.accessor("userId", {
|
||||
header: "Availability",
|
||||
cell: (info) =>
|
||||
@@ -78,6 +144,18 @@ export default function CodeList({user}: {user: User}) {
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between pb-4 pt-1 px-4">
|
||||
<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>
|
||||
<table className="rounded-xl bg-mti-purple-ultralight/40 w-full">
|
||||
<thead>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
@@ -102,5 +180,6 @@ export default function CodeList({user}: {user: User}) {
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type {NextApiRequest, NextApiResponse} from "next";
|
||||
import {app} from "@/firebase";
|
||||
import {getFirestore, setDoc, doc, query, collection, where, getDocs} from "firebase/firestore";
|
||||
import {getFirestore, setDoc, doc, query, collection, where, getDocs, getDoc, deleteDoc} from "firebase/firestore";
|
||||
import {withIronSessionApiRoute} from "iron-session/next";
|
||||
import {sessionOptions} from "@/lib/session";
|
||||
import {Type} from "@/interfaces/user";
|
||||
@@ -16,6 +16,7 @@ export default withIronSessionApiRoute(handler, sessionOptions);
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method === "GET") return get(req, res);
|
||||
if (req.method === "POST") return post(req, res);
|
||||
if (req.method === "DELETE") return del(req, res);
|
||||
|
||||
return res.status(404).json({ok: false});
|
||||
}
|
||||
@@ -121,3 +122,21 @@ async function post(req: NextApiRequest, res: NextApiResponse) {
|
||||
res.status(200).json({ok: true, valid: results.filter((x) => x).length});
|
||||
});
|
||||
}
|
||||
|
||||
async function del(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!req.session.user) {
|
||||
res.status(401).json({ok: false, reason: "You must be logged in to generate a code!"});
|
||||
return;
|
||||
}
|
||||
|
||||
const codes = req.query.code as string[];
|
||||
|
||||
for (const code of codes) {
|
||||
const snapshot = await getDoc(doc(db, "codes", code as string));
|
||||
if (!snapshot.exists()) continue;
|
||||
|
||||
await deleteDoc(snapshot.ref);
|
||||
}
|
||||
|
||||
res.status(200).json({codes});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user