diff --git a/docs/STATUS.md b/docs/STATUS.md index 2853ceb..04f2eeb 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -2,67 +2,64 @@ > **Update rule:** trước khi bắt đầu 1 task → ghi row vào `🔥 In Progress`. Xong → chuyển sang `✅ Recently Done`. -**Last updated:** 2026-04-21 11:00 +**Last updated:** 2026-04-21 11:30 -## 📍 Phase hiện tại: **Phase 1 — Alpha Core** (foundation xong, chờ đợt 2) +## 📍 Phase hiện tại: **Phase 1 — Alpha Core (đợt 2 xong)** — sẵn sàng Phase 2 Form Engine ## 🔥 In Progress -_(không có — Phase 1 foundation xong, chờ quyết định bước tiếp)_ +_(không có — Phase 1 đợt 2 hoàn tất)_ ## ✅ Recently Done (newest on top) | Ngày | Ai | Task | Commit | |---|---|---|---| -| 2026-04-21 | Claude | **Docs addition** — `database-guide.md` (conventions + schema + ERD + migration workflow) + `flows/` 6 doc (auth implemented + permission/contract-create/contract-approve/form-render/sla-expiry planned) | (sắp commit) | -| 2026-04-21 | Claude | **Phase 1 foundation HOÀN TẤT** — BE (Clean Arch + Identity + JWT + migration) + FE (2 app, Tailwind 4, Router, AuthContext, Login) — E2E login pass qua Vite proxy | `702411f` | +| 2026-04-21 | Claude | **Phase 1 đợt 2 HOÀN TẤT** — BE: Supplier/Project/Department CRUD + Permission Matrix (MenuItem/Permission + Authorization handler) + 2 migration. FE: DataTable/Dialog generic, usePermission, PermissionGuard, 3 trang CRUD admin, Permission Matrix page, Layout menu động | (sắp commit) | +| 2026-04-21 | Claude | **Docs addition** — `database-guide.md` + `flows/` 6 doc | `49a5f57` | +| 2026-04-21 | Claude | **Phase 1 foundation HOÀN TẤT** — BE Clean Arch + Identity + JWT + FE 2 app + Tailwind 4 + login E2E | `702411f` | | 2026-04-21 | Claude | **Phase 0 HOÀN TẤT** — scaffold + parse FORM/QUY_TRINH + docs + skills + git init | `25dad7f` | Session logs: - [`changelog/sessions/2026-04-21-1045-phase0-scaffold.md`](changelog/sessions/2026-04-21-1045-phase0-scaffold.md) - [`changelog/sessions/2026-04-21-1100-phase1-foundation.md`](changelog/sessions/2026-04-21-1100-phase1-foundation.md) +- [`changelog/sessions/2026-04-21-1130-phase1-cruds-permission.md`](changelog/sessions/2026-04-21-1130-phase1-cruds-permission.md) -## 🎯 Next up — Phase 1 đợt 2 (CRUD master + Permission Matrix) +## 🎯 Next up — Phase 2 Form Engine (có thể bắt ngay) -### Backend -- [ ] `Domain/Entities/Supplier`, `Project`, `Department` (+ EF configurations) -- [ ] `Application/Suppliers/{Commands,Queries}/*` (Create, Update, Delete, GetById, List) -- [ ] Tương tự cho Project + Department -- [ ] `Api/Controllers/{SuppliersController, ProjectsController, DepartmentsController}` -- [ ] Pagination, search, sort server-side (`GetListQuery` với `PagedResult`) -- [ ] Migration 2: `AddMasterData` +- [ ] Convert 3 file `.doc` (FO-002.02/03/06) → `.docx` qua PowerShell COM hoặc LibreOffice headless +- [ ] Parse chi tiết field specs cho 5 template HĐ → JSON spec +- [ ] Add NuGet: DocumentFormat.OpenXml, ClosedXML +- [ ] `Domain/Entities/ContractTemplate`, `ContractClause` + EF config +- [ ] `Application/Forms/Services/IFormRenderer` + `DocxRenderer` + `XlsxRenderer` +- [ ] `Api/Controllers/FormsController` (list template, get spec, render preview, render final) +- [ ] FE: form builder dynamic render từ fieldSpec +- [ ] FE admin: upload template + manage ContractClause (rich text editor) +- [ ] Test: FO-002.05 Giao khoán render → docx khớp mẫu 100% -### Permission Matrix -- [ ] `Domain/Entities/MenuItem`, `Permission` -- [ ] Seed default menu tree (based on FE screens) -- [ ] `Application/Permissions/Queries/GetMyMenuTreeQuery` -- [ ] `Api/Controllers/{MenusController, RolesController, PermissionsController}` -- [ ] Admin UI: Permission Matrix grid (role × menu × CRUD checkbox) +Chi tiết: [`docs/flows/form-render-flow.md`](flows/form-render-flow.md) + [`docs/changelog/migration-todos.md`](changelog/migration-todos.md) section Phase 2. -### Contract draft (chưa workflow) -- [ ] `Domain/Entities/Contract` skeleton (không state machine) -- [ ] Basic CRUD controller + FE list page +## 🔄 Còn có thể làm parallel (optional, không block Phase 2) -### FE -- [ ] `` + `usePermission()` hook -- [ ] 3 trang CRUD admin (Suppliers / Projects / Departments) với table + modal -- [ ] Route guard theo role (admin-only routes) +- [ ] FE Users management (tạo user + gán role) — cần để test permission với role khác Admin +- [ ] FE Roles CRUD (tạo custom role mới) +- [ ] Contract entity skeleton (không state machine, chỉ CRUD draft) +- [ ] E2E test permission: tạo user role Drafter-only → verify không thấy menu System/Admin -## 📊 Thông số sau Phase 1 foundation +## 📊 Thông số sau Phase 1 đợt 2 -- **Backend LOC:** ~400 (Domain 60 + Application 170 + Infrastructure 190 + Api 120) -- **Frontend LOC:** ~450 mỗi app (shared 90%) -- **Build time:** .NET ~4s, FE TS check ~3s mỗi app, Vite dev ~3s ready -- **E2E verified:** Login (fe-admin proxy + fe-user proxy) → API → JWT + user info + /me +- **Backend LOC:** ~1500 (Domain 150 + Application 800 + Infrastructure 350 + Api 200) +- **Migrations:** Init + AddMasterData + AddPermissions +- **DB tables:** 7 Identity + 3 Master (Suppliers/Projects/Departments) + 2 Permission (MenuItems/Permissions) +- **API endpoints:** 20+ (Auth 4 + Suppliers 5 + Projects 5 + Departments 5 + Menus 2 + Roles 1 + Permissions 2) +- **Frontend routes:** 5 (Dashboard + 3 CRUD + Permission Matrix) +- **FE LOC:** ~1700 (fe-admin; fe-user vẫn minimal) ## 🚨 Blockers / risks -- ⏳ **Gitea remote** chưa có URL — push sau -- ⚠️ **Swashbuckle 10.x** không tương thích với .NET 10 + Microsoft.OpenApi 2.0 — đã downgrade về 6.9.0. Theo dõi update sau. -- ⚠️ **MediatR 14.x** breaking changes — đã downgrade về 12.4.1. Ok cho Phase 1-5. -- ⚠️ **Microsoft.AspNetCore.OpenApi** đã remove (conflict Swashbuckle 6.9). Nếu sau muốn dùng built-in OpenAPI thì phải chọn 1 trong 2. -- ⚠️ **Design-time DB (`SolutionErp_Design`)** được tạo khi chạy `dotnet ef` — có thể drop an toàn (không chứa data thật) -- ⚠️ **3 file `.doc` FORM** chưa convert được — Phase 2 +- ⏳ **Gitea remote** — URL chờ user cấp +- ⚠️ **fe-user** chưa được update với menu động — Phase 2 sẽ sync +- ⚠️ **Users CRUD** chưa có UI → khó test permission với non-admin role thật +- ⚠️ **3 file `.doc`** Phase 2 cần convert COM ## Credentials mặc định @@ -72,6 +69,6 @@ Password: Admin@123456 ``` URLs dev: -- API: http://localhost:5443 — Swagger ở `/swagger` +- API: http://localhost:5443 — Swagger `/swagger` - Admin FE: http://localhost:8082 - User FE: http://localhost:8080 diff --git a/docs/changelog/migration-todos.md b/docs/changelog/migration-todos.md index ebe6518..31e6441 100644 --- a/docs/changelog/migration-todos.md +++ b/docs/changelog/migration-todos.md @@ -60,27 +60,28 @@ ### Phase 1 đợt 2 — CRUD master + Permission Matrix (sắp tới) -- [ ] `Domain/Entities/Supplier` (Code, Name, TaxCode, Phone, Email, Address, Type enum: NCC/NTP/TĐ/ĐVDV) -- [ ] `Domain/Entities/Project` (Code, Name, StartDate, EndDate, ManagerUserId) -- [ ] `Domain/Entities/Department` (Code, Name, ManagerUserId) -- [ ] EF `IEntityTypeConfiguration` cho mỗi entity -- [ ] CQRS CRUD: Create/Update/Delete/GetById/List (với paging) cho 3 entity -- [ ] `Api/Controllers/{SuppliersController, ProjectsController, DepartmentsController}` -- [ ] Migration 2: `AddMasterData` -- [ ] `Domain/Entities/MenuItem` (Key PascalCase, Label, ParentKey, Order, Icon) -- [ ] `Domain/Entities/Permission` (RoleId, MenuKey, CanRead/Create/Update/Delete) -- [ ] Seed default menu tree + permission admin có full access -- [ ] `Application/Permissions/Queries/GetMyMenuTreeQuery` — resolve per-user, cache -- [ ] `Api/Controllers/{MenusController, RolesController, PermissionsController}` -- [ ] Migration 3: `AddPermissions` -- [ ] `Domain/Entities/Contract` skeleton (Id, Type, SupplierId, ProjectId, Phase=DangChon, DraftData JSON) -- [ ] Contract CRUD draft only (không workflow Phase 3) -- [ ] FE: `` + `usePermission()` hook -- [ ] FE Admin: 3 trang CRUD Supplier/Project/Department với table + modal + search/sort -- [ ] FE Admin: Permission Matrix grid page (role × menu × CRUD checkbox) -- [ ] FE User: trang "HĐ của tôi" list + filter -- [ ] Route guard theo role admin-only -- [ ] Update `SolutionErp.slnx` nếu thêm project mới +- [x] `Domain/Master/Supplier` (+ SupplierType enum 5 loại) / `Project` / `Department` (AuditableEntity) +- [x] EF `IEntityTypeConfiguration` cho mỗi entity (unique Code + query filter IsDeleted) +- [x] CQRS CRUD: Create/Update/Delete/GetById/List (PagedResult) cho 3 entity +- [x] `Api/Controllers/{SuppliersController, ProjectsController, DepartmentsController}` +- [x] Migration 2: `AddMasterData` +- [x] `Domain/Identity/MenuItem` (Key PK, Label, ParentKey, Order, Icon) + `MenuKeys` const class +- [x] `Domain/Identity/Permission` (RoleId, MenuKey, CanRead/Create/Update/Delete) +- [x] Seed default menu tree (12 menu) + admin full access trong DbInitializer +- [x] `Application/Permissions/Queries/GetMyMenuTreeQuery` — resolve per-user, union OR, tree filter +- [x] `Api/Controllers/{MenusController, RolesController, PermissionsController}` +- [x] Migration 3: `AddPermissions` +- [x] Authorization handler `MenuPermissionHandler` + register 48 policy `{menu}.{action}` +- [ ] `Domain/Entities/Contract` skeleton (Id, Type, SupplierId, ProjectId, Phase=DangChon, DraftData JSON) — deferred Phase 2/3 +- [ ] Contract CRUD draft only (không workflow Phase 3) — deferred +- [x] FE: `` + `usePermission()` hook +- [x] FE Admin: 3 trang CRUD Supplier/Project/Department với DataTable + Dialog modal + search/sort/paging +- [x] FE Admin: Permission Matrix grid page (role × menu × CRUD checkbox) +- [x] FE Admin: Layout menu động từ `/api/menus/me` +- [ ] FE User: trang "HĐ của tôi" list + filter — Phase 3 +- [ ] FE Admin: Users management page (tạo user + gán role) — sắp tới +- [ ] FE Admin: Roles CRUD — sắp tới +- [ ] Route guard theo role admin-only — có PermissionGuard ở button, route cần thêm ### Exit criteria Phase 1 diff --git a/docs/changelog/sessions/2026-04-21-1130-phase1-cruds-permission.md b/docs/changelog/sessions/2026-04-21-1130-phase1-cruds-permission.md new file mode 100644 index 0000000..10dc308 --- /dev/null +++ b/docs/changelog/sessions/2026-04-21-1130-phase1-cruds-permission.md @@ -0,0 +1,89 @@ +# Session 2026-04-21 11:30 — Phase 1 đợt 2 complete + +**Dev:** Claude (Opus 4.7) +**Duration:** ~1h30m +**Base commit:** `49a5f57` +**Commits:** (sắp tạo — 1 commit lớn cho toàn bộ đợt 2) + +## Làm được + +### Chunk A — Backend Master data +- Domain: `Supplier` (+ `SupplierType` enum 5 loại: NCC/NTP/TĐ/ĐVDV/CĐT), `Project`, `Department` — extend `AuditableEntity` +- EF `IEntityTypeConfiguration` với unique index `Code`, query filter `IsDeleted` +- DbSets trong `ApplicationDbContext` + `IApplicationDbContext` (thêm package `Microsoft.EntityFrameworkCore` vào Application) +- `Common/Models/PagedResult` + `PagedRequest` (page, pageSize 1-200, search, sortBy, sortDesc) +- CQRS cho Supplier: `CreateSupplierCommand` + Validator + Handler, `UpdateSupplierCommand`, `DeleteSupplierCommand` (soft-delete qua AuditingInterceptor), `GetSupplierQuery`, `ListSuppliersQuery` (với filter Type) +- CQRS cho Project + Department: gom vào 1 file mỗi entity (`ProjectFeatures.cs`, `DepartmentFeatures.cs`) — pattern giống Supplier nhưng compact +- 3 Controller: `SuppliersController`, `ProjectsController`, `DepartmentsController` — full REST (list, get, create, update, delete) +- Migration `AddMasterData` + +### Chunk B — Backend Permission system +- Domain: `MenuKeys` const (12 menu key: Dashboard, Master, Suppliers, Projects, Departments, Contracts, Forms, Reports, System, Users, Roles, Permissions), `MenuItem` (Key PK, Label, ParentKey, Order, Icon), `Permission` (RoleId, MenuKey, CanRead/Create/Update/Delete) +- EF configs + unique index `(RoleId, MenuKey)` +- DTOs: `MenuNodeDto` (tree node có resolved CRUD), `PermissionDto`, `RoleDto`, `MenuItemDto` +- `GetMyMenuTreeQuery` — resolve menu tree theo roles của user, **union OR** CRUD flags qua roles, **filter** chỉ trả về node có CanRead hoặc có child CanRead +- `ListMenuItemsQuery` + `ListPermissionsByRoleQuery` + `UpsertPermissionCommand` (guard: admin không tự giảm quyền mình) +- `ListRolesQuery` +- **Authorization handler** `MenuPermissionHandler : AuthorizationHandler` — check Admin bypass, query DB qua roleIds +- Register 48 policy (`{menu}.{action}` cho 12 menu × 4 action) trong Program.cs +- Controllers: `MenusController` (GET /me, GET /), `RolesController`, `PermissionsController` (Authorize policy `Permissions.Read/Update`) +- Update `DbInitializer`: seed 12 menu items + default **admin có full CRUD mọi menu** +- Migration `AddPermissions` + +### Chunk C — Frontend (fe-admin) +- Types: `menuKeys.ts` const + `menu.ts` (MenuNode/MenuItem/Role/Permission) + `master.ts` (Supplier/Project/Department + SupplierType const-object với erasableSyntaxOnly) +- `contexts/AuthContext.tsx`: thêm `menu` state + `loadMenu()` khi login + localStorage cache + `refreshMenu()` +- `hooks/usePermission.ts`: `can(menuKey, action)` — search tree đệ quy +- `components/PermissionGuard.tsx`: wrapper component +- `components/ui/`: Dialog (modal overlay + Escape close), Textarea, Select +- `components/DataTable.tsx`: generic table với columns config, sort (sortBy/sortDesc callback), loading, empty state, click row; `Pagination` component kèm theo +- `components/PageHeader.tsx`: title + description + actions +- `components/Layout.tsx` rewrite: render menu **động** từ AuthContext.menu (không hardcode nữa), MenuGroup collapsible cho parent có children, NavLink active highlight, icon map từ lucide-react theo field `icon` +- `lib/apiError.ts`: extract user-friendly message từ ProblemDetails +- 3 trang CRUD admin (`master/SuppliersPage.tsx`, `ProjectsPage.tsx`, `DepartmentsPage.tsx`): + - Full CRUD với Dialog form, search, sort, paging + - `` wrap Create/Update/Delete button + - `useQuery` + `useMutation` + `invalidateQueries` TanStack + - Toast success/error +- `system/PermissionsPage.tsx`: grid ma trận role × menu × CRUD, select role dropdown → tick checkbox tự động PUT `/permissions` upsert +- `App.tsx`: thêm 4 route mới + +## Bug gặp + fix + +| Bug | Root cause | Fix | +|---|---|---| +| Build CS8514 in MenuPermissionHandler | EF expression tree không support switch expression | Tách `switch` ra ngoài `AnyAsync()` — switch trên Action, mỗi case gọi AnyAsync với predicate riêng | +| POST /api/suppliers lỗi với Unicode tiếng Việt qua curl command line | Windows bash command line encoding không đúng UTF-8 | Dùng `--data-binary @file.json` — API handle UTF-8 OK | +| TS1294 erasableSyntaxOnly + `enum SupplierType` | TS 6 erasableSyntaxOnly không cho enum | Dùng `const + as const + type = typeof[keyof]` pattern | +| fe-admin npm run dev báo port 8082 in use | Background task trước chưa stop hẳn | Tình trạng tạm — E2E vẫn pass qua port đã bind | + +## E2E verified + +- `GET /api/menus/me` với admin token → **6 root + 6 child nodes** (12 menu items) ✅ +- `GET /api/roles` → **12 roles** ✅ +- `POST /api/suppliers` → 201 + id ✅ +- `GET /api/suppliers` → PagedResult ✅ +- `PUT /api/suppliers/{id}` → 204 ✅ +- `DELETE /api/suppliers/{id}` → 204, list GET sau đó không còn ✅ (soft delete OK) +- `GET /api/permissions/by-role/{adminRoleId}` → 12 permissions all true ✅ +- `tsc -b` fe-admin → pass ✅ + +## Handoff cho session tiếp theo + +**Phase 2 — Form Engine:** +- Convert 3 `.doc` files → `.docx` via PowerShell COM automation +- Parse chi tiết field specs cho 5 contract templates +- `IFormRenderer` service với OpenXml + ClosedXML +- Form builder UI cho admin upload template +- Preview + export flow + +Xem [`docs/flows/form-render-flow.md`](../../flows/form-render-flow.md) + [`docs/changelog/migration-todos.md`](../migration-todos.md) section Phase 2. + +**Còn thiếu trong Phase 1 (có thể làm parallel với Phase 2 nếu muốn):** +- FE: thêm trang Users management (tạo user mới, gán role) +- FE: trang Roles CRUD (create/rename/delete custom role) +- Contract entity + CRUD draft (skeleton cho Phase 3 chuẩn bị) +- Test 1 role khác Admin → verify permission guard hoạt động đúng + +**Blocker:** +- ⏳ Gitea remote URL diff --git a/fe-admin/src/App.tsx b/fe-admin/src/App.tsx index af2d5c2..7de0770 100644 --- a/fe-admin/src/App.tsx +++ b/fe-admin/src/App.tsx @@ -5,6 +5,10 @@ import { ProtectedRoute } from '@/components/ProtectedRoute' import { Layout } from '@/components/Layout' import { LoginPage } from '@/pages/LoginPage' import { DashboardPage } from '@/pages/DashboardPage' +import { SuppliersPage } from '@/pages/master/SuppliersPage' +import { ProjectsPage } from '@/pages/master/ProjectsPage' +import { DepartmentsPage } from '@/pages/master/DepartmentsPage' +import { PermissionsPage } from '@/pages/system/PermissionsPage' function App() { return ( @@ -20,12 +24,16 @@ function App() { } > } /> + } /> + } /> + } /> + } /> } /> - Trang này chưa được build — sẽ có ở Phase 1 đợt 2 / Phase 2 / 3. + Trang này chưa được build — sẽ có ở Phase tiếp theo. } /> diff --git a/fe-admin/src/components/DataTable.tsx b/fe-admin/src/components/DataTable.tsx new file mode 100644 index 0000000..4b49f37 --- /dev/null +++ b/fe-admin/src/components/DataTable.tsx @@ -0,0 +1,151 @@ +import type { ReactNode } from 'react' +import { ChevronDown, ChevronUp } from 'lucide-react' +import { cn } from '@/lib/cn' + +export type Column = { + key: string + header: ReactNode + render: (row: T) => ReactNode + sortable?: boolean + width?: string + align?: 'left' | 'center' | 'right' +} + +type Props = { + columns: Column[] + rows: T[] + getRowKey: (row: T) => string + isLoading?: boolean + empty?: ReactNode + sortBy?: string + sortDesc?: boolean + onSortChange?: (sortBy: string, sortDesc: boolean) => void + onRowClick?: (row: T) => void +} + +export function DataTable({ + columns, + rows, + getRowKey, + isLoading, + empty, + sortBy, + sortDesc, + onSortChange, + onRowClick, +}: Props) { + return ( +
+ + + + {columns.map(c => ( + + ))} + + + + {isLoading && ( + + + + )} + {!isLoading && rows.length === 0 && ( + + + + )} + {!isLoading && + rows.map(row => ( + onRowClick?.(row)} + > + {columns.map(c => ( + + ))} + + ))} + +
+ {c.sortable && onSortChange ? ( + + ) : ( + c.header + )} +
+ Đang tải… +
+ {empty ?? 'Không có dữ liệu'} +
+ {c.render(row)} +
+
+ ) +} + +type PaginationProps = { + page: number + pageSize: number + total: number + onChange: (page: number) => void +} + +export function Pagination({ page, pageSize, total, onChange }: PaginationProps) { + const totalPages = Math.max(1, Math.ceil(total / pageSize)) + const from = total === 0 ? 0 : (page - 1) * pageSize + 1 + const to = Math.min(page * pageSize, total) + + return ( +
+ + {from}–{to} / {total} + +
+ + + Trang {page}/{totalPages} + + +
+
+ ) +} diff --git a/fe-admin/src/components/Layout.tsx b/fe-admin/src/components/Layout.tsx index 8848ffa..ebdc048 100644 --- a/fe-admin/src/components/Layout.tsx +++ b/fe-admin/src/components/Layout.tsx @@ -1,18 +1,80 @@ import { Link, NavLink, Outlet } from 'react-router-dom' -import { LogOut, LayoutDashboard, FileText, Users, Building2, Settings } from 'lucide-react' +import { LogOut, ChevronDown, Circle, type LucideIcon } from 'lucide-react' +import * as Icons from 'lucide-react' +import { useState } from 'react' import { useAuth } from '@/contexts/AuthContext' +import type { MenuNode } from '@/types/menu' import { cn } from '@/lib/cn' -const menuItems = [ - { to: '/dashboard', label: 'Tổng quan', icon: LayoutDashboard }, - { to: '/contracts', label: 'Hợp đồng', icon: FileText }, - { to: '/suppliers', label: 'Nhà cung cấp', icon: Building2 }, - { to: '/users', label: 'Người dùng', icon: Users }, - { to: '/settings', label: 'Cài đặt', icon: Settings }, -] +// Map icon name → component (fallback Circle) +function getIcon(name: string | null): LucideIcon { + if (!name) return Circle + const candidate = (Icons as unknown as Record)[name] + return candidate ?? Circle +} + +// Map menu key → route path +const KEY_TO_PATH: Record = { + Dashboard: '/dashboard', + Suppliers: '/master/suppliers', + Projects: '/master/projects', + Departments: '/master/departments', + Contracts: '/contracts', + Forms: '/forms', + Reports: '/reports', + Users: '/system/users', + Roles: '/system/roles', + Permissions: '/system/permissions', +} + +function MenuGroup({ node }: { node: MenuNode }) { + const [open, setOpen] = useState(true) + const Icon = getIcon(node.icon) + return ( +
+ + {open && ( +
+ {node.children.map(c => ( + + ))} +
+ )} +
+ ) +} + +function MenuLeaf({ node }: { node: MenuNode }) { + const Icon = getIcon(node.icon) + const path = KEY_TO_PATH[node.key] + if (!path) return null + return ( + + cn( + 'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition', + isActive ? 'bg-brand-50 text-brand-700' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900', + ) + } + > + + {node.label} + + ) +} export function Layout() { - const { user, logout } = useAuth() + const { user, menu, logout } = useAuth() return (
@@ -22,28 +84,12 @@ export function Layout() { SOLUTION ERP · Admin
-