feat(guide): proper order sorting & hidden

This commit is contained in:
2026-03-19 01:28:55 +07:00 Unverified
parent db21e3cea4
commit 3c04403076
6 changed files with 53 additions and 18 deletions
@@ -23,6 +23,7 @@ import { guides } from "@/lib/db/schema";
export default async function GuideEditPage({ export default async function GuideEditPage({
params, params,
}: PageProps<"/admin/guide/[id]">) { }: PageProps<"/admin/guide/[id]">) {
if (!(await adminCheck())) redirect("/login");
const { id } = await params; const { id } = await params;
const [guide] = await db.select().from(guides).where(eq(guides.id, id)); const [guide] = await db.select().from(guides).where(eq(guides.id, id));
if (!guide) notFound(); if (!guide) notFound();
@@ -21,6 +21,7 @@ import { db } from "@/lib/db";
import { guides } from "@/lib/db/schema"; import { guides } from "@/lib/db/schema";
export default async function GuideCreatePage() { export default async function GuideCreatePage() {
if (!(await adminCheck())) redirect("/login");
const [{ maxOrder }] = await db const [{ maxOrder }] = await db
.select({ .select({
maxOrder: sql<number>` maxOrder: sql<number>`
+19
View File
@@ -0,0 +1,19 @@
"use server";
import { eq, not } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import { adminCheck } from "@/lib/auth";
import { db } from "@/lib/db";
import { guides } from "@/lib/db/schema";
export async function hideGuide(id: string) {
if (!(await adminCheck())) redirect("/login");
await db
.update(guides)
.set({
hidden: not(guides.hidden),
})
.where(eq(guides.id, id));
revalidatePath("/admin/guide");
}
+14 -5
View File
@@ -1,12 +1,14 @@
import { Pencil, PlusIcon } from "lucide-react"; import { EyeIcon, EyeOffIcon, Pencil, PlusIcon } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import Image from "@/components/image"; import Image from "@/components/image";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardHeader, CardTitle } from "@/components/ui/card";
import { cn } from "@/lib/utils";
import { searchGuide } from "../../guide/api"; import { searchGuide } from "../../guide/api";
import { hideGuide } from "./api";
export default async function AdminGuidePage() { export default async function AdminGuidePage() {
const list = await searchGuide(); const list = await searchGuide("", true);
return ( return (
<div className="grow-0 flex flex-col pt-5 gap-2 mx-4"> <div className="grow-0 flex flex-col pt-5 gap-2 mx-4">
@@ -20,18 +22,25 @@ export default async function AdminGuidePage() {
{list.map((card) => ( {list.map((card) => (
<Card <Card
key={card.id} key={card.id}
className="bg-card/50 hover:bg-border backdrop-blur-sm transition-colors py-3 sm:py-6 rounded-sm sm:rounded-xl group relative" className={cn(
"bg-card/50 hover:bg-border backdrop-blur-sm transition-colors py-3 sm:py-6 rounded-sm sm:rounded-xl group relative",
card.hidden && "opacity-70",
)}
> >
<div className="flex gap-1 absolute opacity-0 group-hover:opacity-100 transition-opacity top-0 right-0 m-2">
<Button <Button
size="icon" size="icon"
variant="outline" variant="outline"
className="absolute opacity-0 group-hover:opacity-100 transition-opacity top-0 right-0 m-2" onClick={hideGuide.bind(null, card.id)}
asChild
> >
{card.hidden ? <EyeOffIcon /> : <EyeIcon />}
</Button>
<Button size="icon" variant="outline" asChild>
<Link href={`/admin/guide/${card.id}`}> <Link href={`/admin/guide/${card.id}`}>
<Pencil /> <Pencil />
</Link> </Link>
</Button> </Button>
</div>
<CardHeader className="px-3 sm:px-6"> <CardHeader className="px-3 sm:px-6">
<CardTitle>{card.name}</CardTitle> <CardTitle>{card.name}</CardTitle>
<div className="relative w-full aspect-square mt-2 rounded-sm sm:rounded-lg border overflow-hidden"> <div className="relative w-full aspect-square mt-2 rounded-sm sm:rounded-lg border overflow-hidden">
+9 -4
View File
@@ -1,13 +1,18 @@
"use server"; "use server";
import { ilike } from "drizzle-orm"; import { and, desc, ilike, not } from "drizzle-orm";
import { db } from "@/lib/db"; import { db } from "@/lib/db";
import { guides } from "@/lib/db/schema"; import { guides } from "@/lib/db/schema";
export async function searchGuide(search: string = "") { export async function searchGuide(search: string = "", admin = false) {
return await db return await db
.select() .select()
.from(guides) .from(guides)
.where(ilike(guides.name, `%${search}%`)) .where(
.orderBy(guides.id); and(
ilike(guides.name, `%${search}%`),
admin ? undefined : not(guides.hidden),
),
)
.orderBy(desc(guides.order));
} }
+1 -1
View File
@@ -2,6 +2,6 @@ import { searchGuide } from "./api";
import { GuideList } from "./client"; import { GuideList } from "./client";
export default async function GuidePage() { export default async function GuidePage() {
const initialData = await searchGuide(); const initialData = await searchGuide("");
return <GuideList initialList={initialData} />; return <GuideList initialList={initialData} />;
} }