Files
buzz/app/(ui)/rubgram/(slip)/admin/slip/[round]/layout.tsx
T
2026-03-19 01:03:23 +07:00

202 lines
6.6 KiB
TypeScript
Executable File

import { eq, sql } from "drizzle-orm";
import { ChevronLeft, ChevronRight, ReceiptText } from "lucide-react";
import Link from "next/link";
import { cache, Suspense } from "react";
import { SidebarLink } from "@/app/(ui)/admin/client";
import { SimpleTooltip } from "@/components/tooltip";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuLabel,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar";
import { db } from "@/lib/db";
import { endgameArchive } from "@/lib/db/schema";
import { parseSearchNumber } from "@/lib/utils";
import { Watcher } from "../../../../admin/client";
export default async function ({
params,
children,
}: {
params: Promise<{ round: string }>;
children: React.ReactNode;
}) {
const { round: r } = await params;
const round = parseSearchNumber(r);
return (
<SidebarProvider>
<Suspense fallback={<Sidebar />}>
<ThisSidebar round={round} />
</Suspense>
<SidebarInset className="bg-transparent">
{children}
<div className="absolute block md:hidden opacity-50 hover:opacity-100 bottom-1 left-1">
<SidebarTrigger />
</div>
<Watcher />
</SidebarInset>
</SidebarProvider>
);
}
const getRange = cache(
async () =>
await db
.select({
min: sql<number>`min(${endgameArchive.round})`,
max: sql<number>`max(${endgameArchive.round})`,
})
.from(endgameArchive),
);
const getEntries = cache(
async (round: number) =>
await db
.select()
.from(endgameArchive)
.where(eq(endgameArchive.round, round))
.orderBy(endgameArchive.queue),
);
async function ThisSidebar({ round }: { round: number }) {
const [[{ min, max }], entries] = await Promise.all([
getRange(),
getEntries(round),
]);
return (
<Sidebar variant="floating">
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
asChild
>
<span>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<div className="bg-sidebar-primary text-sidebar-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
<ReceiptText className="size-4" />
</div>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-(--radix-dropdown-menu-trigger-width)"
align="start"
>
<DropdownMenuGroup>
<DropdownMenuLabel>เลือกรอบ</DropdownMenuLabel>
<DropdownMenuRadioGroup value={`${round}`}>
{range(min, max + 1)
.toReversed()
.map((r) => (
<Link href={`/rubgram/admin/slip/${r}`} key={r}>
<DropdownMenuRadioItem value={`${r}`}>
รอบที่ {r}
</DropdownMenuRadioItem>
</Link>
))}
</DropdownMenuRadioGroup>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-medium">ประวัติสลิปรายได้</span>
<span>รอบที่ {round}</span>
</div>
<div className="flex ml-auto h-full justify-evenly items-center">
{round <= min ? (
<ChevronLeft className="size-4 text-muted-foreground" />
) : (
<SimpleTooltip text="รอบก่อนหน้า" side="right">
<Link
href={`/rubgram/admin/slip/${round - 1}`}
className="h-full flex items-center"
>
<ChevronLeft className="size-4" />
</Link>
</SimpleTooltip>
)}
{round >= max ? (
<ChevronRight className="size-4 text-muted-foreground" />
) : (
<SimpleTooltip text="รอบถัดไป" side="right">
<Link
href={`/rubgram/admin/slip/${round + 1}`}
className="h-full flex items-center"
>
<ChevronRight className="size-4" />
</Link>
</SimpleTooltip>
)}
</div>
</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupContent>
{entries.map((e) => (
<SidebarMenu key={e.id}>
<SidebarMenuItem>
<SidebarLink
prefetch
href={`/rubgram/admin/slip/${round}/${e.queue}`}
>
{e.queue}. {e.name}
</SidebarLink>
</SidebarMenuItem>
</SidebarMenu>
))}
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" className="justify-center">
รายได้รวม {entries.reduce((a, e) => a + e.price, 0)} บาท
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
</Sidebar>
);
}
const range = (start: number, stop?: number, step?: number) => {
// Handle cases where stop/step are optional (e.g., range(5) -> [0, 1, 2, 3, 4])
if (stop === undefined) {
[stop, start] = [start, 0];
}
if (step === undefined) {
step = start < stop ? 1 : -1;
}
// Calculate the length of the array
const length = Math.ceil((stop - start) / step);
// Create the array using Array.from() and a map function
return Array.from({ length }, (_, index) => start + index * step);
};