"use client"; import { useState, useTransition } from "react"; import { CATEGORIES } from "@/lib/ui-config"; import type { CategoryId, Place } from "@/lib/types"; import type { Dispatch } from "@/lib/app-state"; import { FieldLabel } from "@/components/ui-primitives"; import { CoverPicker, commitCover, coverStateFromUrl, disposeCover, type CoverState, } from "@/components/cover-picker"; import { Icons } from "@/components/icons"; import { editPlace } from "@/lib/db/actions"; export function EditPlaceSheet({ place, onClose, dispatch, }: { place: Place; onClose: () => void; dispatch: Dispatch; }) { const [name, setName] = useState(place.name); const [address, setAddress] = useState(place.address); const [category, setCategory] = useState(place.category); const [tags, setTags] = useState(place.tags); const [tagInput, setTagInput] = useState(""); const [cover, setCover] = useState(() => coverStateFromUrl(place.cover_url)); const [saving, setSaving] = useState(false); const [, startTransition] = useTransition(); const isValid = name.trim() && address.trim(); const addTag = (raw: string) => { const t = raw.trim().replace(/,$/, ""); if (t && !tags.includes(t) && tags.length < 10) setTags([...tags, t]); setTagInput(""); }; const submit = () => { if (!isValid) return; setSaving(true); const trimmedAddress = address.trim(); startTransition(async () => { try { // Upload to R2 only now — local blob (if any) is converted to a real // URL. An existing URL is returned unchanged. const coverUrl = await commitCover(cover); const patch = { name: name.trim(), address: trimmedAddress, short_address: trimmedAddress.split(",").slice(0, 2).join(" · "), city: trimmedAddress.split(",").pop()?.trim() || "", category, tags, cover_url: coverUrl, }; await editPlace(place.id, patch); disposeCover(cover); dispatch({ type: "PATCH_PLACE", placeId: place.id, patch }); onClose(); dispatch({ type: "TOAST", value: "Đã lưu thay đổi" }); } catch (e) { setSaving(false); dispatch({ type: "TOAST", value: (e as Error).message || "Lưu thất bại", }); } }); }; return ( <>
Sửa địa điểm
dispatch({ type: "TOAST", value: msg })} />
Tên địa điểm
setName(e.target.value)} />
Địa chỉ
setAddress(e.target.value)} />
Danh mục
{(Object.entries(CATEGORIES) as [CategoryId, typeof CATEGORIES[CategoryId]][]).map( ([k, c]) => { const I = Icons[c.icon as keyof typeof Icons]; return ( ); }, )}
Thẻ · {tags.length}/10
{tags.map((t) => ( {t} ))} { const v = e.target.value; if (v.endsWith(",")) addTag(v); else setTagInput(v); }} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); addTag(tagInput); } else if (e.key === "Backspace" && !tagInput && tags.length) { setTags(tags.slice(0, -1)); } }} placeholder={tags.length ? "" : "Enter để thêm thẻ"} style={{ flex: 1, minWidth: 80, height: 32 }} />
); }