feat(rubgram): add bulk delete in admin
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useProgress } from "@bprogress/next";
|
||||
import { Calendar, Lock, Unlock } from "lucide-react";
|
||||
import { Calendar, Lock, Trash2, Unlock } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
@@ -39,7 +39,7 @@ import { shared } from "@/lib/comms";
|
||||
import { sse } from "@/lib/db/sse-endpoints";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { getEndgameConfig } from "../api";
|
||||
import { setFree, setLimit, toggleCheck, toggleLock } from "../api";
|
||||
import { bulkDelete, setFree, setLimit, toggleCheck, toggleLock } from "../api";
|
||||
|
||||
export function SidebarLink({
|
||||
submission,
|
||||
@@ -259,6 +259,7 @@ export function SubmissionList({
|
||||
}) {
|
||||
const [query, setQuery] = useState("");
|
||||
const [debug] = shared.state("debug");
|
||||
const [selIds, setSelIds] = shared.state("rubgram.sel_id");
|
||||
const bottomRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -280,23 +281,86 @@ export function SubmissionList({
|
||||
(debug || (s.paid && !s.deleted)) &&
|
||||
(s.publicQueue + s.name).toLowerCase().includes(query),
|
||||
)
|
||||
.map((s) => (
|
||||
<SidebarMenuButton
|
||||
key={s.id}
|
||||
asChild
|
||||
className={cn(((debug && !s.paid) || s.deleted) && "opacity-50")}
|
||||
>
|
||||
<SidebarLink
|
||||
submission={{ ...s, queue: s.publicQueue }}
|
||||
prefetch={!s.deleted}
|
||||
/>
|
||||
</SidebarMenuButton>
|
||||
))}
|
||||
.map((s) =>
|
||||
debug ? (
|
||||
<div key={s.id} className="flex items-center gap-0">
|
||||
<Checkbox
|
||||
className="mx-1 shrink-0"
|
||||
checked={selIds?.includes(s.id) ?? false}
|
||||
onCheckedChange={(ch) =>
|
||||
setSelIds((prev) =>
|
||||
ch
|
||||
? [...(prev || []), s.id]
|
||||
: (prev || []).filter((i) => i !== s.id),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
className={cn(
|
||||
((debug && !s.paid) || s.deleted) && "opacity-50",
|
||||
)}
|
||||
>
|
||||
<SidebarLink
|
||||
submission={{ ...s, queue: s.publicQueue }}
|
||||
prefetch={!s.deleted}
|
||||
/>
|
||||
</SidebarMenuButton>
|
||||
</div>
|
||||
) : (
|
||||
<SidebarMenuButton
|
||||
key={s.id}
|
||||
asChild
|
||||
className={cn(
|
||||
((debug && !s.paid) || s.deleted) && "opacity-50",
|
||||
)}
|
||||
>
|
||||
<SidebarLink
|
||||
submission={{ ...s, queue: s.publicQueue }}
|
||||
prefetch={!s.deleted}
|
||||
/>
|
||||
</SidebarMenuButton>
|
||||
),
|
||||
)}
|
||||
<div ref={bottomRef} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function BulkDeleteButton() {
|
||||
const [selIds, setSelIds] = shared.state("rubgram.sel_id");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
if (!selIds?.length) return null;
|
||||
|
||||
return (
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
className="w-full"
|
||||
disabled={loading}
|
||||
onClick={async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await bulkDelete(selIds);
|
||||
setSelIds([]);
|
||||
toast.success(`ลบ ${selIds.length} รายการแล้ว`);
|
||||
} catch (e) {
|
||||
toast.error(`${e}`);
|
||||
}
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
{loading ? "กำลังลบ..." : <Trash2 />}
|
||||
ลบ {selIds.length} รายการ
|
||||
</Button>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
);
|
||||
}
|
||||
|
||||
export function Watcher() {
|
||||
const router = useRouter();
|
||||
const count = useRef<number | undefined>(undefined);
|
||||
|
||||
@@ -33,6 +33,7 @@ import { db } from "@/lib/db";
|
||||
import { endgameSubmissions } from "@/lib/db/schema";
|
||||
import { getEndgameConfig, random, wipe } from "../api";
|
||||
import {
|
||||
BulkDeleteButton,
|
||||
CalendarButton,
|
||||
LimitManager,
|
||||
SubmissionList,
|
||||
@@ -123,6 +124,7 @@ export default async function AdminLayout({
|
||||
</SidebarGroup>
|
||||
</SidebarContent>
|
||||
<SidebarFooter>
|
||||
<BulkDeleteButton />
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>การตั้งค่า</SidebarGroupLabel>
|
||||
<LimitManager
|
||||
|
||||
+15
-1
@@ -1,6 +1,6 @@
|
||||
"use server";
|
||||
|
||||
import { and, desc, eq, gt, lt, not, sql } from "drizzle-orm";
|
||||
import { and, desc, eq, gt, inArray, lt, not, sql } from "drizzle-orm";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
@@ -433,6 +433,20 @@ export async function cancel(sid: string) {
|
||||
revalidatePath("/rubgram");
|
||||
}
|
||||
|
||||
export async function bulkDelete(ids: string[]) {
|
||||
if (!(await adminCheck())) throw "Unauthorized";
|
||||
await db
|
||||
.update(endgameSubmissions)
|
||||
.set({ deleted: true })
|
||||
.where(inArray(endgameSubmissions.id, ids));
|
||||
|
||||
revalidatePath("/rubgram/admin");
|
||||
|
||||
sse.rubgram.pub("update", { type: "wipe" });
|
||||
|
||||
await actionLog(`Bulk deleted ${ids.length} rubgram submissions`);
|
||||
}
|
||||
|
||||
export async function discordCall(id: string) {
|
||||
if (!(await adminCheck())) throw "Unauthorized";
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ type SharedStates = Partial<{
|
||||
connected: boolean;
|
||||
updated: boolean;
|
||||
"rubgram.services": string[];
|
||||
"rubgram.sel_id": string[];
|
||||
"tl.deleteMode": boolean;
|
||||
inspect: Record<string, string>;
|
||||
// nf(Not Found) and showcase(Not In Showcase) is error dialog that opens guide
|
||||
|
||||
Reference in New Issue
Block a user