diff --git a/src/app/(dashboard)/list/classes/page.tsx b/src/app/(dashboard)/list/classes/page.tsx index 2385194a..b18940b6 100644 --- a/src/app/(dashboard)/list/classes/page.tsx +++ b/src/app/(dashboard)/list/classes/page.tsx @@ -4,93 +4,178 @@ import Table from "@/components/Table"; import TableSearch from "@/components/TableSearch"; import prisma from "@/lib/prisma"; import { ITEM_PER_PAGE } from "@/lib/settings"; -import { Class, Prisma, Teacher } from "@prisma/client"; +import { CellGroup, Pastor, MembershipLevel, Prisma } from "@prisma/client"; import Image from "next/image"; import { auth } from "@clerk/nextjs/server"; -type ClassList = Class & { supervisor: Teacher }; +type CellGroupList = CellGroup & { + leader: Pastor | null; + level: MembershipLevel; + _count: { + members: number; + }; +}; -const ClassListPage = async ({ +const CellGroupListPage = async ({ searchParams, }: { searchParams: { [key: string]: string | undefined }; }) => { + const { sessionClaims } = auth(); + const role = (sessionClaims?.metadata as { role?: string })?.role; -const { sessionClaims } = auth(); -const role = (sessionClaims?.metadata as { role?: string })?.role; - - -const columns = [ - { - header: "Class Name", - accessor: "name", - }, - { - header: "Capacity", - accessor: "capacity", - className: "hidden md:table-cell", - }, - { - header: "Grade", - accessor: "grade", - className: "hidden md:table-cell", - }, - { - header: "Supervisor", - accessor: "supervisor", - className: "hidden md:table-cell", - }, - ...(role === "admin" - ? [ - { - header: "Actions", - accessor: "action", - }, - ] - : []), -]; - -const renderRow = (item: ClassList) => ( - - {item.name} - {item.capacity} - {item.name[0]} - - {item.supervisor.name + " " + item.supervisor.surname} - - -
- {role === "admin" && ( - <> - - - - )} -
- - -); + const columns = [ + { + header: "Cell Group Name", + accessor: "name", + }, + { + header: "Members", + accessor: "members", + className: "hidden md:table-cell", + }, + { + header: "Capacity", + accessor: "capacity", + className: "hidden md:table-cell", + }, + { + header: "Level", + accessor: "level", + className: "hidden lg:table-cell", + }, + { + header: "Leader", + accessor: "leader", + className: "hidden lg:table-cell", + }, + { + header: "Meeting Day", + accessor: "meetingDay", + className: "hidden xl:table-cell", + }, + ...(role === "admin" || role === "super_admin" || role === "pastor" + ? [ + { + header: "Actions", + accessor: "action", + }, + ] + : []), + ]; + + const renderRow = (item: CellGroupList) => { + // Calculate occupancy percentage + const occupancyPercentage = Math.round((item._count.members / item.capacity) * 100); + + // Determine occupancy color + const getOccupancyColor = (percentage: number) => { + if (percentage >= 90) return "text-red-600 bg-red-50"; + if (percentage >= 75) return "text-orange-600 bg-orange-50"; + if (percentage >= 50) return "text-yellow-600 bg-yellow-50"; + return "text-green-600 bg-green-50"; + }; + + // Format meeting day + const formatDay = (day: string | null) => { + if (!day) return "TBD"; + return day.charAt(0) + day.slice(1).toLowerCase(); + }; + + const occupancyColor = getOccupancyColor(occupancyPercentage); + + return ( + + +
+

{item.name}

+

+ {item._count.members}/{item.capacity} members • {formatDay(item.meetingDay)} +

+
+ + +
+ {item._count.members} + / {item.capacity} + + {occupancyPercentage}% + +
+ + {item.capacity} + + + Level {item.level.level}: {item.level.name} + + + + {item.leader ? ( +
+

{item.leader.name} {item.leader.surname}

+

Pastor

+
+ ) : ( + + )} + + +
+

{formatDay(item.meetingDay)}

+ {item.meetingTime && ( +

+ {new Date(item.meetingTime).toLocaleTimeString('en-US', { + hour: '2-digit', + minute: '2-digit', + hour12: true + })} +

+ )} +
+ + +
+ {(role === "admin" || role === "super_admin" || role === "pastor") && ( + <> + + + + )} +
+ + + ); + }; const { page, ...queryParams } = searchParams; const p = page ? parseInt(page) : 1; // URL PARAMS CONDITION - - const query: Prisma.ClassWhereInput = {}; + const query: Prisma.CellGroupWhereInput = {}; if (queryParams) { for (const [key, value] of Object.entries(queryParams)) { if (value !== undefined) { switch (key) { - case "supervisorId": - query.supervisorId = value; + case "leaderId": + query.leaderId = value; + break; + case "levelId": + query.levelId = parseInt(value); + break; + case "meetingDay": + query.meetingDay = value as any; break; case "search": - query.name = { contains: value, mode: "insensitive" }; + query.OR = [ + { name: { contains: value, mode: "insensitive" } }, + { location: { contains: value, mode: "insensitive" } }, + { leader: { name: { contains: value, mode: "insensitive" } } }, + ]; break; default: break; @@ -100,41 +185,94 @@ const renderRow = (item: ClassList) => ( } const [data, count] = await prisma.$transaction([ - prisma.class.findMany({ + prisma.cellGroup.findMany({ where: query, include: { - supervisor: true, + leader: { select: { name: true, surname: true } }, + level: { select: { level: true, name: true } }, + _count: { + select: { members: true }, + }, }, take: ITEM_PER_PAGE, skip: ITEM_PER_PAGE * (p - 1), + orderBy: { + name: 'asc', + }, }), - prisma.class.count({ where: query }), + prisma.cellGroup.count({ where: query }), ]); + // Get summary statistics + const totalMembers = data.reduce((sum, group) => sum + group._count.members, 0); + const totalCapacity = data.reduce((sum, group) => sum + group.capacity, 0); + const overallOccupancy = totalCapacity > 0 ? Math.round((totalMembers / totalCapacity) * 100) : 0; + return (
{/* TOP */}
-

All Classes

+
+

+ Cell Groups +

+
+ + Total Groups: {count} + + + Members: {totalMembers} + + + Occupancy: {overallOccupancy}% + +
+
- - - {role === "admin" && } + {(role === "admin" || role === "super_admin" || role === "pastor") && ( + + )}
+ + {/* Quick Stats Cards */} +
+
+

Active Cell Groups

+

{data.length}

+
+
+

Total Members

+

{totalMembers}

+
+
+

Average Group Size

+

+ {data.length > 0 ? Math.round(totalMembers / data.length) : 0} +

+
+
+

Available Slots

+

{totalCapacity - totalMembers}

+
+
+ {/* LIST */} + {/* PAGINATION */} ); }; -export default ClassListPage; +export default CellGroupListPage;