116 lines
4.1 KiB
TypeScript
116 lines
4.1 KiB
TypeScript
"use client";
|
||
|
||
import { Input } from "@/components/ui/input";
|
||
import { Label } from "@/components/ui/label";
|
||
import { Textarea } from "@/components/ui/textarea";
|
||
import {
|
||
CTA_TYPE_OPTIONS,
|
||
parseProductContext,
|
||
serializeProductContext,
|
||
type CtaType,
|
||
type ProductContextFields,
|
||
} from "@/lib/types/product-context";
|
||
|
||
interface ProductContextFormProps {
|
||
value: string;
|
||
onChange: (serialized: string) => void;
|
||
compact?: boolean;
|
||
/** 產品層級表單:隱藏品牌欄位(品牌由上層品牌卡管理) */
|
||
productOnly?: boolean;
|
||
}
|
||
|
||
export function ProductContextForm({
|
||
value,
|
||
onChange,
|
||
compact = false,
|
||
productOnly = false,
|
||
}: ProductContextFormProps) {
|
||
const fields = parseProductContext(value);
|
||
|
||
function update(patch: Partial<ProductContextFields>) {
|
||
onChange(serializeProductContext({ ...fields, ...patch }));
|
||
}
|
||
|
||
return (
|
||
<div className={compact ? "space-y-3" : "space-y-4"}>
|
||
{!productOnly ? (
|
||
<div className="grid gap-3 sm:grid-cols-2">
|
||
<div className="space-y-1.5">
|
||
<Label className="text-xs">品牌名稱</Label>
|
||
<Input
|
||
value={fields.brand}
|
||
onChange={(e) => update({ brand: e.target.value })}
|
||
placeholder="例:kahu"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1.5">
|
||
<Label className="text-xs">產品名稱</Label>
|
||
<Input
|
||
value={fields.product}
|
||
onChange={(e) => update({ product: e.target.value })}
|
||
placeholder="例:寵物洗毛精"
|
||
/>
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<div className="space-y-1.5">
|
||
<Label className="text-xs">產品名稱(選填,留空則用上方顯示名稱)</Label>
|
||
<Input
|
||
value={fields.product}
|
||
onChange={(e) => update({ product: e.target.value })}
|
||
placeholder="例:溫和洗毛精"
|
||
/>
|
||
</div>
|
||
)}
|
||
<div className="space-y-1.5">
|
||
<Label className="text-xs">產品特色/能解決什麼</Label>
|
||
<Textarea
|
||
value={fields.features}
|
||
onChange={(e) => update({ features: e.target.value })}
|
||
rows={compact ? 2 : 3}
|
||
placeholder="例:溫和無香料、適合皮膚敏感的狗,在家洗澡好沖洗不殘留"
|
||
/>
|
||
</div>
|
||
<div className="space-y-1.5">
|
||
<Label className="text-xs">置入語氣(選填)</Label>
|
||
<Textarea
|
||
value={fields.placementTone ?? ""}
|
||
onChange={(e) => update({ placementTone: e.target.value })}
|
||
rows={2}
|
||
placeholder="例:像朋友分享經驗,先回應對方困擾再順帶提到;不要第一句就講品牌"
|
||
/>
|
||
</div>
|
||
<div className="grid gap-3 sm:grid-cols-2">
|
||
<div className="space-y-1.5">
|
||
<Label className="text-xs">留言 CTA 類型(選填)</Label>
|
||
<select
|
||
value={(fields.ctaType ?? "none") as CtaType}
|
||
onChange={(e) => update({ ctaType: e.target.value as CtaType })}
|
||
className="flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||
>
|
||
{CTA_TYPE_OPTIONS.map((opt) => (
|
||
<option key={opt.value} value={opt.value}>
|
||
{opt.label}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
{fields.ctaType === "link" && (
|
||
<div className="space-y-1.5">
|
||
<Label className="text-xs">CTA 連結網址(選填)</Label>
|
||
<Input
|
||
value={fields.ctaUrl ?? ""}
|
||
onChange={(e) => update({ ctaUrl: e.target.value })}
|
||
placeholder="https://… 有填才會自然帶進留言,沒填就不帶"
|
||
/>
|
||
</div>
|
||
)}
|
||
</div>
|
||
{fields.ctaType === "link" && (
|
||
<p className="text-[11px] text-muted-foreground">
|
||
有填網址時,AI 只會在其中一則草稿、像順手分享般帶上連結;留空則完全不放連結。
|
||
</p>
|
||
)}
|
||
</div>
|
||
);
|
||
} |