feat: scanner sidebar
All checks were successful
Client CMS Check Build (NixCN CMS) TeamCity build finished
Backend Check Build (NixCN CMS) TeamCity build finished

Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
2026-02-13 13:25:36 +08:00
parent 9f511c0682
commit 170afb4a3b
9 changed files with 167 additions and 17 deletions

View File

@@ -0,0 +1,12 @@
import { useUserInfo } from '@/hooks/data/useUserInfo';
import { CheckinScannerNavView } from './checkin-scanner-nav.view';
export function CheckinScannerNavContainer() {
const { data } = useUserInfo();
if ((data.data?.permission_level ?? 0) <= 20) {
return null;
}
return <CheckinScannerNavView />;
}

View File

@@ -0,0 +1,28 @@
import { IconScan } from '@tabler/icons-react';
import { useState } from 'react';
import { Dialog, DialogTrigger } from '@/components/ui/dialog';
import { SidebarMenuButton, SidebarMenuItem } from '@/components/ui/sidebar';
import { CheckinScannerDialogView } from './checkin-scanner.dialog.view';
export function CheckinScannerNavView() {
const [open, setOpen] = useState(false);
const handleScan = (value: string) => {
console.log('Scanned:', value);
setOpen(false);
};
return (
<Dialog open={open} onOpenChange={setOpen}>
<SidebarMenuItem>
<DialogTrigger asChild>
<SidebarMenuButton tooltip="扫码签到">
<IconScan />
<span></span>
</SidebarMenuButton>
</DialogTrigger>
</SidebarMenuItem>
<CheckinScannerDialogView onScan={handleScan} />
</Dialog>
);
}

View File

@@ -13,7 +13,7 @@ import {
SidebarMenuItem,
} from '@/components/ui/sidebar';
export function AppSidebar({ navData, footerWidget, ...props }: React.ComponentProps<typeof Sidebar> & { navData: NavData; footerWidget: React.ReactNode }) {
export function AppSidebar({ navData, footerWidget, secondaryNavExtra, ...props }: React.ComponentProps<typeof Sidebar> & { navData: NavData; footerWidget: React.ReactNode; secondaryNavExtra?: React.ReactNode }) {
return (
<Sidebar collapsible="offcanvas" {...props}>
<SidebarHeader>
@@ -33,7 +33,9 @@ export function AppSidebar({ navData, footerWidget, ...props }: React.ComponentP
</SidebarHeader>
<SidebarContent>
<NavMain items={navData.navMain} />
<NavSecondary items={navData.navSecondary} className="mt-auto" />
<NavSecondary items={navData.navSecondary} className="mt-auto">
{secondaryNavExtra}
</NavSecondary>
</SidebarContent>
<SidebarFooter>
{footerWidget}

View File

@@ -14,6 +14,7 @@ import {
export function NavSecondary({
items,
children,
...props
}: {
items: {
@@ -21,6 +22,7 @@ export function NavSecondary({
url: string;
icon: Icon;
}[];
children?: React.ReactNode;
} & React.ComponentPropsWithoutRef<typeof SidebarGroup>) {
return (
<SidebarGroup {...props}>
@@ -40,6 +42,7 @@ export function NavSecondary({
</Link>
</SidebarMenuItem>
))}
{children}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>

View File

@@ -1,5 +1,6 @@
import { createFileRoute, Outlet, useRouterState } from '@tanstack/react-router';
import { Suspense } from 'react';
import { CheckinScannerNavContainer } from '@/components/checkin/checkin-scanner-nav.container';
import { AppSidebar } from '@/components/sidebar/app-sidebar.view';
import { NavUserContainer } from '@/components/sidebar/nav-user.container';
import { NavUserSkeleton } from '@/components/sidebar/nav-user.skeletion';
@@ -37,6 +38,11 @@ function RouteComponent() {
<NavUserContainer />
</Suspense>
)}
secondaryNavExtra={(
<Suspense fallback={null}>
<CheckinScannerNavContainer />
</Suspense>
)}
variant="inset"
/>
<SidebarInset>