diff --git a/LogisticsSupportTool_API_DOCS/app/admin/User/GrantROle by ID.bru b/LogisticsSupportTool_API_DOCS/app/admin/User/GrantROle by ID.bru index 8cbd46e..faad09c 100644 --- a/LogisticsSupportTool_API_DOCS/app/admin/User/GrantROle by ID.bru +++ b/LogisticsSupportTool_API_DOCS/app/admin/User/GrantROle by ID.bru @@ -4,23 +4,24 @@ meta { seq: 7 } -post { +patch { url: {{url}}/lst/api/admin/:userID/grant body: json auth: inherit } params:path { - userID: 0hlO48C7Jw1J804FxrCnonK + userID: 0hlO48C7Jw1J804FxrCnonKjQ2zh48R6 } body:json { { - "module":"users", - "role":"admin" + "module":"siloAdjustments", + "role":"viewer" } } settings { encodeUrl: true + timeout: 0 } diff --git a/LogisticsSupportTool_API_DOCS/app/admin/User/RevokeRole by ID.bru b/LogisticsSupportTool_API_DOCS/app/admin/User/RevokeRole by ID.bru new file mode 100644 index 0000000..ef58d43 --- /dev/null +++ b/LogisticsSupportTool_API_DOCS/app/admin/User/RevokeRole by ID.bru @@ -0,0 +1,27 @@ +meta { + name: RevokeRole by ID + type: http + seq: 3 +} + +post { + url: {{url}}/lst/api/admin/:userID/grant + body: json + auth: inherit +} + +params:path { + userID: 0hlO48C7Jw1J804FxrCnonKjQ2zh48R6 +} + +body:json { + { + "module":"siloAdjustments", + "role":"viewer" + } +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/app/src/internal/admin/routes.ts b/app/src/internal/admin/routes.ts index 7e64748..4386acd 100644 --- a/app/src/internal/admin/routes.ts +++ b/app/src/internal/admin/routes.ts @@ -4,22 +4,30 @@ import { requireAuth } from "../../pkg/middleware/authMiddleware.js"; //admin routes import users from "./routes/getUserRoles.js"; import grantRoles from "./routes/grantRole.js"; +import revokeRoles from "./routes/revokeRole.js"; import servers from "./routes/servers/serverRoutes.js"; export const setupAdminRoutes = (app: Express, basePath: string) => { - app.use( - basePath + "/api/admin/server", // will pass bc system admin but this is just telling us we need this - servers - ); + app.use( + basePath + "/api/admin/server", // will pass bc system admin but this is just telling us we need this + servers, + ); - app.use( - basePath + "/api/admin/users", - requireAuth("user", ["systemAdmin"]), // will pass bc system admin but this is just telling us we need this - users - ); - app.use( - basePath + "/api/admin", - requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this - grantRoles - ); + app.use( + basePath + "/api/admin/users", + requireAuth("user", ["systemAdmin"]), // will pass bc system admin but this is just telling us we need this + users, + ); + + app.use( + basePath + "/api/admin", + requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this + grantRoles, + ); + + app.use( + basePath + "/api/admin", + requireAuth("user", ["systemAdmin", "admin"]), // will pass bc system admin but this is just telling us we need this + revokeRoles, + ); }; diff --git a/app/src/internal/admin/routes/grantRole.ts b/app/src/internal/admin/routes/grantRole.ts index 2d442b3..797082c 100644 --- a/app/src/internal/admin/routes/grantRole.ts +++ b/app/src/internal/admin/routes/grantRole.ts @@ -1,74 +1,82 @@ -import { Router } from "express"; import type { Request, Response } from "express"; -import { tryCatch } from "../../../pkg/utils/tryCatch.js"; -import { db } from "../../../pkg/db/db.js"; +import { Router } from "express"; import z from "zod"; +import { db } from "../../../pkg/db/db.js"; import { userRoles } from "../../../pkg/db/schema/user_roles.js"; import { createLogger } from "../../../pkg/logger/logger.js"; +import { tryCatch } from "../../../pkg/utils/tryCatch.js"; const roleSchema = z.object({ - module: z.enum([ - "users", - "system", - "ocp", - "siloAdjustments", - "demandManagement", - "logistics", - "production", - "quality", - "eom", - "forklifts", - ]), - role: z.enum(["admin", "manager", "supervisor", "test,", "viewer"]), + module: z.enum([ + "users", + "system", + "ocp", + "siloAdjustments", + "demandManagement", + "logistics", + "production", + "quality", + "eom", + "forklifts", + ]), + role: z.enum([ + "systemAdmin", + "admin", + "manager", + "supervisor", + "tester", + "user", + "viewer", + ]), }); const router = Router(); -router.post("/:userId/grant", async (req: Request, res: Response) => { - const log = createLogger({ - module: "admin", - subModule: "grantRoles", - }); - const userId = req.params.userId; - console.log(userId); +router.patch("/:userId/grant", async (req: Request, res: Response) => { + const log = createLogger({ + module: "admin", + subModule: "grantRoles", + }); + const userId = req.params.userId; - try { - const validated = roleSchema.parse(req.body); + try { + const validated = roleSchema.parse(req.body); - const data = await db - .insert(userRoles) - .values({ - userId, - module: validated.module, - role: validated.role, - }) - .onConflictDoUpdate({ - target: [userRoles.userId, userRoles.module], - set: { module: validated.module, role: validated.role }, - }); - log.info( - {}, - `Module: ${validated.module}, Role: ${validated.role} as was just granted to userID: ${userId}` - ); - return res.status(200).json({ - success: true, - message: `Module: ${validated.module}, Role: ${validated.role} as was just granted`, - data, - }); - } catch (err) { - if (err instanceof z.ZodError) { - const flattened = z.flattenError(err); - return res.status(400).json({ - error: "Validation failed", - details: flattened, - }); - } + const data = await db + .insert(userRoles) + .values({ + userId: userId, + module: validated.module, + role: validated.role, + }) + .onConflictDoUpdate({ + target: [userRoles.userId, userRoles.module], + set: { module: validated.module, role: validated.role }, + }); + log.info( + {}, + `Module: ${validated.module}, Role: ${validated.role} as was just granted to userID: ${userId}`, + ); + return res.status(200).json({ + success: true, + message: `Module: ${validated.module}, Role: ${validated.role} as was just granted`, + data, + }); + } catch (err) { + if (err instanceof z.ZodError) { + const flattened = z.flattenError(err); + return res.status(400).json({ + error: "Validation failed", + details: flattened, + }); + } - return res.status(400).json({ - success: false, - message: "Invalid input please try again.", - }); - } + return res.status(400).json({ + success: false, + message: "Invalid input please try again.", + error: err, + }); + } }); export default router; diff --git a/app/src/internal/admin/routes/revokeRole.ts b/app/src/internal/admin/routes/revokeRole.ts new file mode 100644 index 0000000..c40ca2a --- /dev/null +++ b/app/src/internal/admin/routes/revokeRole.ts @@ -0,0 +1,71 @@ +import { and, eq } from "drizzle-orm"; +import type { Request, Response } from "express"; +import { Router } from "express"; +import z from "zod"; +import { db } from "../../../pkg/db/db.js"; +import { userRoles } from "../../../pkg/db/schema/user_roles.js"; +import { createLogger } from "../../../pkg/logger/logger.js"; +import { tryCatch } from "../../../pkg/utils/tryCatch.js"; + +const roleSchema = z.object({ + module: z.enum([ + "users", + "system", + "ocp", + "siloAdjustments", + "demandManagement", + "logistics", + "production", + "quality", + "eom", + "forklifts", + ]), +}); + +const router = Router(); + +router.patch("/:userId/revoke", async (req: Request, res: Response) => { + const log = createLogger({ + module: "admin", + subModule: "grantRoles", + }); + const userId = req.params.userId; + + try { + const validated = roleSchema.parse(req.body); + + const data = await db + .delete(userRoles) + .where( + and( + eq(userRoles.userId, userId), + eq(userRoles.module, validated.module), + ), + ); + log.info( + {}, + `Module: ${validated.module}, was just revoked fron userID: ${userId}`, + ); + return res.status(200).json({ + success: true, + message: `Module: ${validated.module}, was just revoked fron userID: ${userId}`, + data, + }); + } catch (err) { + if (err instanceof z.ZodError) { + const flattened = z.flattenError(err); + return res.status(400).json({ + error: "Validation failed", + details: flattened, + }); + } + + return res.status(400).json({ + success: false, + message: "Invalid input please try again.", + error: err, + }); + } +}); + +export default router; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 01b2379..8940676 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -25,6 +25,7 @@ "@tanstack/react-query": "^5.89.0", "@tanstack/react-router": "^1.131.36", "@tanstack/react-router-devtools": "^1.131.36", + "@tanstack/react-table": "^8.21.3", "@types/react-calendar-timeline": "^0.28.6", "axios": "^1.12.2", "better-auth": "^1.3.11", @@ -3372,6 +3373,26 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/@tanstack/react-table": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", + "integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.21.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/@tanstack/router-core": { "version": "1.131.36", "resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.131.36.tgz", @@ -3533,6 +3554,19 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@tanstack/table-core": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz", + "integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tanstack/virtual-file-routes": { "version": "1.131.2", "resolved": "https://registry.npmjs.org/@tanstack/virtual-file-routes/-/virtual-file-routes-1.131.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 143aaa4..c8e65eb 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,6 +27,7 @@ "@tanstack/react-query": "^5.89.0", "@tanstack/react-router": "^1.131.36", "@tanstack/react-router-devtools": "^1.131.36", + "@tanstack/react-table": "^8.21.3", "@types/react-calendar-timeline": "^0.28.6", "axios": "^1.12.2", "better-auth": "^1.3.11", diff --git a/frontend/src/components/navBar/Nav.tsx b/frontend/src/components/navBar/Nav.tsx index 0d1d574..d867e8a 100644 --- a/frontend/src/components/navBar/Nav.tsx +++ b/frontend/src/components/navBar/Nav.tsx @@ -1,81 +1,76 @@ import { Link } from "@tanstack/react-router"; import { useAuth, useLogout } from "../../lib/authClient"; -import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "../ui/dropdown-menu"; import { ModeToggle } from "../mode-toggle"; +import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; import { Button } from "../ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "../ui/dropdown-menu"; export default function Nav() { - const { session } = useAuth(); - const logout = useLogout(); - return ( - + ); } diff --git a/frontend/src/components/navBar/SideBarNav.tsx b/frontend/src/components/navBar/SideBarNav.tsx index e48e76c..9dbf960 100644 --- a/frontend/src/components/navBar/SideBarNav.tsx +++ b/frontend/src/components/navBar/SideBarNav.tsx @@ -1,26 +1,26 @@ -import { - Sidebar, - SidebarContent, - SidebarFooter, - SidebarTrigger, -} from "../ui/sidebar"; -import { Header } from "./Header"; -import Admin from "./Admin"; import { userAccess } from "../../lib/authClient"; +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarTrigger, +} from "../ui/sidebar"; +import Admin from "./Admin"; +import { Header } from "./Header"; export default function SideBarNav() { - return ( -