feat: refactor admin components and improve submission handling

This commit is contained in:
Dreamgineer
2025-09-24 05:44:18 -04:00 Unverified
parent f4307e0090
commit 9f588bf0ff
14 changed files with 169 additions and 44 deletions
-6
View File
@@ -11,12 +11,6 @@ import { db } from "@/lib/db";
import { characters, submissions } from "@/lib/db/schema";
import { CopyButton, EnkaBrowser } from "./client";
export const revalidate = 86400; // 24 hours
export async function generateStaticParams() {
return await db.select({ id: submissions.id }).from(submissions);
}
export default async function AdminSubmissionView({
params,
}: {
+28 -3
View File
@@ -3,6 +3,7 @@
import { Lock, Unlock } from "lucide-react";
import Link from "next/link";
import { useParams } from "next/navigation";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import {
@@ -52,11 +53,11 @@ export function SidebarLink({
)}
>
{submission.queue}. {submission.name}
<SidebarMenuBadge>
<SidebarMenuBadge className="pointer-events-auto">
<Checkbox
className="mr-2 z-500"
className="mr-2"
checked={submission.checked}
onClick={() => toggleCheck(submission.id)}
onCheckedChange={() => toggleCheck(submission.id)}
/>
</SidebarMenuBadge>
</Link>
@@ -160,3 +161,27 @@ export function LimitManager({
</SidebarMenu>
);
}
export function SubmissionList({
subs,
}: {
subs: { id: string; name: string; checked: boolean; queue: number }[];
}) {
const [query, setQuery] = useState("");
return (
<>
<Input
placeholder="Search..."
type="search"
onChange={(ev) => setQuery(ev.target.value.toLowerCase())}
/>
{subs
.filter((s) => (s.queue + s.name).toLowerCase().includes(query))
.map((s) => (
<SidebarMenuButton key={s.id} asChild>
<SidebarLink submission={s} />
</SidebarMenuButton>
))}
</>
);
}
+16 -16
View File
@@ -1,6 +1,5 @@
import { Dice3, Trash2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Sidebar,
SidebarContent,
@@ -9,14 +8,12 @@ import {
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarProvider,
SidebarMenu, SidebarProvider
} from "@/components/ui/sidebar";
import { getArtifactConfig } from "@/lib/api";
import { getArtifactConfig, random, wipe } from "@/lib/api";
import { db } from "@/lib/db";
import { submissions } from "@/lib/db/schema";
import { LimitManager, SidebarLink } from "./client";
import { LimitManager, SubmissionList } from "./client";
export default async function AdminLayout({
children,
@@ -40,10 +37,20 @@ export default async function AdminLayout({
<div className="flex justify-between items-center">
<b>Admin</b>
<div className="flex gap-1">
<Button variant="ghost" size="icon" className="size-8">
<Button
variant="ghost"
size="icon"
className="size-8"
onClick={random}
>
<Dice3 size={24} className="size-6" />
</Button>
<Button variant="ghost" size="icon" className="size-8">
<Button
variant="ghost"
size="icon"
className="size-8"
onClick={wipe}
>
<Trash2 size={24} className="size-6 text-red-500" />
</Button>
</div>
@@ -53,14 +60,7 @@ export default async function AdminLayout({
<SidebarGroup>
<SidebarGroupLabel>Submissions</SidebarGroupLabel>
<SidebarMenu>
<form>
<Input placeholder="Search..." type="search" name="q" />
</form>
{subs.map((s) => (
<SidebarMenuButton key={s.id} asChild>
<SidebarLink submission={s} />
</SidebarMenuButton>
))}
<SubmissionList subs={subs} />
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
+1 -1
View File
@@ -1,3 +1,3 @@
export default function AdminPage() {
return <div>Admin Page</div>;
return <div></div>;
}
+4 -1
View File
@@ -36,7 +36,10 @@ export function CharacterChooser() {
const charIds = data.playerInfo.showAvatarInfoList.map((c) =>
c.avatarId.toString(),
);
setChars(await getCharacters(charIds));
const chars = await getCharacters(charIds);
setChars(
charIds.map((e) => chars.find((c) => c.amber === e)!).filter(Boolean),
);
}
fetchChars()
.catch(() => setIsError(true))
+2
View File
@@ -7,8 +7,10 @@ import { submitArtifact } from "@/lib/api";
export function ArtifactFormWrapper(props: React.ComponentProps<"form">) {
const router = useRouter();
async function submit(data: FormData) {
console.log(Object.fromEntries(data.entries()));
submitArtifact(data)
.then((q) => {
if (typeof q === "string") return toast.error(q);
cookieStore.set("sid", q.id);
router.refresh();
})
+3 -4
View File
@@ -4,13 +4,12 @@ import Image from "next/image";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { Tooltip } from "@/components/ui/tooltip";
import { base } from "@/lib/const";
import type { YoutubeLiveInfo } from "../api/live/route";
export async function LiveButton() {
const live: YoutubeLiveInfo = await fetch(`${base}/api/live`).then((e) =>
e.json(),
);
const live: YoutubeLiveInfo = await fetch(`http://localhost:3000/api/live`)
.then((e) => e.json())
.catch(() => "none");
if (live === "none") return;
const { url, thumbnails } = live;
return (
+2
View File
@@ -114,6 +114,8 @@ export default async function ArtifactFormPage() {
<Label htmlFor="character">ตัวละคร</Label>
<ComboBox
placeholder="ค้นหาตัวละคร"
id="character"
name="character"
data={
await db
.select({