Added a list for codes, for users to delete unused ones
This commit is contained in:
106
src/pages/(admin)/Lists/CodeList.tsx
Normal file
106
src/pages/(admin)/Lists/CodeList.tsx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import useCodes from "@/hooks/useCodes";
|
||||||
|
import {Code, User} from "@/interfaces/user";
|
||||||
|
import {createColumnHelper, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table";
|
||||||
|
import axios from "axios";
|
||||||
|
import {BsTrash} from "react-icons/bs";
|
||||||
|
import {toast} from "react-toastify";
|
||||||
|
|
||||||
|
const columnHelper = createColumnHelper<Code>();
|
||||||
|
|
||||||
|
export default function CodeList({user}: {user: User}) {
|
||||||
|
const {codes, reload} = useCodes(user?.type === "corporate" ? user?.id : undefined);
|
||||||
|
|
||||||
|
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", {
|
||||||
|
header: "Code",
|
||||||
|
cell: (info) => info.getValue(),
|
||||||
|
}),
|
||||||
|
columnHelper.accessor("email", {
|
||||||
|
header: "Invited E-mail",
|
||||||
|
cell: (info) => info.getValue() || "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">
|
||||||
|
{!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>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
data: codes,
|
||||||
|
columns: defaultColumns,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<table className="rounded-xl bg-mti-purple-ultralight/40 w-full">
|
||||||
|
<thead>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<tr key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<th className="p-4 text-left" key={header.id}>
|
||||||
|
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody className="px-2">
|
||||||
|
{table.getRowModel().rows.map((row) => (
|
||||||
|
<tr className="odd:bg-white even:bg-mti-purple-ultralight/40 rounded-lg py-2" key={row.id}>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<td className="px-4 py-2" key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import {User} from "@/interfaces/user";
|
import {User} from "@/interfaces/user";
|
||||||
import {Tab} from "@headlessui/react";
|
import {Tab} from "@headlessui/react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
import CodeList from "./CodeList";
|
||||||
import ExamList from "./ExamList";
|
import ExamList from "./ExamList";
|
||||||
import GroupList from "./GroupList";
|
import GroupList from "./GroupList";
|
||||||
import PackageList from "./PackageList";
|
import PackageList from "./PackageList";
|
||||||
@@ -45,6 +46,19 @@ export default function Lists({user}: {user: User}) {
|
|||||||
}>
|
}>
|
||||||
Group List
|
Group List
|
||||||
</Tab>
|
</Tab>
|
||||||
|
{user && ["developer", "admin", "corporate"].includes(user.type) && (
|
||||||
|
<Tab
|
||||||
|
className={({selected}) =>
|
||||||
|
clsx(
|
||||||
|
"w-full rounded-lg py-2.5 text-sm font-medium leading-5 text-mti-purple-light",
|
||||||
|
"ring-white ring-opacity-60 ring-offset-2 ring-offset-mti-purple-light focus:outline-none focus:ring-2",
|
||||||
|
"transition duration-300 ease-in-out",
|
||||||
|
selected ? "bg-white shadow" : "text-blue-100 hover:bg-white/[0.12] hover:text-mti-purple-dark",
|
||||||
|
)
|
||||||
|
}>
|
||||||
|
Code List
|
||||||
|
</Tab>
|
||||||
|
)}
|
||||||
{user && ["developer", "admin"].includes(user.type) && (
|
{user && ["developer", "admin"].includes(user.type) && (
|
||||||
<Tab
|
<Tab
|
||||||
className={({selected}) =>
|
className={({selected}) =>
|
||||||
@@ -71,6 +85,11 @@ export default function Lists({user}: {user: User}) {
|
|||||||
<Tab.Panel className="overflow-y-scroll max-h-[600px] rounded-xl scrollbar-hide">
|
<Tab.Panel className="overflow-y-scroll max-h-[600px] rounded-xl scrollbar-hide">
|
||||||
<GroupList user={user} />
|
<GroupList user={user} />
|
||||||
</Tab.Panel>
|
</Tab.Panel>
|
||||||
|
{user && ["developer", "admin", "corporate"].includes(user.type) && (
|
||||||
|
<Tab.Panel className="overflow-y-scroll max-h-[600px] rounded-xl scrollbar-hide">
|
||||||
|
<CodeList user={user} />
|
||||||
|
</Tab.Panel>
|
||||||
|
)}
|
||||||
{user && ["developer", "admin"].includes(user.type) && (
|
{user && ["developer", "admin"].includes(user.type) && (
|
||||||
<Tab.Panel className="overflow-y-scroll max-h-[600px] rounded-xl scrollbar-hide">
|
<Tab.Panel className="overflow-y-scroll max-h-[600px] rounded-xl scrollbar-hide">
|
||||||
<PackageList user={user} />
|
<PackageList user={user} />
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const db = getFirestore(app);
|
|||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
if (req.method === "GET") return GET(req, res);
|
if (req.method === "GET") return GET(req, res);
|
||||||
|
if (req.method === "DELETE") return DELETE(req, res);
|
||||||
|
|
||||||
res.status(404).json({ok: false});
|
res.status(404).json({ok: false});
|
||||||
}
|
}
|
||||||
@@ -21,3 +22,13 @@ async function GET(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
|
|
||||||
res.status(200).json({...snapshot.data(), id: snapshot.id});
|
res.status(200).json({...snapshot.data(), id: snapshot.id});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function DELETE(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
const {id} = req.query;
|
||||||
|
|
||||||
|
const snapshot = await getDoc(doc(db, "codes", id as string));
|
||||||
|
if (!snapshot.exists()) return res.status(404).json;
|
||||||
|
|
||||||
|
await deleteDoc(snapshot.ref);
|
||||||
|
res.status(200).json({...snapshot.data(), id: snapshot.id});
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ async function get(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {creator} = req.query as {creator?: string};
|
const {creator} = req.query as {creator?: string};
|
||||||
const q = query(collection(db, "codes"), where("creator", "==", creator));
|
const q = query(collection(db, "codes"), where("creator", "==", creator || ""));
|
||||||
const snapshot = await getDocs(creator ? q : collection(db, "codes"));
|
const snapshot = await getDocs(creator ? q : collection(db, "codes"));
|
||||||
|
|
||||||
res.status(200).json(snapshot.docs.map((doc) => doc.data()));
|
res.status(200).json(snapshot.docs.map((doc) => doc.data()));
|
||||||
|
|||||||
Reference in New Issue
Block a user