From 0d95796a44fb01f0f59d7933715499f48c90d77c Mon Sep 17 00:00:00 2001 From: amjed-ali-k Date: Sat, 28 Oct 2023 22:55:18 +0530 Subject: [PATCH] feat: aded update option to batch --- src/app/api/secure/exam-seating/route.ts | 17 + .../exam-seating/student-batches/route.ts | 15 + .../exam/_components/NewExamForm.tsx | 2 +- .../exam-seating/exam/_components/PDFgen.tsx | 2 +- .../hall/[hallId]/_components/newClass.tsx | 721 ++++++++++++++++++ .../tools/exam-seating/hall/[hallId]/page.tsx | 19 + .../_components/classList.tsx | 0 .../new}/_components/newClass.tsx | 2 +- .../{new-class => hall/new}/page.tsx | 0 .../{class-list => hall}/page.tsx | 2 +- 10 files changed, 776 insertions(+), 4 deletions(-) create mode 100644 src/app/dashboard/tools/exam-seating/hall/[hallId]/_components/newClass.tsx create mode 100644 src/app/dashboard/tools/exam-seating/hall/[hallId]/page.tsx rename src/app/dashboard/tools/exam-seating/{class-list => hall}/_components/classList.tsx (100%) rename src/app/dashboard/tools/exam-seating/{new-class => hall/new}/_components/newClass.tsx (99%) rename src/app/dashboard/tools/exam-seating/{new-class => hall/new}/page.tsx (100%) rename src/app/dashboard/tools/exam-seating/{class-list => hall}/page.tsx (94%) diff --git a/src/app/api/secure/exam-seating/route.ts b/src/app/api/secure/exam-seating/route.ts index b22b426..4ef41b7 100644 --- a/src/app/api/secure/exam-seating/route.ts +++ b/src/app/api/secure/exam-seating/route.ts @@ -86,6 +86,23 @@ export async function PUT(request: NextRequest) { ); } + const hall = await prisma.examHall.findUnique({ + where: { + id: body.data.id, + }, + }); + + if (!hall) { + return NextResponse.json({ message: "Hall not found" }, { status: 404 }); + } + + if (hall.createdById !== userId) { + return NextResponse.json( + { message: "You are not authorized to update this hall" }, + { status: 401 } + ); + } + const results = await prisma.examHall.update({ where: { id: body.data.id, diff --git a/src/app/api/secure/exam-seating/student-batches/route.ts b/src/app/api/secure/exam-seating/student-batches/route.ts index a0b4429..a525682 100644 --- a/src/app/api/secure/exam-seating/student-batches/route.ts +++ b/src/app/api/secure/exam-seating/student-batches/route.ts @@ -74,6 +74,21 @@ export async function PUT(request: NextRequest) { ); } + const batch = await prisma.studentBatchForExam.findUnique({ + where: { + id: body.data.id, + }, + }); + + if (!batch) + return NextResponse.json({ message: "Invalid request" }, { status: 400 }); + + if (batch.createdById !== userId) + return NextResponse.json( + { message: "You are not authorized to update this batch" }, + { status: 401 } + ); + const results = await prisma.studentBatchForExam.update({ where: { id: body.data.id, diff --git a/src/app/dashboard/tools/exam-seating/exam/_components/NewExamForm.tsx b/src/app/dashboard/tools/exam-seating/exam/_components/NewExamForm.tsx index 0c1e52c..6c27e20 100644 --- a/src/app/dashboard/tools/exam-seating/exam/_components/NewExamForm.tsx +++ b/src/app/dashboard/tools/exam-seating/exam/_components/NewExamForm.tsx @@ -67,7 +67,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { sum, flat, group, sift } from "radash"; import { assignHallsCustom } from "@/lib/examTools/customHallAsiign"; import { AllocatedSeat, allocateSeats } from "@/lib/examTools/hallSort"; -import { SeatObjectType } from "../../new-class/_components/newClass"; +import { SeatObjectType } from "../../hall/new/_components/newClass"; import { GenerateAttendanceSheet, GenerateHallsAssignment, diff --git a/src/app/dashboard/tools/exam-seating/exam/_components/PDFgen.tsx b/src/app/dashboard/tools/exam-seating/exam/_components/PDFgen.tsx index d8785b2..1067ceb 100644 --- a/src/app/dashboard/tools/exam-seating/exam/_components/PDFgen.tsx +++ b/src/app/dashboard/tools/exam-seating/exam/_components/PDFgen.tsx @@ -10,7 +10,7 @@ import { import { ArrangedResult } from "./NewExamForm"; import React, { useMemo } from "react"; import { group, max } from "radash"; -import { SeatType } from "../../new-class/_components/newClass"; +import { SeatType } from "../../hall/new/_components/newClass"; import { AllocatedSeat } from "@/lib/examTools/hallSort"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; diff --git a/src/app/dashboard/tools/exam-seating/hall/[hallId]/_components/newClass.tsx b/src/app/dashboard/tools/exam-seating/hall/[hallId]/_components/newClass.tsx new file mode 100644 index 0000000..622ed0f --- /dev/null +++ b/src/app/dashboard/tools/exam-seating/hall/[hallId]/_components/newClass.tsx @@ -0,0 +1,721 @@ +"use client"; +import React, { useEffect, useRef, useState } from "react"; +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuShortcut, + ContextMenuSeparator, + ContextMenuSub, + ContextMenuSubContent, + ContextMenuSubTrigger, + ContextMenuTrigger, +} from "@/components/ui/context-menu"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuPortal, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; + +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; + +import { + ArrowUp, + BoxSelect, + Copy, + Dice1, + RectangleHorizontal, + RockingChair, + Save, + Trash, +} from "lucide-react"; +import { CalendarIcon, PlusCircledIcon, PlusIcon } from "@radix-ui/react-icons"; +import { Label } from "@/components/ui/label"; +import { Input } from "@/components/ui/input"; +import cn from "classnames"; +import axios from "axios"; +import { useToast } from "@/components/ui/use-toast"; +import { ExamHall } from "@prisma/client"; +import { useRouter } from "next/navigation"; +import { usePermenantGet } from "@/lib/swr"; + +function EditHallForm({ id }: { id?: string }) { + const [hallName, setHallName] = useState(""); + const [structure, setStructure] = useState([[]] as SeatObjectType[][]); + + const { toast } = useToast(); + const router = useRouter(); + const { data: existingData } = usePermenantGet( + `/api/secure/exam-seating?id=${id}` + ); + + const onSubmit: React.MouseEventHandler = (e) => { + e.preventDefault(); + if (hallName === "") + return toast({ + title: "Hall name is required", + description: "Please enter a name for the hall", + variant: "destructive", + }); + axios + .post("/api/secure/exam-seating", { name: hallName, structure }) + .then((res) => { + toast({ + title: "Well done!", + description: `Class ${res.data.name} added successfully`, + }); + router.push("/dashboard/tools/exam-seating/"); + }); + }; + return ( +
+
+
+ + setHallName(e.target.value)} + type="text" + id="name" + placeholder="Name" + /> +
+
+
+

Class structure

+ +
+
+ +
+
+ ); +} + +export default EditHallForm; + +function ClassLayout({ + structure, + setStructure, +}: { + structure: SeatObjectType[][]; + setStructure: (structure: SeatObjectType[][]) => void; +}) { + const [savedDesks, setsavedDesks] = useState([] as SeatObjectType[]); + + const onAddNew = (seatType: SeatObjectType, row: number, col: number) => { + const _structure = [...structure]; + const _row = _structure[row] ?? []; + _row[col] = seatType; + _structure[row] = _row; + setStructure(_structure); + + if (preDefinedSeats[seatType.name]) return; + setsavedDesks([...savedDesks, seatType]); + }; + + const onRemove = (row: number, col: number) => { + const _structure = [...structure]; + const _row = _structure[row] ?? []; + _row.splice(col, 1); + _structure[row] = _row; + setStructure(_structure); + }; + return ( +
+
+

+ + Front side + +

+
+
+ {structure.map((e, i) => ( + onAddNew(k, i, e.length)} + > +
+ {e.map((k, j) => ( + onAddNew(k, i, e.length)} + key={j} + seat={k} + onRemove={() => onRemove(i, j)} + /> + ))} + +
0, + } + )} + > + { + onAddNew(st, i, e.length); + }} + /> +
+
+
+ ))} +
+
+
+
+ onAddNew(st, structure.length, 0)} + /> +
+
Add seat in new row
+
+
+ ); +} + +function MyContextMenu({ + children, + savedTypes, + onAddNew, +}: { + savedTypes?: SeatObjectType[]; + onAddNew?: (seatType: SeatObjectType) => void; + children: React.ReactNode; +}) { + const [isOpen, setIsOpen] = useState(false); + const closeModal = () => setIsOpen(false); + return ( + + + {children} + + + + + New Desk + + + { + onAddNew && onAddNew(preDefinedSeats.twoSeatDesk); + }} + > + 2 Seats + + { + onAddNew && onAddNew(preDefinedSeats.threeSeatDesk); + }} + > + 3 Seats + + { + onAddNew && onAddNew(preDefinedSeats.fourSeatDesk); + }} + > + 4 Seats + + + + Custom + + + + New Drawing Table + + + + New Free Space + + + { + onAddNew && onAddNew(preDefinedSeats.oneBlackSpace); + }} + > + 1 Seats + + { + onAddNew && onAddNew(preDefinedSeats.twoBlankSpace); + }} + > + 2 Seats + + { + onAddNew && onAddNew(preDefinedSeats.threeBlankSpace); + }} + > + 3 Seats + + { + onAddNew && onAddNew(preDefinedSeats.fourBlankSpace); + }} + > + 4 Seats + + + + Custom + + + + + Custom + + {savedTypes && savedTypes.length > 1 && ( + <> + + + + + + Saved seats + + + {savedTypes.map((e) => ( + + { + onAddNew && onAddNew(e); + }} + > + + {e.name} + + + ))} + + + + )} + + + + + ); +} + +function SeatComponent({ + seat, + onRemove, + onAddNew, +}: { + seat: SeatObjectType; + onAddNew?: () => void; + onRemove: () => void; +}) { + return ( + + +
b !== SeatType.BLANK, false) === + false, + } + )} + > +
+ +
+ {seat.structure.map((e, i) => { + return ( + + + + {SeatType.BLANK !== e && seatTypeMin[e]} + + +

{seatTypeDescription[e]}

+
+
+
+ ); + })} +
+
+ + + + Duplicate + + + + Delete + + +
+ ); +} + +export enum SeatType { + THEORY, + DRAWING, + COMMON, + BLANK, +} + +export type SeatObjectType = { + name: string; + seatCount: number; + structure: SeatType[]; +}; + +const preDefinedSeats: { [key: string]: SeatObjectType } = { + threeSeatDesk: { + name: "3 Seat Desk", + seatCount: 3, + structure: [SeatType.THEORY, SeatType.THEORY, SeatType.THEORY], + }, + twoSeatDesk: { + name: "2 Seat Desk", + seatCount: 2, + structure: [SeatType.THEORY, SeatType.THEORY], + }, + fourSeatDesk: { + name: "4 Seat Desk", + seatCount: 4, + structure: [ + SeatType.THEORY, + SeatType.THEORY, + SeatType.THEORY, + SeatType.THEORY, + ], + }, + drawingTable: { + name: "drawingTable", + seatCount: 1, + structure: [SeatType.DRAWING], + }, + oneBlackSpace: { + name: "1 Blank Space", + seatCount: 1, + structure: [SeatType.BLANK], + }, + twoBlankSpace: { + name: "2 Blank Space", + seatCount: 2, + structure: [SeatType.BLANK, SeatType.BLANK], + }, + threeBlankSpace: { + name: "3 Blank Space", + seatCount: 3, + structure: [SeatType.BLANK, SeatType.BLANK, SeatType.BLANK], + }, + fourBlankSpace: { + name: "4 Blank Space", + seatCount: 4, + structure: [SeatType.BLANK, SeatType.BLANK, SeatType.BLANK, SeatType.BLANK], + }, +}; + +function AddNewButton({ + savedTypes, + onAddNew, +}: { + savedTypes?: SeatObjectType[]; + onAddNew?: (seatType: SeatObjectType) => void; +}) { + const [isOpen, setIsOpen] = useState(false); + const closeModal = () => setIsOpen(false); + return ( + + + + + + + + + + + New desk + + + + { + onAddNew && onAddNew(preDefinedSeats.twoSeatDesk); + }} + > + 2 Seats + + { + onAddNew && onAddNew(preDefinedSeats.threeSeatDesk); + }} + > + 3 Seats + + { + onAddNew && onAddNew(preDefinedSeats.fourSeatDesk); + }} + > + 4 Seats + + + + More... + + + + + + + + New blank space + + + + { + onAddNew && onAddNew(preDefinedSeats.oneBlackSpace); + }} + > + 1 Seat + + { + onAddNew && onAddNew(preDefinedSeats.twoBlankSpace); + }} + > + 2 Seats + + { + onAddNew && onAddNew(preDefinedSeats.threeBlankSpace); + }} + > + 3 Seats + + { + onAddNew && onAddNew(preDefinedSeats.fourBlankSpace); + }} + > + 4 Seats + + + + More... + + + + + { + onAddNew && onAddNew(preDefinedSeats.drawingTable); + }} + > + + New Drawing Table + + + + + Custom seat + + + + {/* {savedTypes && savedTypes.length > 1 && ( + <> + + + + + + Saved seats + + + {savedTypes.map((e) => ( + { + onAddNew && onAddNew(e); + }} + > + + {e.name} + + ))} + + + + + )} */} + + + + + ); +} + +function AddNewSeatDialog({ + onAddNew, + closeModal, +}: { + onAddNew?: (seatType: SeatObjectType) => void; + closeModal: () => void; +}) { + const [seats, setseats] = useState([] as SeatType[]); + const [name, setName] = useState(""); + return ( + + + Add new desk/table + + Add seats and seat types here. You can save them for later use. + + +
+
+ + setName(e.target.value)} + type="name" + id="name" + placeholder="Seat Name" + /> +
+ +

+ Click on each seat to change seat type. T means + Theory only, D means Drawing only, C{" "} + means Common seat and B means Blank space. +

+
1 ? "justify-between" : "justify-start" + } border p-1`} + > + {seats.map((e, i) => { + const _seats = [...seats]; + return ( + + + { + _seats[i] = (_seats[i] + 1) % 4; + setseats(_seats); + }} + className="group hover:bg-zinc-800 flex w-11 pl-4 hover:pl-3 hover:w-auto text-sm duration-300 items-center pr-3 border rounded-md" + > +
+ {seatTypeDescription[e].charAt(0).toUpperCase()} +
+
+ {seatTypeDescription[e].slice(1)} +
+
+ +

{seatTypeDescription[e]}

+
+
+
+ ); + })} + +
+
+ + + + + +
+ ); +} + +const seatTypeDescription = { + [SeatType.THEORY]: "Theory only", + [SeatType.DRAWING]: "Drawing only", + [SeatType.COMMON]: "Common seat", + [SeatType.BLANK]: "Blank space", +}; + +const seatTypeMin = { + [SeatType.THEORY]: "T", + [SeatType.DRAWING]: "D", + [SeatType.COMMON]: "C", + [SeatType.BLANK]: "B", +}; diff --git a/src/app/dashboard/tools/exam-seating/hall/[hallId]/page.tsx b/src/app/dashboard/tools/exam-seating/hall/[hallId]/page.tsx new file mode 100644 index 0000000..bd985f7 --- /dev/null +++ b/src/app/dashboard/tools/exam-seating/hall/[hallId]/page.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import EditHallForm from "./_components/newClass"; + +async function page({ params }: { params: { hallId: string } }) { + return ( +
+
+

+ Add new class +

+
+
+ +
+
+ ); +} + +export default page; diff --git a/src/app/dashboard/tools/exam-seating/class-list/_components/classList.tsx b/src/app/dashboard/tools/exam-seating/hall/_components/classList.tsx similarity index 100% rename from src/app/dashboard/tools/exam-seating/class-list/_components/classList.tsx rename to src/app/dashboard/tools/exam-seating/hall/_components/classList.tsx diff --git a/src/app/dashboard/tools/exam-seating/new-class/_components/newClass.tsx b/src/app/dashboard/tools/exam-seating/hall/new/_components/newClass.tsx similarity index 99% rename from src/app/dashboard/tools/exam-seating/new-class/_components/newClass.tsx rename to src/app/dashboard/tools/exam-seating/hall/new/_components/newClass.tsx index ef7594d..d246268 100644 --- a/src/app/dashboard/tools/exam-seating/new-class/_components/newClass.tsx +++ b/src/app/dashboard/tools/exam-seating/hall/new/_components/newClass.tsx @@ -85,7 +85,7 @@ function NewClassComponent() { title: "Well done!", description: `Class ${res.data.name} added successfully`, }); - router.push("/dashboard/tools/exam-seating/class-list"); + router.push("/dashboard/tools/exam-seating/"); }); }; return ( diff --git a/src/app/dashboard/tools/exam-seating/new-class/page.tsx b/src/app/dashboard/tools/exam-seating/hall/new/page.tsx similarity index 100% rename from src/app/dashboard/tools/exam-seating/new-class/page.tsx rename to src/app/dashboard/tools/exam-seating/hall/new/page.tsx diff --git a/src/app/dashboard/tools/exam-seating/class-list/page.tsx b/src/app/dashboard/tools/exam-seating/hall/page.tsx similarity index 94% rename from src/app/dashboard/tools/exam-seating/class-list/page.tsx rename to src/app/dashboard/tools/exam-seating/hall/page.tsx index d06bd64..39d5e78 100644 --- a/src/app/dashboard/tools/exam-seating/class-list/page.tsx +++ b/src/app/dashboard/tools/exam-seating/hall/page.tsx @@ -22,7 +22,7 @@ async function page() {
- +