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 0000000..22ffb87 Binary files /dev/null and b/frontend/public/imgs/dkLst.png differ diff --git a/frontend/public/imgs/ltLst.png b/frontend/public/imgs/ltLst.png new file mode 100644 index 0000000..cfd8d4c Binary files /dev/null and b/frontend/public/imgs/ltLst.png differ diff --git a/frontend/src/components/mode-toggle.tsx b/frontend/src/components/mode-toggle.tsx new file mode 100644 index 0000000..e313a72 --- /dev/null +++ b/frontend/src/components/mode-toggle.tsx @@ -0,0 +1,37 @@ +import { Moon, Sun } from "lucide-react"; +import { useTheme } from "../lib/providers/theme-provider"; + +import { Button } from "./ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "./ui/dropdown-menu"; + +export function ModeToggle() { + const { setTheme } = useTheme(); + + return ( + + + + + + 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 ( -