haixunMaster/components/product-profile/brand-form-dialog.tsx

151 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { InlineAlert } from "@/components/ui/inline-alert";
import type { ActionFeedback } from "@/lib/use-action-feedback";
import { parseFetchJson } from "@/lib/utils";
export interface BrandRow {
id: string;
name: string;
notes: string | null;
}
interface BrandFormDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
brand?: BrandRow | null;
onSaved?: (brand: BrandRow) => void;
}
export function BrandFormDialog({ open, onOpenChange, brand, onSaved }: BrandFormDialogProps) {
const [name, setName] = useState("");
const [notes, setNotes] = useState("");
const [submitting, setSubmitting] = useState(false);
const [formFeedback, setFormFeedback] = useState<ActionFeedback | null>(null);
useEffect(() => {
if (!open) return;
setName(brand?.name ?? "");
setNotes(brand?.notes ?? "");
setFormFeedback(null);
}, [open, brand]);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
if (!name.trim()) {
setFormFeedback({ type: "warning", title: "請填寫品牌名稱", message: "品牌名稱為必填。" });
return;
}
setFormFeedback(null);
setSubmitting(true);
const res = await fetch("/api/brands", {
method: brand ? "PATCH" : "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(
brand
? { id: brand.id, name: name.trim(), notes: notes.trim() || null }
: { name: name.trim(), notes: notes.trim() || undefined }
),
});
let data: { error?: string; brand?: { id: string; name: string; notes: string | null } } = {};
try {
data = await parseFetchJson(res);
} catch (err) {
setSubmitting(false);
setFormFeedback({
type: "error",
title: "儲存失敗",
message: err instanceof Error ? err.message : "伺服器回應異常",
});
return;
}
setSubmitting(false);
if (!res.ok) {
setFormFeedback({
type: "error",
title: "儲存失敗",
message: data.error ?? "無法儲存品牌",
});
return;
}
const saved = brand
? { id: brand.id, name: name.trim(), notes: notes.trim() || null }
: data.brand
? {
id: data.brand.id,
name: data.brand.name,
notes: data.brand.notes ?? null,
}
: null;
if (!saved) {
setFormFeedback({
type: "error",
title: "儲存失敗",
message: "伺服器未回傳品牌資料",
});
return;
}
onSaved?.(saved);
onOpenChange(false);
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>{brand ? "編輯品牌" : "新增品牌"}</DialogTitle>
<DialogDescription> AI </DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
{formFeedback && (
<InlineAlert
type={formFeedback.type}
title={formFeedback.title}
message={formFeedback.message}
onDismiss={() => setFormFeedback(null)}
/>
)}
<div className="space-y-1.5">
<Label></Label>
<Input value={name} onChange={(e) => setName(e.target.value)} placeholder="kahu" autoFocus />
</div>
<div className="space-y-1.5">
<Label></Label>
<Textarea
value={notes}
onChange={(e) => setNotes(e.target.value)}
rows={2}
placeholder="例:寵物清潔用品、溫和無添加"
/>
</div>
<DialogFooter>
<Button type="button" variant="ghost" onClick={() => onOpenChange(false)}>
</Button>
<Button type="submit" disabled={submitting}>
{submitting ? "儲存中…" : "儲存"}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
);
}