From 1fb7343aa73132e6d9cd216f89a2ead5e9541904 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Wed, 16 Oct 2024 10:23:59 +0100 Subject: [PATCH] Solved some issues with the redirect as well as adding a way to create entities --- src/pages/api/entities/[id]/index.ts | 8 +- src/pages/api/entities/index.ts | 12 +- src/pages/dashboard/admin.tsx | 15 +- src/pages/dashboard/corporate.tsx | 7 +- src/pages/dashboard/developer.tsx | 9 +- src/pages/dashboard/index.tsx | 2 +- src/pages/dashboard/mastercorporate.tsx | 15 +- src/pages/entities/create.tsx | 176 ++++++++++++++++++++++++ src/resources/entityPermissions.ts | 56 ++++++++ src/utils/entities.be.ts | 49 +++++-- 10 files changed, 326 insertions(+), 23 deletions(-) create mode 100644 src/pages/entities/create.tsx diff --git a/src/pages/api/entities/[id]/index.ts b/src/pages/api/entities/[id]/index.ts index b2e7179b..efe76896 100644 --- a/src/pages/api/entities/[id]/index.ts +++ b/src/pages/api/entities/[id]/index.ts @@ -14,8 +14,9 @@ const db = client.db(process.env.MONGODB_DB); export default withIronSessionApiRoute(handler, sessionOptions); async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.method === "get") return await get(req, res); + if (req.method === "GET") return await get(req, res); if (req.method === "PATCH") return await patch(req, res); + if (req.method === "DELETE") return await del(req, res); } async function get(req: NextApiRequest, res: NextApiResponse) { @@ -35,9 +36,10 @@ async function del(req: NextApiRequest, res: NextApiResponse) { const { id } = req.query as { id: string }; const entity = await getEntityWithRoles(id) - if (!entity) return res.status(404).json({ok: false}) + if (!entity) return res.status(404).json({ ok: false }) - if (!doesEntityAllow(user, entity, "delete_entity_role")) return res.status(403).json({ok: false}) + if (!doesEntityAllow(user, entity, "delete_entity") && !["admin", "developer"].includes(user.type)) + return res.status(403).json({ok: false}) await deleteEntity(entity) return res.status(200).json({ok: true}); diff --git a/src/pages/api/entities/index.ts b/src/pages/api/entities/index.ts index 11b5bd64..7c8b3896 100644 --- a/src/pages/api/entities/index.ts +++ b/src/pages/api/entities/index.ts @@ -2,7 +2,7 @@ import type {NextApiRequest, NextApiResponse} from "next"; import {withIronSessionApiRoute} from "iron-session/next"; import {sessionOptions} from "@/lib/session"; -import {createEntity, getEntities, getEntitiesWithRoles} from "@/utils/entities.be"; +import {addUsersToEntity, addUserToEntity, createEntity, getEntities, getEntitiesWithRoles} from "@/utils/entities.be"; import {Entity} from "@/interfaces/entity"; import {v4} from "uuid"; import { requestUser } from "@/utils/api"; @@ -39,6 +39,14 @@ async function post(req: NextApiRequest, res: NextApiResponse) { label: req.body.label, }; - await createEntity(entity) + const members = req.body.members as string[] | undefined || [] + console.log(members) + + const roles = await createEntity(entity) + console.log(roles) + + await addUserToEntity(user.id, entity.id, roles.admin.id) + if (members.length > 0) await addUsersToEntity(members, entity.id, roles.default.id) + return res.status(200).json(entity); } diff --git a/src/pages/dashboard/admin.tsx b/src/pages/dashboard/admin.tsx index 51ce62d8..3d2d1722 100644 --- a/src/pages/dashboard/admin.tsx +++ b/src/pages/dashboard/admin.tsx @@ -137,8 +137,19 @@ export default function Dashboard({ user, users, entities, assignments, stats, g value={masterCorporates.length} color="purple" /> - - + router.push("/classrooms")} + label="Classrooms" + value={groups.length} + color="purple" + /> + router.push("/entities")} + label="Entities" + value={entities.length} + color="purple" + /> - + router.push("/entities")} + label="Entities" + value={entities.length} + color="purple" + /> router.push("/entities")} + onClick={() => router.push("/classrooms")} label="Classrooms" value={groups.length} color="purple" /> - + router.push("/entities")} + label="Entities" + value={entities.length} + color="purple" + /> { const user = await requestUser(req, res) diff --git a/src/pages/dashboard/mastercorporate.tsx b/src/pages/dashboard/mastercorporate.tsx index 8d1dfc16..5d9000c8 100644 --- a/src/pages/dashboard/mastercorporate.tsx +++ b/src/pages/dashboard/mastercorporate.tsx @@ -137,8 +137,19 @@ export default function Dashboard({ user, users, entities, assignments, stats, g /> router.push("/users?type=corporate")} Icon={BsBank} label="Corporate Accounts" value={corporates.length} color="purple" /> - router.push("/classrooms")} Icon={BsPeople} label="Classrooms" value={groups.length} color="purple" /> - + router.push("/classrooms")} + label="Classrooms" + value={groups.length} + color="purple" + /> + router.push("/entities")} + label="Entities" + value={entities.length} + color="purple" + /> { + const user = await requestUser(req, res) + if (!user) return redirect("/login") + + if (shouldRedirectHome(user)) return redirect("/") + if (!["admin", "developer"].includes(user.type)) return redirect("/entities") + + const users = await getUsers() + + return { + props: serialize({user, users: users.filter((x) => x.id !== user.id)}), + }; +}, sessionOptions); + +interface Props { + user: User; + users: User[]; +} + +export default function Home({user, users}: Props) { + const [isLoading, setIsLoading] = useState(false); + const [selectedUsers, setSelectedUsers] = useState([]); + const [label, setLabel] = useState(""); + + const {rows, renderSearch} = useListSearch([["name"], ["corporateInformation", "companyInformation", "name"]], users); + const {items, renderMinimal} = usePagination(rows, 16); + + const router = useRouter(); + + const createGroup = () => { + if (!label.trim()) return; + if (!confirm(`Are you sure you want to create this entity with ${selectedUsers.length} members?`)) return; + + setIsLoading(true); + + axios + .post(`/api/entities`, {label, members: selectedUsers}) + .then((result) => { + toast.success("Your entity has been created successfully!"); + router.replace(`/entities/${result.data.id}`); + }) + .catch((e) => { + console.error(e); + toast.error("Something went wrong!"); + }) + .finally(() => setIsLoading(false)); + }; + + const toggleUser = (u: User) => setSelectedUsers((prev) => (prev.includes(u.id) ? prev.filter((p) => p !== u.id) : [...prev, u.id])); + + return ( + <> + + Create Entity | EnCoach + + + + + + +
+
+
+ + + +

Create Entity

+
+
+ +
+
+ +
+ Entity Label: + +
+ +
+ Members ({selectedUsers.length} selected): +
+
+ {renderSearch()} + {renderMinimal()} +
+
+ +
+ {items.map((u) => ( + + ))} +
+
+ + ); +} diff --git a/src/resources/entityPermissions.ts b/src/resources/entityPermissions.ts index d785aacb..a5d95600 100644 --- a/src/resources/entityPermissions.ts +++ b/src/resources/entityPermissions.ts @@ -43,3 +43,59 @@ export type RolePermission = "delete_assignment" | "start_assignment" | "archive_assignment"; + +export const DEFAULT_PERMISSIONS: RolePermission[] = [ + "view_students", + "view_teachers", + "view_assignments", + "view_classrooms", + "view_entity_roles" +] + +export const ADMIN_PERMISSIONS: RolePermission[] = [ + "view_students", + "view_teachers", + "view_corporates", + "view_mastercorporates", + "edit_students", + "edit_teachers", + "edit_corporates", + "edit_mastercorporates", + "delete_students", + "delete_teachers", + "delete_corporates", + "delete_mastercorporates", + "generate_reading", + "delete_reading", + "generate_listening", + "delete_listening", + "generate_writing", + "delete_writing", + "generate_speaking", + "delete_speaking", + "generate_level", + "delete_level", + "view_classrooms", + "create_classroom", + "rename_classrooms", + "add_to_classroom", + "remove_from_classroom", + "delete_classroom", + "view_entities", + "rename_entity", + "add_to_entity", + "remove_from_entity", + "delete_entity", + "view_entity_roles", + "create_entity_role", + "rename_entity_role", + "edit_role_permissions", + "assign_to_role", + "delete_entity_role", + "view_assignments", + "create_assignment", + "edit_assignment", + "delete_assignment", + "start_assignment", + "archive_assignment" +] diff --git a/src/utils/entities.be.ts b/src/utils/entities.be.ts index f6162119..93b8badd 100644 --- a/src/utils/entities.be.ts +++ b/src/utils/entities.be.ts @@ -1,17 +1,10 @@ import {Entity, EntityWithRoles, Role} from "@/interfaces/entity"; import client from "@/lib/mongodb"; -import { RolePermission } from "@/resources/entityPermissions"; +import { ADMIN_PERMISSIONS, DEFAULT_PERMISSIONS, RolePermission } from "@/resources/entityPermissions"; import { v4 } from "uuid"; import {getRolesByEntities, getRolesByEntity} from "./roles.be"; const db = client.db(process.env.MONGODB_DB); -const DEFAULT_PERMISSIONS: RolePermission[] = [ - "view_students", - "view_teachers", - "view_assignments", - "view_classrooms", - "view_entity_roles" -] export const getEntityWithRoles = async (id: string): Promise => { const entity = await getEntity(id); @@ -45,14 +38,50 @@ export const getEntities = async (ids?: string[]) => { export const createEntity = async (entity: Entity) => { await db.collection("entities").insertOne(entity) - await db.collection("roles").insertOne({ + + const defaultRole = { id: v4(), label: "Default", permissions: DEFAULT_PERMISSIONS, + isDefault: true, entityID: entity.id - }) + } + + const adminRole = { + id: v4(), + label: "Admin", + permissions: ADMIN_PERMISSIONS, + entityID: entity.id + } + + await db.collection("roles").insertOne(defaultRole) + await db.collection("roles").insertOne(adminRole) + + return {default: defaultRole, admin: adminRole} } +export const addUserToEntity = async (user: string, entity: string, role: string) => + await db.collection("users").updateOne( + {id: user}, + { + // @ts-expect-error + $push: { + entities: {id: entity, role}, + }, + }, + ); + +export const addUsersToEntity = async (users: string[], entity: string, role: string) => + await db.collection("users").updateMany( + {id: {$in: users}}, + { + // @ts-expect-error + $push: { + entities: {id: entity, role}, + }, + }, + ); + export const deleteEntity = async (entity: Entity) => { await db.collection("entities").deleteOne({id: entity.id}) await db.collection("roles").deleteMany({entityID: entity.id})