diff --git a/fe-admin/src/pages/system/PermissionsPage.tsx b/fe-admin/src/pages/system/PermissionsPage.tsx index ec60cee..2f66d8b 100644 --- a/fe-admin/src/pages/system/PermissionsPage.tsx +++ b/fe-admin/src/pages/system/PermissionsPage.tsx @@ -1,21 +1,20 @@ import { useMemo, useState } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { toast } from 'sonner' -import { Search, Shield, Check } from 'lucide-react' +import { Search, Shield, Check, Users, KeyRound, BarChart3 } from 'lucide-react' import { PageHeader } from '@/components/PageHeader' -import { EmptyState } from '@/components/EmptyState' import { Input } from '@/components/ui/Input' -import { Select } from '@/components/ui/Select' import { api } from '@/lib/api' import { getErrorMessage } from '@/lib/apiError' +import { cn } from '@/lib/cn' import type { MenuItem, Permission, Role } from '@/types/menu' type CrudKey = 'canRead' | 'canCreate' | 'canUpdate' | 'canDelete' -const CRUD_COLS: { key: CrudKey; label: string; short: string }[] = [ - { key: 'canRead', label: 'Xem', short: 'R' }, - { key: 'canCreate', label: 'Tạo', short: 'C' }, - { key: 'canUpdate', label: 'Sửa', short: 'U' }, - { key: 'canDelete', label: 'Xóa', short: 'D' }, +const CRUD_COLS: { key: CrudKey; label: string; short: string; tone: string }[] = [ + { key: 'canRead', label: 'Xem', short: 'R', tone: 'bg-slate-100 text-slate-700' }, + { key: 'canCreate', label: 'Tạo', short: 'C', tone: 'bg-emerald-100 text-emerald-700' }, + { key: 'canUpdate', label: 'Sửa', short: 'U', tone: 'bg-amber-100 text-amber-700' }, + { key: 'canDelete', label: 'Xóa', short: 'D', tone: 'bg-red-100 text-red-700' }, ] export function PermissionsPage() { @@ -43,9 +42,7 @@ export function PermissionsPage() { mutationFn: async (p: { menuKey: string; canRead: boolean; canCreate: boolean; canUpdate: boolean; canDelete: boolean }) => { await api.put('/permissions', { roleId, ...p }) }, - onSuccess: () => { - qc.invalidateQueries({ queryKey: ['permissions', roleId] }) - }, + onSuccess: () => qc.invalidateQueries({ queryKey: ['permissions', roleId] }), onError: err => toast.error(getErrorMessage(err)), }) @@ -63,17 +60,19 @@ export function PermissionsPage() { }, [menus.data, search]) const stats = useMemo(() => { - const total = (menus.data?.length ?? 0) * 4 + const totalMenus = menus.data?.length ?? 0 + const total = totalMenus * 4 + const breakdown = { canRead: 0, canCreate: 0, canUpdate: 0, canDelete: 0 } let granted = 0 for (const m of menus.data ?? []) { const p = permMap.get(m.key) if (!p) continue - if (p.canRead) granted++ - if (p.canCreate) granted++ - if (p.canUpdate) granted++ - if (p.canDelete) granted++ + if (p.canRead) { granted++; breakdown.canRead++ } + if (p.canCreate) { granted++; breakdown.canCreate++ } + if (p.canUpdate) { granted++; breakdown.canUpdate++ } + if (p.canDelete) { granted++; breakdown.canDelete++ } } - return { granted, total } + return { granted, total, totalMenus, breakdown } }, [menus.data, permMap]) function currentFlags(menuKey: string) { @@ -88,8 +87,7 @@ export function PermissionsPage() { function toggle(menuKey: string, field: CrudKey) { const flags = currentFlags(menuKey) - const next = { ...flags, [field]: !flags[field] } - upsert.mutate({ menuKey, ...next }) + upsert.mutate({ menuKey, ...flags, [field]: !flags[field] }) } function columnAllChecked(field: CrudKey) { @@ -113,124 +111,200 @@ export function PermissionsPage() {