"use client"; import { X } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { AdminField, AdminSettingsActions, AdminSettingsGroup, AdminSettingsRow } from "@/components/admin"; import { apiUpdateSiteSettings, type PublicSettings } from "@/lib/api"; import { BRAND_LIMITS, runeCount, siteDocumentTitle } from "@/lib/brand"; import { toast } from "@/lib/toast"; export default function BasicIdentityPanel({ initial, onSaved, onDirtyChange, }: { initial: PublicSettings; onSaved?: (next: PublicSettings) => void; onDirtyChange?: (dirty: boolean) => void; }) { const [name, setName] = useState(initial.site_name); const [wordmark, setWordmark] = useState(initial.site_wordmark); const [slogan, setSlogan] = useState(initial.site_slogan); const [desc, setDesc] = useState(initial.site_description); const [keywords, setKeywords] = useState(initial.site_keywords); const [kwInput, setKwInput] = useState(""); const [saved, setSaved] = useState({ name: initial.site_name, wordmark: initial.site_wordmark, slogan: initial.site_slogan, desc: initial.site_description, keywords: initial.site_keywords, }); const [saving, setSaving] = useState(false); const [nameError, setNameError] = useState(""); const [saveError, setSaveError] = useState(""); const saveGen = useRef(0); const dirty = name.trim() !== saved.name || wordmark.trim() !== saved.wordmark || slogan.trim() !== saved.slogan || desc.trim() !== saved.desc || JSON.stringify(keywords) !== JSON.stringify(saved.keywords); useEffect(() => { onDirtyChange?.(dirty); }, [dirty, onDirtyChange]); const preview = siteDocumentTitle(name, slogan); const addKeyword = (raw: string) => { const parts = raw.split(/[,,;;]/).map((s) => s.trim()).filter(Boolean); if (parts.length === 0) return; setSaveError(""); setKeywords((prev) => { const next = [...prev]; const seen = new Set(next.map((k) => k.toLowerCase())); for (const k of parts) { if (runeCount(k) > BRAND_LIMITS.keywordRunes) { toast(`关键词「${k}」超过 ${BRAND_LIMITS.keywordRunes} 字`); continue; } if (seen.has(k.toLowerCase())) continue; if (next.length >= BRAND_LIMITS.keywordCount) { toast(`最多 ${BRAND_LIMITS.keywordCount} 个关键词`); break; } seen.add(k.toLowerCase()); next.push(k); } return next; }); setKwInput(""); }; const discard = () => { setName(saved.name); setWordmark(saved.wordmark); setSlogan(saved.slogan); setDesc(saved.desc); setKeywords([...saved.keywords]); setKwInput(""); setNameError(""); setSaveError(""); }; const save = async () => { if (saving) return; const snap = { name: name.trim(), wordmark: wordmark.trim(), slogan: slogan.trim(), desc: desc.trim(), keywords: [...keywords], }; if (!snap.name) { setNameError("显示名称不能为空"); toast("显示名称不能为空"); return; } setNameError(""); setSaveError(""); const ticket = ++saveGen.current; setSaving(true); try { const res = await apiUpdateSiteSettings({ site_name: snap.name, site_wordmark: snap.wordmark, site_slogan: snap.slogan, site_description: snap.desc, site_keywords: snap.keywords, }); if (ticket !== saveGen.current) return; setSaved({ name: res.site_name, wordmark: res.site_wordmark, slogan: res.site_slogan, desc: res.site_description, keywords: res.site_keywords, }); setName((v) => (v.trim() === snap.name ? res.site_name : v)); setWordmark((v) => (v.trim() === snap.wordmark ? res.site_wordmark : v)); setSlogan((v) => (v.trim() === snap.slogan ? res.site_slogan : v)); setDesc((v) => (v.trim() === snap.desc ? res.site_description : v)); setKeywords((prev) => (JSON.stringify(prev) === JSON.stringify(snap.keywords) ? res.site_keywords : prev)); onSaved?.(res); toast("基础资料已保存", "ok"); } catch (e) { if (ticket !== saveGen.current) return; const msg = e instanceof Error ? e.message : "保存失败"; setSaveError(msg); toast(msg); } finally { if (ticket === saveGen.current) setSaving(false); } }; return ( void save()} onDiscard={discard} /> } > { setName(v); if (nameError) setNameError(""); if (saveError) setSaveError(""); }} placeholder="姜十三论坛" error={nameError} /> { setWordmark(v); if (saveError) setSaveError(""); }} placeholder="姜十三" /> 出现在浏览器标题和页眉副标题。预览: {preview} } > { setSlogan(v); if (saveError) setSaveError(""); }} placeholder="技术分享与讨论" /> { setDesc(v); if (saveError) setSaveError(""); }} placeholder="一句话介绍社区定位…" />
{keywords.map((k) => ( ))} {keywords.length === 0 ? 尚未添加 : null}
setKwInput(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter" || e.key === "," || e.key === ",") { e.preventDefault(); addKeyword(kwInput); } }} placeholder="输入后回车添加" className="admin-field-input" aria-label="添加关键词" />
); }