From bee436d3416a84ca8b0c021513187dd932250a17 Mon Sep 17 00:00:00 2001 From: Blake Matthes Date: Tue, 23 Sep 2025 20:54:28 -0500 Subject: [PATCH] feat(frontend): sidebar migration started --- .../Controller/UpdateApp.bru | 23 + .../app/auth/Auth Me.bru | 2 +- .../app/auth/Get Users.bru | 2 +- .../app/auth/Get user Roles.bru | 22 + .../app/auth/GrantROle by ID.bru | 2 +- .../app/auth/Register.bru | 6 +- .../app/auth/Session.bru | 2 +- .../app/auth/Sign-In - email.bru | 2 +- .../app/auth/Sign-In - username.bru | 2 +- .../app/auth/Signout.bru | 2 +- .../app/system/Health.bru | 2 +- .../environments/lst.bru | 2 +- .../admin/controller/systemAdminRole.ts | 2 +- app/src/internal/auth/routes/register.ts | 1 - app/src/internal/auth/routes/routes.ts | 2 + app/src/internal/auth/routes/userroles.ts | 32 + controller/.env-example | 2 + controller/index.html | 4 +- .../053913e4-c93ff02c43e2936f96783ab815d9bc13 | 9 + .../e14c4eec-39d7ed97d04dd2ae5c727a465e0599da | 15 + frontend/package-lock.json | 261 ++++++ frontend/package.json | 7 + frontend/public/imgs/dkLst.png | Bin 0 -> 23064 bytes frontend/public/imgs/ltLst.png | Bin 0 -> 22454 bytes frontend/src/components/mode-toggle.tsx | 37 + frontend/src/components/navBar/Admin.tsx | 72 ++ frontend/src/components/navBar/Header.tsx | 36 + frontend/src/components/navBar/Nav.tsx | 87 +- frontend/src/components/navBar/SideBarNav.tsx | 19 + frontend/src/components/navBar/UserMenu.tsx | 0 frontend/src/components/ui/avatar.tsx | 51 ++ frontend/src/components/ui/card.tsx | 137 ++-- frontend/src/components/ui/checkbox.tsx | 48 +- frontend/src/components/ui/dropdown-menu.tsx | 263 ++++++ frontend/src/components/ui/input.tsx | 32 +- frontend/src/components/ui/label.tsx | 32 +- frontend/src/components/ui/select.tsx | 284 +++---- frontend/src/components/ui/separator.tsx | 26 + frontend/src/components/ui/sheet.tsx | 137 ++++ frontend/src/components/ui/sidebar.tsx | 750 ++++++++++++++++++ frontend/src/components/ui/skeleton.tsx | 13 + frontend/src/components/ui/tooltip.tsx | 59 ++ frontend/src/hooks/use-mobile.ts | 19 + frontend/src/lib/authClient.ts | 75 +- frontend/src/lib/axiosAPI.ts | 6 + .../src/lib/formStuff/components/CheckBox.tsx | 11 +- .../src/lib/providers/SessionProvider.tsx | 12 +- frontend/src/lib/providers/theme-provider.tsx | 67 ++ frontend/src/main.tsx | 1 + frontend/src/routeTree.gen.ts | 97 ++- .../routes/(auth)/-components/LoginForm.tsx | 120 ++- frontend/src/routes/(auth)/login.tsx | 2 +- frontend/src/routes/__root.tsx | 24 +- .../src/routes/_adminLayout/admin/servers.tsx | 9 + .../routes/_adminLayout/admin/settings.tsx | 9 + .../src/routes/_adminLayout/admin/users.tsx | 9 + frontend/src/routes/_adminLayout/route.tsx | 19 + 57 files changed, 2608 insertions(+), 359 deletions(-) create mode 100644 LogisticsSupportTool_API_DOCS/Controller/UpdateApp.bru create mode 100644 LogisticsSupportTool_API_DOCS/app/auth/Get user Roles.bru create mode 100644 app/src/internal/auth/routes/userroles.ts create mode 100644 frontend/.tanstack/tmp/053913e4-c93ff02c43e2936f96783ab815d9bc13 create mode 100644 frontend/.tanstack/tmp/e14c4eec-39d7ed97d04dd2ae5c727a465e0599da create mode 100644 frontend/public/imgs/dkLst.png create mode 100644 frontend/public/imgs/ltLst.png create mode 100644 frontend/src/components/mode-toggle.tsx create mode 100644 frontend/src/components/navBar/Admin.tsx create mode 100644 frontend/src/components/navBar/Header.tsx create mode 100644 frontend/src/components/navBar/SideBarNav.tsx create mode 100644 frontend/src/components/navBar/UserMenu.tsx create mode 100644 frontend/src/components/ui/avatar.tsx create mode 100644 frontend/src/components/ui/dropdown-menu.tsx create mode 100644 frontend/src/components/ui/separator.tsx create mode 100644 frontend/src/components/ui/sheet.tsx create mode 100644 frontend/src/components/ui/sidebar.tsx create mode 100644 frontend/src/components/ui/skeleton.tsx create mode 100644 frontend/src/components/ui/tooltip.tsx create mode 100644 frontend/src/hooks/use-mobile.ts create mode 100644 frontend/src/lib/axiosAPI.ts create mode 100644 frontend/src/lib/providers/theme-provider.tsx create mode 100644 frontend/src/routes/_adminLayout/admin/servers.tsx create mode 100644 frontend/src/routes/_adminLayout/admin/settings.tsx create mode 100644 frontend/src/routes/_adminLayout/admin/users.tsx create mode 100644 frontend/src/routes/_adminLayout/route.tsx diff --git a/LogisticsSupportTool_API_DOCS/Controller/UpdateApp.bru b/LogisticsSupportTool_API_DOCS/Controller/UpdateApp.bru new file mode 100644 index 0000000..0f6e35e --- /dev/null +++ b/LogisticsSupportTool_API_DOCS/Controller/UpdateApp.bru @@ -0,0 +1,23 @@ +meta { + name: UpdateApp + type: http + seq: 3 +} + +get { + url: https://usmcd1vms036.alpla.net/lst/api/controller/update + body: none + auth: inherit +} + +headers { + Content-Type: application/json +} + +body:json { + {} +} + +settings { + encodeUrl: true +} diff --git a/LogisticsSupportTool_API_DOCS/app/auth/Auth Me.bru b/LogisticsSupportTool_API_DOCS/app/auth/Auth Me.bru index b3b6c03..552b483 100644 --- a/LogisticsSupportTool_API_DOCS/app/auth/Auth Me.bru +++ b/LogisticsSupportTool_API_DOCS/app/auth/Auth Me.bru @@ -5,7 +5,7 @@ meta { } get { - url: http://{{url}}/lst/api/user/me + url: {{url}}/lst/api/user/me body: none auth: inherit } diff --git a/LogisticsSupportTool_API_DOCS/app/auth/Get Users.bru b/LogisticsSupportTool_API_DOCS/app/auth/Get Users.bru index eb4db6e..5983f00 100644 --- a/LogisticsSupportTool_API_DOCS/app/auth/Get Users.bru +++ b/LogisticsSupportTool_API_DOCS/app/auth/Get Users.bru @@ -5,7 +5,7 @@ meta { } post { - url: http://{{url}}/lst/api/admin/users + url: {{url}}/lst/api/admin/users body: none auth: inherit } diff --git a/LogisticsSupportTool_API_DOCS/app/auth/Get user Roles.bru b/LogisticsSupportTool_API_DOCS/app/auth/Get user Roles.bru new file mode 100644 index 0000000..c338d20 --- /dev/null +++ b/LogisticsSupportTool_API_DOCS/app/auth/Get user Roles.bru @@ -0,0 +1,22 @@ +meta { + name: Get user Roles + type: http + seq: 9 +} + +get { + url: {{url}}/lst/api/user/roles + body: json + auth: inherit +} + +body:json { + { + "module":"users", + "role":"admin" + } +} + +settings { + encodeUrl: true +} diff --git a/LogisticsSupportTool_API_DOCS/app/auth/GrantROle by ID.bru b/LogisticsSupportTool_API_DOCS/app/auth/GrantROle by ID.bru index a8cc340..1856429 100644 --- a/LogisticsSupportTool_API_DOCS/app/auth/GrantROle by ID.bru +++ b/LogisticsSupportTool_API_DOCS/app/auth/GrantROle by ID.bru @@ -5,7 +5,7 @@ meta { } post { - url: http://{{url}}/lst/api/admin/:userID/grant + url: {{url}}/lst/api/admin/:userID/grant body: json auth: inherit } diff --git a/LogisticsSupportTool_API_DOCS/app/auth/Register.bru b/LogisticsSupportTool_API_DOCS/app/auth/Register.bru index 29c94dd..926f551 100644 --- a/LogisticsSupportTool_API_DOCS/app/auth/Register.bru +++ b/LogisticsSupportTool_API_DOCS/app/auth/Register.bru @@ -5,16 +5,16 @@ meta { } post { - url: http://{{url}}/lst/api/user/register + url: {{url}}/lst/api/user/register body: json auth: inherit } body:json { { - "username":"matthes011", + "username":"matthes01", "name":"blake", - "email":"blake1.matthes@alpla.com", + "email":"blake.matthes@alpla.com", "password":"nova0511" } } diff --git a/LogisticsSupportTool_API_DOCS/app/auth/Session.bru b/LogisticsSupportTool_API_DOCS/app/auth/Session.bru index aff92e6..0019246 100644 --- a/LogisticsSupportTool_API_DOCS/app/auth/Session.bru +++ b/LogisticsSupportTool_API_DOCS/app/auth/Session.bru @@ -5,7 +5,7 @@ meta { } get { - url: http://{{url}}/lst/api/auth/get-session + url: {{url}}/lst/api/auth/get-session body: none auth: inherit } diff --git a/LogisticsSupportTool_API_DOCS/app/auth/Sign-In - email.bru b/LogisticsSupportTool_API_DOCS/app/auth/Sign-In - email.bru index 755d61c..1b59837 100644 --- a/LogisticsSupportTool_API_DOCS/app/auth/Sign-In - email.bru +++ b/LogisticsSupportTool_API_DOCS/app/auth/Sign-In - email.bru @@ -5,7 +5,7 @@ meta { } post { - url: http://{{url}}/lst/api/auth/sign-in/email + url: {{url}}/lst/api/auth/sign-in/email body: json auth: inherit } diff --git a/LogisticsSupportTool_API_DOCS/app/auth/Sign-In - username.bru b/LogisticsSupportTool_API_DOCS/app/auth/Sign-In - username.bru index 4e95f35..41c1faa 100644 --- a/LogisticsSupportTool_API_DOCS/app/auth/Sign-In - username.bru +++ b/LogisticsSupportTool_API_DOCS/app/auth/Sign-In - username.bru @@ -5,7 +5,7 @@ meta { } post { - url: http://{{url}}/lst/api/auth/sign-in/username + url: {{url}}/lst/api/auth/sign-in/username body: json auth: inherit } diff --git a/LogisticsSupportTool_API_DOCS/app/auth/Signout.bru b/LogisticsSupportTool_API_DOCS/app/auth/Signout.bru index 679829e..ee7ae2b 100644 --- a/LogisticsSupportTool_API_DOCS/app/auth/Signout.bru +++ b/LogisticsSupportTool_API_DOCS/app/auth/Signout.bru @@ -5,7 +5,7 @@ meta { } post { - url: http://{{url}}/lst/api/auth/sign-out + url: {{url}}/lst/api/auth/session body: none auth: inherit } diff --git a/LogisticsSupportTool_API_DOCS/app/system/Health.bru b/LogisticsSupportTool_API_DOCS/app/system/Health.bru index b4c3af7..5bdc0b5 100644 --- a/LogisticsSupportTool_API_DOCS/app/system/Health.bru +++ b/LogisticsSupportTool_API_DOCS/app/system/Health.bru @@ -5,7 +5,7 @@ meta { } get { - url: http://{{url}}/lst/api/system/health + url: {{url}}/lst/api/system/health body: none auth: inherit } diff --git a/LogisticsSupportTool_API_DOCS/environments/lst.bru b/LogisticsSupportTool_API_DOCS/environments/lst.bru index 347396f..ce2bdba 100644 --- a/LogisticsSupportTool_API_DOCS/environments/lst.bru +++ b/LogisticsSupportTool_API_DOCS/environments/lst.bru @@ -1,4 +1,4 @@ vars { - url: localhost:4200 + url: http://localhost:4200 session_cookie: } diff --git a/app/src/internal/admin/controller/systemAdminRole.ts b/app/src/internal/admin/controller/systemAdminRole.ts index ab2bf1e..e58867a 100644 --- a/app/src/internal/admin/controller/systemAdminRole.ts +++ b/app/src/internal/admin/controller/systemAdminRole.ts @@ -16,7 +16,7 @@ export const systemAdminRole = async (userId: string) => { }, { userId: userId, - module: "system", + module: "admin", role: "systemAdmin", }, { diff --git a/app/src/internal/auth/routes/register.ts b/app/src/internal/auth/routes/register.ts index c2ff802..c699c37 100644 --- a/app/src/internal/auth/routes/register.ts +++ b/app/src/internal/auth/routes/register.ts @@ -25,7 +25,6 @@ const registerSchema = z.object({ router.post("/", async (req: Request, res: Response) => { // check if we are the first user so we can add as system admin to all modules const totalUsers = await db.select({ count: count() }).from(user); - console.log(totalUsers[0].count); try { // Parse + validate incoming JSON against Zod schema const validated = registerSchema.parse(req.body); diff --git a/app/src/internal/auth/routes/routes.ts b/app/src/internal/auth/routes/routes.ts index 2ffa740..b7758d4 100644 --- a/app/src/internal/auth/routes/routes.ts +++ b/app/src/internal/auth/routes/routes.ts @@ -1,9 +1,11 @@ import type { Express, Request, Response } from "express"; import me from "./me.js"; import register from "./register.js"; +import userRoles from "./userroles.js"; import { requireAuth } from "../../../pkg/middleware/authMiddleware.js"; export const setupAuthRoutes = (app: Express, basePath: string) => { app.use(basePath + "/api/user/me", requireAuth(), me); app.use(basePath + "/api/user/register", register); + app.use(basePath + "/api/user/roles", requireAuth(), userRoles); }; diff --git a/app/src/internal/auth/routes/userroles.ts b/app/src/internal/auth/routes/userroles.ts new file mode 100644 index 0000000..f5f7167 --- /dev/null +++ b/app/src/internal/auth/routes/userroles.ts @@ -0,0 +1,32 @@ +import { Router } from "express"; +import type { Request, Response } from "express"; +import z from "zod"; +import { db } from "../../../pkg/db/db.js"; +import { userRoles } from "../../../pkg/db/schema/user_roles.js"; +import { DrizzleError, eq } from "drizzle-orm"; +import { requireAuth } from "../../../pkg/middleware/authMiddleware.js"; +import { auth } from "../../../pkg/auth/auth.js"; +import { authClient } from "../../../pkg/auth/auth-client.js"; + +const router = Router(); + +router.get("/", async (req: Request, res: Response) => { + const userID = req.user?.id || ""; + try { + // get the roles the user has + const roles = await db + .select() + .from(userRoles) + .where(eq(userRoles.userId, userID)); + + return res.status(200).json({ success: true, data: roles }); + } catch (err) { + if (err instanceof DrizzleError && err.cause) { + return res + .status(400) + .json({ message: "db error", error: err.cause }); + } + } +}); + +export default router; diff --git a/controller/.env-example b/controller/.env-example index ad5b82d..6675001 100644 --- a/controller/.env-example +++ b/controller/.env-example @@ -1,3 +1,5 @@ # What type of deploy ment do we have for the frontend APP_MODE=dev +# The port is only needed if you plan to use a different port but you will need to change it in the wrapper too +PORT=8080 diff --git a/controller/index.html b/controller/index.html index b5de707..45301be 100644 --- a/controller/index.html +++ b/controller/index.html @@ -201,9 +201,9 @@ // You can define your own logMessage function logMessage("info", `Updating to ${fromMyInput}`); - input.value = ""; + //input.value = ""; - input.focus(); + //input.focus(); }; }; diff --git a/frontend/.tanstack/tmp/053913e4-c93ff02c43e2936f96783ab815d9bc13 b/frontend/.tanstack/tmp/053913e4-c93ff02c43e2936f96783ab815d9bc13 new file mode 100644 index 0000000..1bea4e4 --- /dev/null +++ b/frontend/.tanstack/tmp/053913e4-c93ff02c43e2936f96783ab815d9bc13 @@ -0,0 +1,9 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_adminLayout/admin/settings')({ + component: RouteComponent, +}) + +function RouteComponent() { + return
Hello "/_adminLayout/admin/settings"!
+} diff --git a/frontend/.tanstack/tmp/e14c4eec-39d7ed97d04dd2ae5c727a465e0599da b/frontend/.tanstack/tmp/e14c4eec-39d7ed97d04dd2ae5c727a465e0599da new file mode 100644 index 0000000..3797d7b --- /dev/null +++ b/frontend/.tanstack/tmp/e14c4eec-39d7ed97d04dd2ae5c727a465e0599da @@ -0,0 +1,15 @@ +import * as React from 'react' +import { Outlet, createRootRoute } from '@tanstack/react-router' + +export const Route = createRootRoute({ + component: RootComponent, +}) + +function RootComponent() { + return ( + +
Hello "__root"!
+ +
+ ) +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6f41b6e..65236b8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,10 +8,15 @@ "name": "frontend", "version": "0.0.0", "dependencies": { + "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-tooltip": "^1.2.8", "@tailwindcss/vite": "^4.1.13", "@tanstack/react-form": "^1.23.0", "@tanstack/react-query": "^5.89.0", @@ -21,6 +26,7 @@ "better-auth": "^1.3.11", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "js-cookie": "^3.0.5", "lucide-react": "^0.542.0", "react": "^19.1.1", "react-dom": "^19.1.1", @@ -32,6 +38,7 @@ "devDependencies": { "@eslint/js": "^9.33.0", "@tanstack/router-plugin": "^1.131.36", + "@types/js-cookie": "^3.0.6", "@types/node": "^24.3.1", "@types/react": "^19.1.10", "@types/react-dom": "^19.1.7", @@ -1516,6 +1523,33 @@ } } }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz", + "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-is-hydrated": "0.1.0", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-checkbox": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", @@ -1602,6 +1636,42 @@ } } }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", @@ -1644,6 +1714,35 @@ } } }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", @@ -1725,6 +1824,46 @@ } } }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-popper": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", @@ -1828,6 +1967,37 @@ } } }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-select": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", @@ -1871,6 +2041,29 @@ } } }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", + "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", @@ -1889,6 +2082,40 @@ } } }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", + "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", @@ -1959,6 +2186,24 @@ } } }, + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", + "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-layout-effect": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", @@ -3171,6 +3416,13 @@ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, + "node_modules/@types/js-cookie": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -4886,6 +5138,15 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index b030548..3c0aa6c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,10 +10,15 @@ "preview": "vite preview" }, "dependencies": { + "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-tooltip": "^1.2.8", "@tailwindcss/vite": "^4.1.13", "@tanstack/react-form": "^1.23.0", "@tanstack/react-query": "^5.89.0", @@ -23,6 +28,7 @@ "better-auth": "^1.3.11", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "js-cookie": "^3.0.5", "lucide-react": "^0.542.0", "react": "^19.1.1", "react-dom": "^19.1.1", @@ -34,6 +40,7 @@ "devDependencies": { "@eslint/js": "^9.33.0", "@tanstack/router-plugin": "^1.131.36", + "@types/js-cookie": "^3.0.6", "@types/node": "^24.3.1", "@types/react": "^19.1.10", "@types/react-dom": "^19.1.7", diff --git a/frontend/public/imgs/dkLst.png b/frontend/public/imgs/dkLst.png new file mode 100644 index 0000000000000000000000000000000000000000..22ffb87c99ff02fe176ed00599f71d2bf4af07de GIT binary patch literal 23064 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 b|I7=XeTmfSoPP~?)H8#ptDnm{r-UW|oQjZx literal 0 HcmV?d00001 diff --git a/frontend/public/imgs/ltLst.png b/frontend/public/imgs/ltLst.png new file mode 100644 index 0000000000000000000000000000000000000000..cfd8d4c1abeb1ada4b979ce845703f0f54fb5b89 GIT binary patch literal 22454 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
    Mqcyf2kT8<4K7Gkl`%&juN>lGN&CURqII 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 + + + + + setTheme("light")}> + Light + + setTheme("dark")}> + Dark + + setTheme("system")}> + System + + + + ); +} diff --git a/frontend/src/components/navBar/Admin.tsx b/frontend/src/components/navBar/Admin.tsx new file mode 100644 index 0000000..17485b5 --- /dev/null +++ b/frontend/src/components/navBar/Admin.tsx @@ -0,0 +1,72 @@ +import { Server, Settings, User, type LucideIcon } from "lucide-react"; +import { userAccess, type UserRoles } from "../../lib/authClient"; +import { + SidebarGroup, + SidebarGroupContent, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "../ui/sidebar"; +import { Link } from "@tanstack/react-router"; + +type Items = { + title: string; + url: string; + icon: LucideIcon; + role: UserRoles["role"][]; + module: string; + active: boolean; +}; +export default function Admin() { + const items: Items[] = [ + { + title: "Users", + url: "/lst/app/admin/users", + icon: User, + role: ["systemAdmin", "admin"], + module: "admin", + active: true, + }, + { + title: "Settings", + url: "/lst/app/admin/settings", + icon: Settings, + role: ["systemAdmin", "admin"], + module: "admin", + active: true, + }, + { + title: "Servers", + url: "/lst/app/admin/servers", + icon: Server, + role: ["systemAdmin", "admin"], + module: "admin", + active: true, + }, + ]; + return ( + + Admin + + + {items.map((item) => ( + + <> + {userAccess(item.module, item.role) && + item.active && ( + + + + {item.title} + + + )} + + + ))} + + + + ); +} diff --git a/frontend/src/components/navBar/Header.tsx b/frontend/src/components/navBar/Header.tsx new file mode 100644 index 0000000..29765f1 --- /dev/null +++ b/frontend/src/components/navBar/Header.tsx @@ -0,0 +1,36 @@ +import { Link } from "@tanstack/react-router"; +import { + SidebarHeader, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "../ui/sidebar"; + +export function Header() { + return ( + + + + + +
    + Description + +
    + + Logistics Support Tool + + v2.0.0 +
    +
    +
    + +
    +
    +
    + ); +} diff --git a/frontend/src/components/navBar/Nav.tsx b/frontend/src/components/navBar/Nav.tsx index 4ed649a..ef13e81 100644 --- a/frontend/src/components/navBar/Nav.tsx +++ b/frontend/src/components/navBar/Nav.tsx @@ -1,29 +1,80 @@ 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 { Button } from "../ui/button"; export default function Nav() { const { session } = useAuth(); const logout = useLogout(); return ( -