--- import { and, asc, desc, eq, gt, lt, max } from "drizzle-orm"; import AdminLayout from "../../layouts/AdminLayout.astro"; import ImageField from "../../components/admin/ImageField.astro"; import { cases } from "../../db/schema"; import { getDb } from "../../lib/db"; import { getEnv } from "../../lib/env"; import { parseForm } from "../../lib/form"; import { deleteMedia } from "../../lib/media"; import { caseInput } from "../../schemas"; export const prerender = false; const env = getEnv(); const db = getDb(env.DB); let errors: Record = {}; let failedId = ""; let failedAdd = false; function str(form: FormData, key: string): string { return String(form.get(key) ?? "").trim(); } if (Astro.request.method === "POST") { const form = await Astro.request.formData(); const action = str(form, "action"); if (action === "add" || action === "save") { const parsed = parseForm(form, caseInput); if (!parsed.ok) { errors = parsed.errors; failedId = str(form, "id"); failedAdd = action === "add"; } else if (action === "add") { const [row] = await db.select({ m: max(cases.sortOrder) }).from(cases); await db.insert(cases).values({ id: crypto.randomUUID(), title: parsed.data.title, location: parsed.data.location || null, completedAt: parsed.data.completedAt || null, description: parsed.data.description || null, imageUrl: parsed.data.imageUrl || null, sortOrder: (row?.m ?? 0) + 1, status: parsed.data.status, updatedAt: new Date(), }); } else { const id = str(form, "id"); const newImage = parsed.data.imageUrl || null; const [current] = await db.select().from(cases).where(eq(cases.id, id)).limit(1); if (current && current.imageUrl !== newImage) { await deleteMedia(env, current.imageUrl); } await db .update(cases) .set({ title: parsed.data.title, location: parsed.data.location || null, completedAt: parsed.data.completedAt || null, description: parsed.data.description || null, imageUrl: newImage, status: parsed.data.status, updatedAt: new Date(), }) .where(eq(cases.id, id)); } } else if (action === "delete") { const id = str(form, "id"); const [current] = await db.select().from(cases).where(eq(cases.id, id)).limit(1); await deleteMedia(env, current?.imageUrl); await db.delete(cases).where(eq(cases.id, id)); } else if (action === "up" || action === "down") { const id = str(form, "id"); const [current] = await db.select().from(cases).where(eq(cases.id, id)).limit(1); if (current) { const [adj] = await db .select() .from(cases) .where( action === "up" ? lt(cases.sortOrder, current.sortOrder) : gt(cases.sortOrder, current.sortOrder), ) .orderBy(action === "up" ? desc(cases.sortOrder) : asc(cases.sortOrder)) .limit(1); if (adj) { await db.update(cases).set({ sortOrder: adj.sortOrder }).where(eq(cases.id, current.id)); await db.update(cases).set({ sortOrder: current.sortOrder }).where(eq(cases.id, adj.id)); } } } if (Object.keys(errors).length === 0) { return Astro.redirect("/admin/cases"); } } const list = await db.select().from(cases).orderBy(asc(cases.sortOrder)); ---

完成案例

前台「完成案例」區塊會按下面次序顯示(只顯示已發布)。

新增案例

{failedAdd && errors.title && {errors.title}}
{ list.length === 0 ? (

暫時未有案例。

) : ( list.map((item, i) => (
{failedId === item.id && errors.title && {errors.title}}
)) ) }