feat(lst): added icons to the sidebar

This commit is contained in:
2025-02-20 10:25:35 -06:00
parent 54b1b6081a
commit 604fdf1545
9 changed files with 291 additions and 52 deletions

View File

@@ -5,7 +5,7 @@ meta {
}
post {
url: http://localhost:4000/api/auth/login
url: http://localhost:3000/api/auth/login
body: json
auth: none
}

View File

@@ -5,16 +5,11 @@ meta {
}
get {
url: http://localhost:4000/api/auth/session
url: http://localhost:3000/api/auth/session
body: none
auth: basic
auth: bearer
}
headers {
:
}
auth:basic {
username: admin
password: pass123
auth:bearer {
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJwYXNzd29yZDEyMyIsInJvbGUiOiJhZG1pbiJ9LCJpYXQiOjE3NDAwMTc4MTcsImV4cCI6MTc0MDAxODExN30.84RPgbfNkLMWPE0dmhzF62yybeb9FpetYGwGsh8_m-g
}

View File

@@ -11,6 +11,7 @@
},
"dependencies": {
"@antfu/ni": "^23.3.1",
"@radix-ui/react-collapsible": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-separator": "^1.1.2",
@@ -21,6 +22,7 @@
"@tanstack/react-router": "^1.106.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"gridstack": "^11.3.0",
"js-cookie": "^3.0.5",
"lucide-react": "^0.475.0",
"react": "^19.0.0",

View File

@@ -3,21 +3,99 @@ import {ProductionSideBar} from "./side-components/production";
import {Header} from "./side-components/header";
import {LogisticsSideBar} from "./side-components/logistics";
import {QualitySideBar} from "./side-components/quality";
import {AdminSideBar} from "./side-components/Admin";
import {ForkliftSideBar} from "./side-components/forklift";
import {EomSideBar} from "./side-components/eom";
import {AdminSideBar} from "./side-components/admin";
type Feature = string;
interface Module {
id: number;
name: string;
features: Feature[];
active: boolean;
}
interface RolePermissions {
[moduleName: string]: Feature[];
}
interface Roles {
[roleName: string]: RolePermissions;
}
interface User {
id: number;
username: string;
role: keyof Roles;
}
const modules: Module[] = [
{id: 1, name: "production", active: true, features: ["view", "edit", "approve"]},
{id: 2, name: "logistics", active: true, features: ["view", "assign", "track"]},
{id: 3, name: "quality", active: false, features: ["view", "audit", "approve"]},
{id: 4, name: "forklift", active: true, features: ["view", "request", "operate"]},
{id: 5, name: "admin", active: true, features: ["view", "manage_users", "view_logs", "settings"]},
];
const rolePermissions: Roles = {
admin: {
production: ["view", "edit", "approve"],
logistics: ["view", "assign", "track"],
quality: ["view", "audit", "approve"],
forklift: ["view", "request", "operate"],
admin: ["view", "manage_users", "view_logs", "settings"],
},
manager: {
production: ["view", "edit"],
logistics: ["view", "assign"],
quality: ["view", "audit"],
forklift: ["view", "request"],
admin: ["view_logs"],
},
supervisor: {
production: ["view"],
logistics: ["view"],
quality: ["view"],
forklift: ["view", "request"],
admin: [],
},
user: {
production: ["view"],
logistics: [],
quality: [],
forklift: [],
admin: [],
},
};
const users: User[] = [
{id: 1, username: "admin", role: "admin"},
{id: 2, username: "manager", role: "manager"},
{id: 3, username: "supervisor", role: "supervisor"},
{id: 4, username: "user", role: "user"},
];
function hasAccess(user: User, moduleName: string, feature: Feature): boolean {
return rolePermissions[user.role]?.[moduleName]?.includes(feature) || false;
}
function moduleActive(moduleName: string): boolean {
const module = modules.find((m) => m.name === moduleName);
return module ? module.active : false;
}
export function AppSidebar() {
const user = {id: 4, username: "admin", role: "admin"};
return (
<Sidebar collapsible="icon">
<SidebarContent>
<Header />
<ProductionSideBar />
<LogisticsSideBar />
<ForkliftSideBar />
<EomSideBar />
<QualitySideBar />
<AdminSideBar />
{hasAccess(user, "production", "view") && moduleActive("production") && <ProductionSideBar />}
{hasAccess(user, "logistics", "view") && moduleActive("logistics") && <LogisticsSideBar />}
{hasAccess(user, "forklift", "view") && moduleActive("forklift") && <ForkliftSideBar />}
{hasAccess(user, "eom", "view") && moduleActive("admin") && <EomSideBar />}
{hasAccess(user, "quality", "view") && moduleActive("quality") && <QualitySideBar />}
{hasAccess(user, "admin", "view") && moduleActive("admin") && <AdminSideBar />}
</SidebarContent>
<SidebarFooter>
<SidebarTrigger />

View File

@@ -1,4 +1,4 @@
import {Printer} from "lucide-react";
import {Atom, Logs, Minus, Plus, Server, Settings, ShieldCheck, Users, Webhook} from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
@@ -6,41 +6,99 @@ import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from "../../ui/sidebar";
import {Collapsible, CollapsibleContent, CollapsibleTrigger} from "../../ui/collapsible";
const items = [
{
title: "Settings",
title: "Servers",
url: "#",
icon: Printer,
},
{
title: "Swagger",
url: "#",
icon: Printer,
},
{
title: "Logs",
url: "#",
icon: Printer,
},
{
title: "Users",
url: "#",
icon: Printer,
},
{
title: "UCD",
url: "#",
icon: Printer,
icon: Server,
isActive: false,
},
];
const data = {
navMain: [
{
title: "Admin",
url: "#",
icon: ShieldCheck,
items: [
{
title: "Settings",
url: "#",
icon: Settings,
isActive: false,
},
{
title: "Swagger",
url: "#",
icon: Webhook,
isActive: false,
},
{
title: "Logs",
url: "#",
icon: Logs,
isActive: false,
},
{
title: "Users",
url: "#",
icon: Users,
isActive: false,
},
{
title: "UCD",
url: "#",
icon: Atom,
isActive: false,
},
],
},
],
};
export function AdminSideBar() {
return (
<SidebarGroup>
<SidebarGroupLabel>Admin</SidebarGroupLabel>
<SidebarGroupLabel>Admin section</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{data.navMain.map((item, index) => (
<Collapsible key={item.title} defaultOpen={index === 1} className="group/collapsible">
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton>
<item.icon />
{item.title}{" "}
<Plus className="ml-auto group-data-[state=open]/collapsible:hidden" />
<Minus className="ml-auto group-data-[state=closed]/collapsible:hidden" />
</SidebarMenuButton>
</CollapsibleTrigger>
{item.items?.length ? (
<CollapsibleContent>
<SidebarMenuSub>
{item.items.map((item) => (
<SidebarMenuSubItem key={item.title}>
<SidebarMenuSubButton asChild isActive={item.isActive}>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
) : null}
</SidebarMenuItem>
</Collapsible>
))}
</SidebarMenu>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>

View File

@@ -1,4 +1,4 @@
import {Printer} from "lucide-react";
import {Forklift, Hourglass, Minus, Plus, Signature} from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
@@ -6,21 +6,88 @@ import {
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from "../../ui/sidebar";
import {Collapsible, CollapsibleTrigger} from "../../ui/collapsible";
import {CollapsibleContent} from "@radix-ui/react-collapsible";
const items = [
{
title: "Qaulity Request",
title: "Gemone",
url: "#",
icon: Printer,
icon: Forklift,
isActive: false,
},
];
const data = {
navMain: [
{
title: "Forklift Management",
url: "#",
icon: Forklift,
items: [
{
title: "All Forklifts",
url: "#",
icon: Forklift,
},
{
title: "Leasing data",
url: "#",
isActive: false,
icon: Signature,
},
{
title: "Forklift Hours",
url: "#",
isActive: false,
icon: Hourglass,
},
],
},
],
};
export function ForkliftSideBar() {
return (
<SidebarGroup>
<SidebarGroupLabel>Forklift</SidebarGroupLabel>
<SidebarGroupLabel>Forklift Section</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{data.navMain.map((item, index) => (
<Collapsible key={item.title} defaultOpen={index === 1} className="group/collapsible">
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton>
<item.icon />
{item.title}{" "}
<Plus className="ml-auto group-data-[state=open]/collapsible:hidden" />
<Minus className="ml-auto group-data-[state=closed]/collapsible:hidden" />
</SidebarMenuButton>
</CollapsibleTrigger>
{item.items?.length ? (
<CollapsibleContent>
<SidebarMenuSub>
{item.items.map((item) => (
<SidebarMenuSubItem key={item.title}>
<SidebarMenuSubButton asChild isActive={item.isActive}>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
) : null}
</SidebarMenuItem>
</Collapsible>
))}
</SidebarMenu>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>

View File

@@ -1,4 +1,4 @@
import {Printer} from "lucide-react";
import {Cylinder, Package, Printer, Truck} from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
@@ -12,22 +12,22 @@ const items = [
{
title: "Silo Adjustments",
url: "#",
icon: Printer,
icon: Cylinder,
},
{
title: "Bulk orders",
url: "#",
icon: Printer,
icon: Truck,
},
{
title: "Forecast",
url: "#",
icon: Printer,
icon: Truck,
},
{
title: "Ocme cycle counts",
url: "#",
icon: Printer,
icon: Package,
},
];

View File

@@ -0,0 +1,31 @@
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
function Collapsible({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
}
function CollapsibleTrigger({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
return (
<CollapsiblePrimitive.CollapsibleTrigger
data-slot="collapsible-trigger"
{...props}
/>
)
}
function CollapsibleContent({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
return (
<CollapsiblePrimitive.CollapsibleContent
data-slot="collapsible-content"
{...props}
/>
)
}
export { Collapsible, CollapsibleTrigger, CollapsibleContent }

View File

@@ -3,7 +3,7 @@ import {verify} from "hono/jwt";
const session = new OpenAPIHono();
const tags = ["Auth"];
const JWT_SECRET = "your-secret-key";
const JWT_SECRET = process.env.JWT_SECRET;
const route = createRoute({
tags: ["Auth"],
@@ -21,6 +21,14 @@ const route = createRoute({
},
description: "Login successful",
},
401: {
content: {
"application/json": {
schema: {message: ""},
},
},
description: "Error of why you were not logged in.",
},
},
});
session.openapi(route, async (c) => {
@@ -39,7 +47,7 @@ session.openapi(route, async (c) => {
try {
const payload = await verify(token, JWT_SECRET);
console.log(payload);
//console.log(payload);
return c.json({data: {token, user: payload.user}});
} catch (err) {
return c.json({error: "Invalid or expired token"}, 401);