Add image upload to cases with cleanup
This commit is contained in:
+22
-10
@@ -1,15 +1,18 @@
|
|||||||
---
|
---
|
||||||
import { and, asc, desc, eq, gt, lt, max } from "drizzle-orm";
|
import { and, asc, desc, eq, gt, lt, max } from "drizzle-orm";
|
||||||
import AdminLayout from "../../layouts/AdminLayout.astro";
|
import AdminLayout from "../../layouts/AdminLayout.astro";
|
||||||
|
import ImageField from "../../components/admin/ImageField.astro";
|
||||||
import { cases } from "../../db/schema";
|
import { cases } from "../../db/schema";
|
||||||
import { getDb } from "../../lib/db";
|
import { getDb } from "../../lib/db";
|
||||||
import { getEnv } from "../../lib/env";
|
import { getEnv } from "../../lib/env";
|
||||||
import { parseForm } from "../../lib/form";
|
import { parseForm } from "../../lib/form";
|
||||||
|
import { deleteMedia } from "../../lib/media";
|
||||||
import { caseInput } from "../../schemas";
|
import { caseInput } from "../../schemas";
|
||||||
|
|
||||||
export const prerender = false;
|
export const prerender = false;
|
||||||
|
|
||||||
const db = getDb(getEnv().DB);
|
const env = getEnv();
|
||||||
|
const db = getDb(env.DB);
|
||||||
|
|
||||||
let errors: Record<string, string> = {};
|
let errors: Record<string, string> = {};
|
||||||
let failedId = "";
|
let failedId = "";
|
||||||
@@ -44,6 +47,11 @@ if (Astro.request.method === "POST") {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const id = str(form, "id");
|
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
|
await db
|
||||||
.update(cases)
|
.update(cases)
|
||||||
.set({
|
.set({
|
||||||
@@ -51,14 +59,17 @@ if (Astro.request.method === "POST") {
|
|||||||
location: parsed.data.location || null,
|
location: parsed.data.location || null,
|
||||||
completedAt: parsed.data.completedAt || null,
|
completedAt: parsed.data.completedAt || null,
|
||||||
description: parsed.data.description || null,
|
description: parsed.data.description || null,
|
||||||
imageUrl: parsed.data.imageUrl || null,
|
imageUrl: newImage,
|
||||||
status: parsed.data.status,
|
status: parsed.data.status,
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
})
|
})
|
||||||
.where(eq(cases.id, id));
|
.where(eq(cases.id, id));
|
||||||
}
|
}
|
||||||
} else if (action === "delete") {
|
} else if (action === "delete") {
|
||||||
await db.delete(cases).where(eq(cases.id, str(form, "id")));
|
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") {
|
} else if (action === "up" || action === "down") {
|
||||||
const id = str(form, "id");
|
const id = str(form, "id");
|
||||||
const [current] = await db.select().from(cases).where(eq(cases.id, id)).limit(1);
|
const [current] = await db.select().from(cases).where(eq(cases.id, id)).limit(1);
|
||||||
@@ -109,8 +120,7 @@ const list = await db.select().from(cases).orderBy(asc(cases.sortOrder));
|
|||||||
<input id="a-date" name="completedAt" type="text" placeholder="例:2026年2月" />
|
<input id="a-date" name="completedAt" type="text" placeholder="例:2026年2月" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="a-img">圖片 URL</label>
|
<ImageField name="imageUrl" label="圖片" scope="cases" id="a-img" />
|
||||||
<input id="a-img" name="imageUrl" type="text" placeholder="https://..." />
|
|
||||||
</div>
|
</div>
|
||||||
<div style="grid-column:1/-1">
|
<div style="grid-column:1/-1">
|
||||||
<label for="a-desc">描述</label>
|
<label for="a-desc">描述</label>
|
||||||
@@ -144,8 +154,13 @@ const list = await db.select().from(cases).orderBy(asc(cases.sortOrder));
|
|||||||
<input name="completedAt" type="text" value={item.completedAt ?? ""} />
|
<input name="completedAt" type="text" value={item.completedAt ?? ""} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>圖片 URL</label>
|
<ImageField
|
||||||
<input name="imageUrl" type="text" value={item.imageUrl ?? ""} />
|
name="imageUrl"
|
||||||
|
label="圖片"
|
||||||
|
scope="cases"
|
||||||
|
value={item.imageUrl}
|
||||||
|
id={`img-${item.id}`}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div style="grid-column:1/-1">
|
<div style="grid-column:1/-1">
|
||||||
<label>描述</label>
|
<label>描述</label>
|
||||||
@@ -159,9 +174,6 @@ const list = await db.select().from(cases).orderBy(asc(cases.sortOrder));
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{item.imageUrl && (
|
|
||||||
<img src={item.imageUrl} alt={item.title} style="margin-top:14px;max-width:220px;border-radius:10px;" />
|
|
||||||
)}
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button type="submit" name="action" value="save">儲存</button>
|
<button type="submit" name="action" value="save">儲存</button>
|
||||||
<button type="submit" name="action" value="up" class="secondary mini" disabled={i === 0}>↑ 上移</button>
|
<button type="submit" name="action" value="up" class="secondary mini" disabled={i === 0}>↑ 上移</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user