feat: add user and role view sheets with detailed information display
- Created UserViewSheet component to display user details including username, full name, role, status, and timestamps. - Created RoleViewSheet component to display role details including name, level, status, creation date, and associated permissions. - Introduced permissions configuration in a new permissions.ts file, defining categories and permission codes for better management. - Implemented middleware for authentication, ensuring access control for protected routes.
This commit is contained in:
@@ -25,32 +25,26 @@ import {
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from '@/components/ui/collapsible'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip'
|
||||
import { useAuth, type Role } from '@/contexts/auth-context'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useAuth } from '@/contexts/auth-context'
|
||||
import { DASHBOARD_ITEM, CONFIGURATION_ITEM, NAV_GROUPS, type NavGroup, type NavItem } from '@/config/nav'
|
||||
|
||||
function canSee(item: NavItem, permissions: string[]): boolean {
|
||||
function canSee(item: NavItem, permissions: Set<string>): boolean {
|
||||
if (!item.permission) return true
|
||||
return permissions.includes('*') || permissions.includes(item.permission)
|
||||
return permissions.has(item.permission)
|
||||
}
|
||||
|
||||
function isGroupVisible(group: NavGroup, permissions: string[]): boolean {
|
||||
function isGroupVisible(group: NavGroup, permissions: Set<string>): boolean {
|
||||
return group.items.some((item) => canSee(item, permissions))
|
||||
}
|
||||
|
||||
export function AppSidebar() {
|
||||
const { user, setRole, permissions } = useAuth()
|
||||
const { user, permissions, logout } = useAuth()
|
||||
const pathname = usePathname()
|
||||
|
||||
const dashboardActive = pathname === DASHBOARD_ITEM.href
|
||||
@@ -137,7 +131,6 @@ export function AppSidebar() {
|
||||
>
|
||||
<SidebarGroup className="py-0">
|
||||
<SidebarMenu>
|
||||
{/* Group trigger — looks like a regular nav button */}
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
render={<CollapsibleTrigger />}
|
||||
@@ -154,7 +147,6 @@ export function AppSidebar() {
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
|
||||
{/* Sub-items with indent line */}
|
||||
<CollapsibleContent>
|
||||
<SidebarMenuSub>
|
||||
{visibleItems.map((item) => {
|
||||
@@ -186,20 +178,22 @@ export function AppSidebar() {
|
||||
})}
|
||||
|
||||
{/* Configuration — standalone, no sub-nav */}
|
||||
<SidebarGroup className="py-1">
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
render={<Link href={CONFIGURATION_ITEM.href} />}
|
||||
isActive={configurationActive}
|
||||
tooltip={CONFIGURATION_ITEM.title}
|
||||
>
|
||||
<HugeiconsIcon icon={CONFIGURATION_ITEM.icon} size={18} />
|
||||
<span>{CONFIGURATION_ITEM.title}</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</SidebarGroup>
|
||||
{canSee(CONFIGURATION_ITEM, permissions) && (
|
||||
<SidebarGroup className="py-1">
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
render={<Link href={CONFIGURATION_ITEM.href} />}
|
||||
isActive={configurationActive}
|
||||
tooltip={CONFIGURATION_ITEM.title}
|
||||
>
|
||||
<HugeiconsIcon icon={CONFIGURATION_ITEM.icon} size={18} />
|
||||
<span>{CONFIGURATION_ITEM.title}</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
</SidebarGroup>
|
||||
)}
|
||||
</SidebarContent>
|
||||
|
||||
<SidebarFooter>
|
||||
@@ -209,18 +203,16 @@ export function AppSidebar() {
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-1 flex-col group-data-[collapsible=icon]:hidden">
|
||||
<span className="truncate text-sm font-medium text-sidebar-foreground">
|
||||
{user.fullName}
|
||||
{user?.username ?? '—'}
|
||||
</span>
|
||||
<Select value={user.role} onValueChange={(v) => setRole(v as Role)}>
|
||||
<SelectTrigger className="h-5 border-0 p-0 text-xs text-muted-foreground shadow-none focus:ring-0">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="admin">Admin</SelectItem>
|
||||
<SelectItem value="warehouse">Warehouse</SelectItem>
|
||||
<SelectItem value="sales">Sales</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={logout}
|
||||
className="h-auto justify-start p-0 text-xs text-muted-foreground hover:text-foreground hover:bg-transparent"
|
||||
>
|
||||
Sign out
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</SidebarFooter>
|
||||
|
||||
Reference in New Issue
Block a user