[rewrite] Added shadcn ui to frontend and slightly tweaked backend
This commit is contained in:
1 parent
4c8817e853
commit
6e9334ba0e
202 files changed
+19385
-15424
No files matched your search
@@ -0,0 +1,180 @@
|
||||
import { ReactNode } from 'react'
|
||||
import { Link, useLocation } from '@tanstack/react-router'
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from '@/components/ui/collapsible'
|
||||
import {
|
||||
SidebarGroup,
|
||||
SidebarGroupLabel,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
SidebarMenuSub,
|
||||
SidebarMenuSubButton,
|
||||
SidebarMenuSubItem,
|
||||
useSidebar,
|
||||
} from '@/components/ui/sidebar'
|
||||
import { Badge } from '../ui/badge'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '../ui/dropdown-menu'
|
||||
import { NavCollapsible, NavItem, NavLink, type NavGroup } from './types'
|
||||
|
||||
export function NavGroup({ title, items }: NavGroup) {
|
||||
const { state } = useSidebar()
|
||||
const href = useLocation({ select: (location) => location.href })
|
||||
return (
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>{title}</SidebarGroupLabel>
|
||||
<SidebarMenu>
|
||||
{items.map((item) => {
|
||||
const key = `${item.title}-${item.url}`
|
||||
|
||||
if (!item.items)
|
||||
return <SidebarMenuLink key={key} item={item} href={href} />
|
||||
|
||||
if (state === 'collapsed')
|
||||
return (
|
||||
<SidebarMenuCollapsedDropdown key={key} item={item} href={href} />
|
||||
)
|
||||
|
||||
return <SidebarMenuCollapsible key={key} item={item} href={href} />
|
||||
})}
|
||||
</SidebarMenu>
|
||||
</SidebarGroup>
|
||||
)
|
||||
}
|
||||
|
||||
const NavBadge = ({ children }: { children: ReactNode }) => (
|
||||
<Badge className='rounded-full px-1 py-0 text-xs'>{children}</Badge>
|
||||
)
|
||||
|
||||
const SidebarMenuLink = ({ item, href }: { item: NavLink; href: string }) => {
|
||||
const { setOpenMobile } = useSidebar()
|
||||
return (
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
isActive={checkIsActive(href, item)}
|
||||
tooltip={item.title}
|
||||
>
|
||||
<Link to={item.url} onClick={() => setOpenMobile(false)}>
|
||||
{item.icon && <item.icon />}
|
||||
<span>{item.title}</span>
|
||||
{item.badge && <NavBadge>{item.badge}</NavBadge>}
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
const SidebarMenuCollapsible = ({
|
||||
item,
|
||||
href,
|
||||
}: {
|
||||
item: NavCollapsible
|
||||
href: string
|
||||
}) => {
|
||||
const { setOpenMobile } = useSidebar()
|
||||
return (
|
||||
<Collapsible
|
||||
asChild
|
||||
defaultOpen={checkIsActive(href, item, true)}
|
||||
className='group/collapsible'
|
||||
>
|
||||
<SidebarMenuItem>
|
||||
<CollapsibleTrigger asChild>
|
||||
<SidebarMenuButton tooltip={item.title}>
|
||||
{item.icon && <item.icon />}
|
||||
<span>{item.title}</span>
|
||||
{item.badge && <NavBadge>{item.badge}</NavBadge>}
|
||||
<ChevronRight className='ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90' />
|
||||
</SidebarMenuButton>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className='CollapsibleContent'>
|
||||
<SidebarMenuSub>
|
||||
{item.items.map((subItem) => (
|
||||
<SidebarMenuSubItem key={subItem.title}>
|
||||
<SidebarMenuSubButton
|
||||
asChild
|
||||
isActive={checkIsActive(href, subItem)}
|
||||
>
|
||||
<Link to={subItem.url} onClick={() => setOpenMobile(false)}>
|
||||
{subItem.icon && <subItem.icon />}
|
||||
<span>{subItem.title}</span>
|
||||
{subItem.badge && <NavBadge>{subItem.badge}</NavBadge>}
|
||||
</Link>
|
||||
</SidebarMenuSubButton>
|
||||
</SidebarMenuSubItem>
|
||||
))}
|
||||
</SidebarMenuSub>
|
||||
</CollapsibleContent>
|
||||
</SidebarMenuItem>
|
||||
</Collapsible>
|
||||
)
|
||||
}
|
||||
|
||||
const SidebarMenuCollapsedDropdown = ({
|
||||
item,
|
||||
href,
|
||||
}: {
|
||||
item: NavCollapsible
|
||||
href: string
|
||||
}) => {
|
||||
return (
|
||||
<SidebarMenuItem>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<SidebarMenuButton
|
||||
tooltip={item.title}
|
||||
isActive={checkIsActive(href, item)}
|
||||
>
|
||||
{item.icon && <item.icon />}
|
||||
<span>{item.title}</span>
|
||||
{item.badge && <NavBadge>{item.badge}</NavBadge>}
|
||||
<ChevronRight className='ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90' />
|
||||
</SidebarMenuButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent side='right' align='start' sideOffset={4}>
|
||||
<DropdownMenuLabel>
|
||||
{item.title} {item.badge ? `(${item.badge})` : ''}
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{item.items.map((sub) => (
|
||||
<DropdownMenuItem key={`${sub.title}-${sub.url}`} asChild>
|
||||
<Link
|
||||
to={sub.url}
|
||||
className={`${checkIsActive(href, sub) ? 'bg-secondary' : ''}`}
|
||||
>
|
||||
{sub.icon && <sub.icon />}
|
||||
<span className='max-w-52 text-wrap'>{sub.title}</span>
|
||||
{sub.badge && (
|
||||
<span className='ml-auto text-xs'>{sub.badge}</span>
|
||||
)}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</SidebarMenuItem>
|
||||
)
|
||||
}
|
||||
|
||||
function checkIsActive(href: string, item: NavItem, mainNav = false) {
|
||||
return (
|
||||
href === item.url || // /endpint?search=param
|
||||
href.split('?')[0] === item.url || // endpoint
|
||||
!!item?.items?.filter((i) => i.url === href).length || // if child nav is active
|
||||
(mainNav &&
|
||||
href.split('/')[1] !== '' &&
|
||||
href.split('/')[1] === item?.url?.split('/')[1])
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user