fix: tailwind v4 postcss, auth-server session, eslint cleanups
This commit is contained in:
84
frontend/app/_components/SideNavigation.jsx
Executable file
84
frontend/app/_components/SideNavigation.jsx
Executable file
@@ -0,0 +1,84 @@
|
||||
// File: frontend/app/_components/SideNavigation.jsx
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { Home, FileText, Users, Settings, Package2, Upload, PlusCircle, Workflow, BarChart } from 'lucide-react';
|
||||
import { can } from "@/lib/rbac";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
// Component นี้จะรับ user object ที่มี roles และ permissions มาจาก Server Component Parent
|
||||
export function SideNavigation({ user }) {
|
||||
const pathname = usePathname();
|
||||
|
||||
// สร้าง Array ของเมนูหลักตามโครงสร้างเดิมของคุณ
|
||||
const mainNavLinks = [
|
||||
{ href: '/dashboard', label: 'Dashboard', icon: Home, perm: null }, // หน้าแรกเข้าได้ทุกคน
|
||||
{ href: '/correspondences', label: 'Correspondences', icon: FileText, perm: 'correspondence:view' },
|
||||
{ href: '/drawings', label: 'Drawings', icon: FileText, perm: 'drawing:view' },
|
||||
{ href: '/rfas', label: 'RFAs', icon: FileText, perm: 'rfa:view' },
|
||||
{ href: '/transmittals', label: 'Transmittals', icon: FileText, perm: 'transmittal:view' },
|
||||
{ href: '/reports', label: 'Reports', icon: BarChart, perm: 'report:view' },
|
||||
];
|
||||
|
||||
// สร้าง Array ของเมนู Admin ตามโครงสร้างเดิมของคุณ
|
||||
const adminNavLinks = [
|
||||
{ href: '/admin', label: 'Admin', icon: Settings, perm: 'admin:view' },
|
||||
{ href: '/users', label: 'ผู้ใช้/บทบาท', icon: Users, perm: 'users:manage' },
|
||||
{ href: '/workflow', label: 'Workflow', icon: Workflow, perm: 'workflow:view' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full max-h-screen gap-2">
|
||||
<div className="flex h-14 items-center border-b px-4 lg:h-[60px] lg:px-6">
|
||||
<Link href="/dashboard" className="flex items-center gap-2 font-semibold">
|
||||
<Package2 className="w-6 h-6" />
|
||||
<span className="">LCB P3 DMS</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<nav className="grid items-start px-2 text-sm font-medium lg:px-4">
|
||||
{/* Render เมนูหลัก */}
|
||||
{mainNavLinks.map(({ href, label, icon: Icon, perm }) =>
|
||||
(perm === null || can(user, perm)) && (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={cn(
|
||||
'flex items-center gap-3 rounded-lg px-3 py-2 text-muted-foreground transition-all hover:text-primary',
|
||||
(pathname === href || (href !== '/dashboard' && pathname.startsWith(href))) ? 'bg-muted text-primary' : ''
|
||||
)}
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
{label}
|
||||
</Link>
|
||||
)
|
||||
)}
|
||||
|
||||
{/* Render เมนู Admin ถ้ามีสิทธิ์อย่างน้อย 1 เมนู */}
|
||||
{adminNavLinks.some(link => can(user, link.perm)) && (
|
||||
<>
|
||||
<div className="my-2 border-t"></div>
|
||||
{adminNavLinks.map(({ href, label, icon: Icon, perm }) =>
|
||||
can(user, perm) && (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={cn(
|
||||
'flex items-center gap-3 rounded-lg px-3 py-2 text-muted-foreground transition-all hover:text-primary',
|
||||
pathname.startsWith(href) ? 'bg-muted text-primary' : ''
|
||||
)}
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
{label}
|
||||
</Link>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
{/* ส่วน Card ด้านล่างสามารถคงไว้ หรือเอาออกได้ตามต้องการ */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
44
frontend/app/_components/TopBar.jsx
Executable file
44
frontend/app/_components/TopBar.jsx
Executable file
@@ -0,0 +1,44 @@
|
||||
// File: frontend/app/_components/TopBar.jsx <<'JSX'
|
||||
'use client';
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from "@/components/ui/dropdown-menu";
|
||||
import { Tooltip, TooltipProvider, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
|
||||
import { Bell } from "lucide-react";
|
||||
import { useAuth } from "@/lib/auth";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function TopBar() {
|
||||
const { user, loading, logout } = useAuth();
|
||||
|
||||
return (
|
||||
<header className="flex h-14 items-center gap-4 border-b bg-background px-4 lg:h-[60px] lg:px-6">
|
||||
<div className="flex-1" />
|
||||
<TooltipProvider delayDuration={300}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="relative">
|
||||
<Bell className="h-5 w-5" />
|
||||
<span className="absolute right-1 top-1 inline-flex h-2 w-2 rounded-full bg-primary" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">Notifications</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="ml-2">
|
||||
{loading ? "Loading..." : (user?.first_name || "Account")}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-48">
|
||||
<DropdownMenuItem asChild><Link href="/profile">Profile</Link></DropdownMenuItem>
|
||||
<DropdownMenuItem asChild><Link href="/settings">Settings</Link></DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={logout}>Logout</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
85
frontend/app/_components/navigation.jsx
Executable file
85
frontend/app/_components/navigation.jsx
Executable file
@@ -0,0 +1,85 @@
|
||||
//File: frontend/app/_components/navigation.jsx
|
||||
'use client'; // <-- 1. กำหนดให้ไฟล์นี้เป็น Client Component
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { Home, FileText, Settings, Package2 } from 'lucide-react';
|
||||
import { can } from "@/lib/rbac";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
|
||||
export function Navigation({ user }) { // 2. รับข้อมูล user มาจาก props
|
||||
const pathname = usePathname(); // 3. ใช้งาน usePathname ได้แล้ว
|
||||
|
||||
const navLinks = [
|
||||
{ href: '/dashboard', label: 'Dashboard', icon: Home },
|
||||
{ href: '/correspondences', label: 'Correspondences', icon: FileText },
|
||||
{ href: '/drawings', label: 'Drawings', icon: FileText },
|
||||
// ... เพิ่มเมนูอื่นๆ ตามต้องการ
|
||||
];
|
||||
|
||||
const adminLink = {
|
||||
href: '/admin/users',
|
||||
label: 'Admin',
|
||||
icon: Settings,
|
||||
requiredPermission: 'manage_users'
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full max-h-screen flex-col gap-2">
|
||||
<div className="flex h-14 items-center border-b px-4 lg:h-[60px] lg:px-6">
|
||||
<Link href="/dashboard" className="flex items-center gap-2 font-semibold">
|
||||
<Package2 className="h-6 w-6" />
|
||||
<span className="">LCB P3 DMS</span>
|
||||
</Link>
|
||||
{/* Bell Icon can be here if needed */}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<nav className="grid items-start px-2 text-sm font-medium lg:px-4">
|
||||
{navLinks.map(({ href, label, icon: Icon }) => (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={cn(
|
||||
'flex items-center gap-3 rounded-lg px-3 py-2 text-muted-foreground transition-all hover:text-primary',
|
||||
pathname.startsWith(href) ? 'bg-muted text-primary' : ''
|
||||
)}
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
{label}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{user && can(user, adminLink.requiredPermission) && (
|
||||
<>
|
||||
<div className="my-2 border-t"></div>
|
||||
<Link
|
||||
href={adminLink.href}
|
||||
className={cn(
|
||||
'flex items-center gap-3 rounded-lg px-3 py-2 text-muted-foreground transition-all hover:text-primary',
|
||||
pathname.startsWith('/admin') ? 'bg-muted text-primary' : ''
|
||||
)}
|
||||
>
|
||||
<adminLink.icon className="h-4 w-4" />
|
||||
{adminLink.label}
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
<div className="mt-auto p-4">
|
||||
<Card>
|
||||
<CardHeader className="p-2 pt-0 md:p-4">
|
||||
<CardTitle>Need Help?</CardTitle>
|
||||
<CardDescription>Contact support for any issues or questions.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="p-2 pt-0 md:p-4 md:pt-0">
|
||||
<Button size="sm" className="w-full">Contact</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user