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}%
+
+
+
-
+
+ {/* 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;