x
This commit is contained in:
1 parent
683f54fd95
commit
44a613f8ec
12 files changed
+1227
-9
No files matched your search
@@ -18,6 +18,7 @@ import ProtectedRoute from './components/ProtectedRoute'
|
||||
import OrdersPage from './pages/OrdersPage'
|
||||
import OrderDetailPage from './pages/OrderDetailPage'
|
||||
import StoreSettingsPage from './pages/StoreSettingsPage'
|
||||
import BrandIntelligencePage from './pages/BrandIntelligencePage'
|
||||
import PaymentSettingsPage from './pages/PaymentSettingsPage'
|
||||
import ThemeSettingsPage from './pages/ThemeSettingsPage'
|
||||
import ContentSettingsPage from './pages/ContentSettingsPage'
|
||||
@@ -113,6 +114,8 @@ function App() {
|
||||
<Route path="/coupons" element={<CouponsPage />} />
|
||||
<Route path="/raw-materials" element={<RawMaterialsPage />} />
|
||||
<Route path="/settings" element={<StoreSettingsPage />} />
|
||||
<Route path="/settings/brand-intelligence" element={<BrandIntelligencePage />} />
|
||||
<Route path="/brand-intelligence" element={<BrandIntelligencePage />} />
|
||||
<Route path="/settings/payments" element={<PaymentSettingsPage />} />
|
||||
<Route path="/settings/theme" element={<ThemeSettingsPage />} />
|
||||
<Route path="/settings/content" element={<ContentSettingsPage />} />
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
FolderArchive,
|
||||
Map,
|
||||
Target,
|
||||
BrainCircuit,
|
||||
} from 'lucide-react'
|
||||
import { useStoreSettings } from '../contexts/StoreSettingsContext'
|
||||
import { clearAdminAuth } from '../utils/auth'
|
||||
@@ -140,6 +141,7 @@ export default function Sidebar({ isCollapsed, onToggle }: SidebarProps) {
|
||||
|
||||
const CMS_MENU = [
|
||||
{ icon: Layers, label: t('nav.pageBuilder', 'Page Builder (CMS)'), to: '/page-builder' },
|
||||
{ icon: BrainCircuit, label: t('nav.brandIntelligence', 'Brand AI Intelligence'), to: '/brand-intelligence' },
|
||||
{ icon: Sparkles, label: t('nav.cmsBlockShowcase', 'CMS Block Showcase'), to: '/cms-mockup' },
|
||||
{ icon: ImageIcon, label: t('nav.mediaLibrary', 'Media Library'), to: '/media' },
|
||||
{ icon: BookOpen, label: t('nav.scentGuide', 'Scent Guide'), to: '/articles' },
|
||||
@@ -169,11 +171,12 @@ export default function Sidebar({ isCollapsed, onToggle }: SidebarProps) {
|
||||
]
|
||||
|
||||
const SETTINGS_MENU = [
|
||||
{ icon: Store, label: t('nav.storeSettings', 'Store Settings'), to: '/settings' },
|
||||
{ icon: CreditCard, label: t('nav.paymentSettings', 'Payment & Shipping'), to: '/settings/payments' },
|
||||
{ icon: Palette, label: t('nav.themeSettings', 'Theme & Design'), to: '/settings/theme' },
|
||||
{ icon: FileText, label: t('nav.contentSettings', 'Content'), to: '/settings/content' },
|
||||
{ icon: Wrench, label: t('nav.systemMaintenance', 'System'), to: '/settings/system' },
|
||||
{ icon: Store, label: t('nav.storeSettings', 'Store Settings'), to: '/settings' },
|
||||
{ icon: BrainCircuit, label: t('nav.brandIntelligence', 'Brand AI Intelligence'), to: '/settings/brand-intelligence' },
|
||||
{ icon: CreditCard, label: t('nav.paymentSettings', 'Payment & Shipping'), to: '/settings/payments' },
|
||||
{ icon: Palette, label: t('nav.themeSettings', 'Theme & Design'), to: '/settings/theme' },
|
||||
{ icon: FileText, label: t('nav.contentSettings', 'Content'), to: '/settings/content' },
|
||||
{ icon: Wrench, label: t('nav.systemMaintenance', 'System'), to: '/settings/system' },
|
||||
]
|
||||
|
||||
const handleLogout = () => {
|
||||
|
||||
@@ -0,0 +1,616 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { API_BASE, type BrandKnowledge } from '@homaroma/types'
|
||||
import {
|
||||
BrainCircuit,
|
||||
Save,
|
||||
Loader2,
|
||||
Copy,
|
||||
Check,
|
||||
Wand2,
|
||||
Sparkles,
|
||||
CheckCircle2,
|
||||
Palette,
|
||||
Users,
|
||||
Feather,
|
||||
PackageCheck,
|
||||
Send,
|
||||
Code2,
|
||||
Download
|
||||
} from 'lucide-react'
|
||||
import { getAdminToken } from '../utils/auth'
|
||||
|
||||
type ActiveTab = 'identity' | 'visual' | 'products' | 'marketing' | 'compiled'
|
||||
|
||||
export default function BrandIntelligencePage() {
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [saveSuccess, setSaveSuccess] = useState(false)
|
||||
const [copiedType, setCopiedType] = useState<'md' | 'json' | null>(null)
|
||||
const [activeTab, setActiveTab] = useState<ActiveTab>('identity')
|
||||
|
||||
// Form State
|
||||
const [brandName, setBrandName] = useState('AKÉRIA')
|
||||
const [tagline, setTagline] = useState('')
|
||||
const [story, setStory] = useState('')
|
||||
const [targetAudience, setTargetAudience] = useState('')
|
||||
const [toneOfVoice, setToneOfVoice] = useState('')
|
||||
const [visualArtPrompt, setVisualArtPrompt] = useState('')
|
||||
const [productUsps, setProductUsps] = useState('')
|
||||
const [marketingGuidelines, setMarketingGuidelines] = useState('')
|
||||
const [customRules, setCustomRules] = useState('')
|
||||
const [masterPromptOverride, setMasterPromptOverride] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
fetch(`${API_BASE}/brand-knowledge`)
|
||||
.then((r) => r.json())
|
||||
.then((data: BrandKnowledge) => {
|
||||
setBrandName(data.brandName || 'AKÉRIA')
|
||||
setTagline(data.tagline || '')
|
||||
setStory(data.story || '')
|
||||
setTargetAudience(data.targetAudience || '')
|
||||
setToneOfVoice(data.toneOfVoice || '')
|
||||
setVisualArtPrompt(data.visualArtPrompt || '')
|
||||
setProductUsps(data.productUsps || '')
|
||||
setMarketingGuidelines(data.marketingGuidelines || '')
|
||||
setCustomRules(data.customRules || '')
|
||||
setMasterPromptOverride(data.masterPromptOverride || '')
|
||||
})
|
||||
.catch(console.error)
|
||||
.finally(() => setLoading(false))
|
||||
}, [])
|
||||
|
||||
// Dynamically compile preview on client
|
||||
const liveCompiledPrompt = masterPromptOverride.trim()
|
||||
? masterPromptOverride.trim()
|
||||
: [
|
||||
`# BRAND KNOWLEDGE DIRECTIVE: ${brandName || 'AKÉRIA'}`,
|
||||
tagline ? `**Tagline:** ${tagline}` : null,
|
||||
story ? `## 1. BRAND STORY & CORE PHILOSOPHY\n${story.trim()}` : null,
|
||||
targetAudience ? `## 2. TARGET AUDIENCE & PERSONA\n${targetAudience.trim()}` : null,
|
||||
toneOfVoice ? `## 3. TONE OF VOICE & MESSAGING STYLE\n${toneOfVoice.trim()}` : null,
|
||||
visualArtPrompt ? `## 4. VISUAL ART DIRECTION & PHOTOGRAPHY DIRECTIVE\n${visualArtPrompt.trim()}` : null,
|
||||
productUsps ? `## 5. PRODUCTS, SIGNATURE NOTES & USPs\n${productUsps.trim()}` : null,
|
||||
marketingGuidelines ? `## 6. MARKETING CHANNELS & CONTENT GUIDELINES\n${marketingGuidelines.trim()}` : null,
|
||||
customRules ? `## 7. STRICT BRAND COMPLIANCE & RULES\n${customRules.trim()}` : null,
|
||||
`---\n*This document is the single source of truth for all LLM, AI Image, and Copywriting tasks representing ${brandName || 'AKÉRIA'}.*`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join('\n\n')
|
||||
|
||||
const handleCopyMarkdown = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(liveCompiledPrompt)
|
||||
setCopiedType('md')
|
||||
setTimeout(() => setCopiedType(null), 2500)
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
const handleCopyJson = async () => {
|
||||
try {
|
||||
const payload = {
|
||||
brandName,
|
||||
tagline,
|
||||
story,
|
||||
targetAudience,
|
||||
toneOfVoice,
|
||||
visualArtPrompt,
|
||||
productUsps,
|
||||
marketingGuidelines,
|
||||
customRules,
|
||||
compiledPrompt: liveCompiledPrompt,
|
||||
}
|
||||
await navigator.clipboard.writeText(JSON.stringify(payload, null, 2))
|
||||
setCopiedType('json')
|
||||
setTimeout(() => setCopiedType(null), 2500)
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
const handleDownloadMarkdown = () => {
|
||||
const blob = new Blob([liveCompiledPrompt], { type: 'text/markdown;charset=utf-8' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = `${(brandName || 'brand').toLowerCase().replace(/\s+/g, '_')}_ai_knowledge.md`
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
const handleSave = async (e?: React.FormEvent) => {
|
||||
if (e) e.preventDefault()
|
||||
setSaving(true)
|
||||
setSaveSuccess(false)
|
||||
|
||||
try {
|
||||
const payload = {
|
||||
brandName,
|
||||
tagline: tagline || null,
|
||||
story: story || null,
|
||||
targetAudience: targetAudience || null,
|
||||
toneOfVoice: toneOfVoice || null,
|
||||
visualArtPrompt: visualArtPrompt || null,
|
||||
productUsps: productUsps || null,
|
||||
marketingGuidelines: marketingGuidelines || null,
|
||||
customRules: customRules || null,
|
||||
masterPromptOverride: masterPromptOverride || null,
|
||||
}
|
||||
|
||||
const token = getAdminToken()
|
||||
const res = await fetch(`${API_BASE}/brand-knowledge`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
const data = await res.json()
|
||||
if (res.ok && data.success) {
|
||||
setSaveSuccess(true)
|
||||
setTimeout(() => setSaveSuccess(false), 3500)
|
||||
} else {
|
||||
alert(data.error || 'Failed to save brand knowledge')
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Save brand knowledge error:', err)
|
||||
alert('Failed to save brand knowledge')
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center p-20">
|
||||
<Loader2 className="w-8 h-8 text-purple-400 animate-spin" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-5xl space-y-8 animate-fade-in text-slate-100 pb-20">
|
||||
{/* Page Header */}
|
||||
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-3.5">
|
||||
<div className="p-3 bg-purple-500/10 border border-purple-500/20 rounded-2xl shadow-lg shadow-purple-950/30">
|
||||
<BrainCircuit className="w-7 h-7 text-purple-400" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h1 className="text-3xl font-extrabold tracking-tight text-white">Brand AI Intelligence</h1>
|
||||
<span className="px-2.5 py-0.5 rounded-full bg-purple-500/20 text-purple-300 text-[10px] font-extrabold uppercase tracking-wider border border-purple-500/30">
|
||||
LLM Knowledge Base
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-slate-400 text-xs sm:text-sm mt-0.5">
|
||||
ศูนย์รวมองค์ความรู้ ตัวตน กลิ่น สินค้า และ Art Direction เพื่อส่งต่อให้ AI & Copywriters ทุกระบบ
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Global Action Buttons */}
|
||||
<div className="flex flex-wrap items-center gap-2.5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopyMarkdown}
|
||||
className={`px-3.5 py-2 rounded-xl text-xs font-bold flex items-center gap-1.5 transition-all border cursor-pointer ${
|
||||
copiedType === 'md'
|
||||
? 'bg-emerald-500/20 border-emerald-500/40 text-emerald-400'
|
||||
: 'bg-white/5 hover:bg-white/10 border-white/10 text-slate-300 hover:text-white'
|
||||
}`}
|
||||
title="คัดลอก Master Prompt ในรูปแบบ Markdown สำหรับป้อน ChatGPT / Claude / Gemini"
|
||||
>
|
||||
{copiedType === 'md' ? <Check className="w-3.5 h-3.5" /> : <Copy className="w-3.5 h-3.5" />}
|
||||
<span>{copiedType === 'md' ? 'Copied Markdown!' : 'Copy for LLM'}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopyJson}
|
||||
className={`px-3.5 py-2 rounded-xl text-xs font-bold flex items-center gap-1.5 transition-all border cursor-pointer ${
|
||||
copiedType === 'json'
|
||||
? 'bg-emerald-500/20 border-emerald-500/40 text-emerald-400'
|
||||
: 'bg-white/5 hover:bg-white/10 border-white/10 text-slate-300 hover:text-white'
|
||||
}`}
|
||||
title="คัดลอกในรูปแบบ JSON Schema"
|
||||
>
|
||||
{copiedType === 'json' ? <Check className="w-3.5 h-3.5" /> : <Code2 className="w-3.5 h-3.5" />}
|
||||
<span>JSON</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSave}
|
||||
disabled={saving}
|
||||
className="px-5 py-2 bg-purple-600 hover:bg-purple-500 active:scale-[0.99] disabled:opacity-50 text-white font-bold rounded-xl flex items-center gap-2 transition-all shadow-lg shadow-purple-950/40 text-xs cursor-pointer"
|
||||
>
|
||||
{saving ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Save className="w-3.5 h-3.5" />}
|
||||
<span>บันทึกความรู้แบรนด์</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{saveSuccess && (
|
||||
<div className="flex items-center gap-2 px-4 py-3 bg-emerald-500/10 border border-emerald-500/30 text-emerald-400 rounded-xl text-xs font-bold animate-fade-in shadow-lg">
|
||||
<CheckCircle2 className="w-4 h-4 shrink-0 text-emerald-400" />
|
||||
<span>บันทึกข้อมูล Brand AI Intelligence เรียบร้อยแล้ว พร้อมส่งต่อไปยัง AI System Context ทันที!</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Modern Tabs Header */}
|
||||
<div className="bg-night-900/80 p-1.5 rounded-2xl border border-white/10 flex flex-wrap gap-1.5 shadow-xl backdrop-blur-md">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTab('identity')}
|
||||
className={`flex-1 min-w-[150px] py-2.5 px-3.5 rounded-xl text-xs font-bold uppercase tracking-wider flex items-center justify-center gap-2 transition-all cursor-pointer ${
|
||||
activeTab === 'identity'
|
||||
? 'bg-purple-600 text-white shadow-lg shadow-purple-900/40'
|
||||
: 'text-slate-400 hover:text-white hover:bg-white/5'
|
||||
}`}
|
||||
>
|
||||
<Feather className="w-4 h-4" />
|
||||
<span>1. Identity & Voice</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTab('visual')}
|
||||
className={`flex-1 min-w-[150px] py-2.5 px-3.5 rounded-xl text-xs font-bold uppercase tracking-wider flex items-center justify-center gap-2 transition-all cursor-pointer ${
|
||||
activeTab === 'visual'
|
||||
? 'bg-purple-600 text-white shadow-lg shadow-purple-900/40'
|
||||
: 'text-slate-400 hover:text-white hover:bg-white/5'
|
||||
}`}
|
||||
>
|
||||
<Palette className="w-4 h-4" />
|
||||
<span>2. Art & Visuals</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTab('products')}
|
||||
className={`flex-1 min-w-[150px] py-2.5 px-3.5 rounded-xl text-xs font-bold uppercase tracking-wider flex items-center justify-center gap-2 transition-all cursor-pointer ${
|
||||
activeTab === 'products'
|
||||
? 'bg-purple-600 text-white shadow-lg shadow-purple-900/40'
|
||||
: 'text-slate-400 hover:text-white hover:bg-white/5'
|
||||
}`}
|
||||
>
|
||||
<PackageCheck className="w-4 h-4" />
|
||||
<span>3. Products & USPs</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTab('marketing')}
|
||||
className={`flex-1 min-w-[150px] py-2.5 px-3.5 rounded-xl text-xs font-bold uppercase tracking-wider flex items-center justify-center gap-2 transition-all cursor-pointer ${
|
||||
activeTab === 'marketing'
|
||||
? 'bg-purple-600 text-white shadow-lg shadow-purple-900/40'
|
||||
: 'text-slate-400 hover:text-white hover:bg-white/5'
|
||||
}`}
|
||||
>
|
||||
<Send className="w-4 h-4" />
|
||||
<span>4. Marketing & Rules</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveTab('compiled')}
|
||||
className={`flex-1 min-w-[160px] py-2.5 px-3.5 rounded-xl text-xs font-bold uppercase tracking-wider flex items-center justify-center gap-2 transition-all cursor-pointer ${
|
||||
activeTab === 'compiled'
|
||||
? 'bg-gradient-to-r from-purple-600 to-pink-600 text-white shadow-lg'
|
||||
: 'text-purple-300 hover:text-white hover:bg-purple-500/10'
|
||||
}`}
|
||||
>
|
||||
<Sparkles className="w-4 h-4 text-amber-300" />
|
||||
<span>5. Master LLM Context</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* TAB CONTENT 1: IDENTITY & VOICE */}
|
||||
{activeTab === 'identity' && (
|
||||
<div className="space-y-6 animate-fade-in">
|
||||
<div className="admin-card p-6 sm:p-8 space-y-6 border-white/10 shadow-2xl">
|
||||
<div className="flex items-center justify-between border-b border-white/10 pb-4">
|
||||
<div>
|
||||
<h3 className="text-base font-bold text-white uppercase tracking-wider flex items-center gap-2">
|
||||
<Feather className="w-4 h-4 text-purple-400" /> Brand Core Identity & Philosophy
|
||||
</h3>
|
||||
<p className="text-xs text-slate-400 mt-0.5">
|
||||
ระบุชื่อแบรนด์ สโลแกน และเรื่องราวที่มา เพื่อให้ LLM สร้างบทความหรือแคปชันที่สะท้อนคุณค่าของแบรนด์
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid sm:grid-cols-2 gap-5">
|
||||
<div className="space-y-2">
|
||||
<label className="text-[11px] uppercase font-bold tracking-widest text-slate-400">
|
||||
Brand Name (ชื่อแบรนด์)
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={brandName}
|
||||
onChange={(e) => setBrandName(e.target.value)}
|
||||
className="admin-input w-full font-bold text-white"
|
||||
placeholder="AKÉRIA"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-[11px] uppercase font-bold tracking-widest text-slate-400">
|
||||
Brand Tagline (สโลแกนประจำแบรนด์)
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={tagline}
|
||||
onChange={(e) => setTagline(e.target.value)}
|
||||
className="admin-input w-full text-slate-200"
|
||||
placeholder="Soft Asian Elegance & Mindful Beauty Sanctuary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="text-[11px] uppercase font-bold tracking-widest text-slate-400">
|
||||
Brand Story & Core Mission (เรื่องราวและพันธกิจของแบรนด์)
|
||||
</label>
|
||||
<span className="text-[10px] text-slate-500 font-mono">{story.length} chars</span>
|
||||
</div>
|
||||
<textarea
|
||||
value={story}
|
||||
onChange={(e) => setStory(e.target.value)}
|
||||
rows={6}
|
||||
className="admin-input w-full text-xs leading-relaxed p-4 bg-night-950/80 resize-y font-sans"
|
||||
placeholder="อธิบายที่มาของแบรนด์ แรงบันดาลใจ และสิ่งที่แบรนด์ต้องการมอบคุณค่าให้แก่ลูกค้า..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
{/* Target Audience */}
|
||||
<div className="admin-card p-6 space-y-4 border-white/10 shadow-xl">
|
||||
<div className="flex items-center justify-between border-b border-white/10 pb-3">
|
||||
<h4 className="text-xs font-bold text-white uppercase tracking-wider flex items-center gap-2">
|
||||
<Users className="w-4 h-4 text-purple-400" /> Target Persona & Audience
|
||||
</h4>
|
||||
<span className="text-[10px] text-slate-500 font-mono">{targetAudience.length} chars</span>
|
||||
</div>
|
||||
<p className="text-[11px] text-slate-400">
|
||||
กลุ่มลูกค้าเป้าหมาย ใครคือคนที่จะหลงรักแบรนด์นี้ (อายุ, พฤติกรรม, ความสนใจ, Pain points)
|
||||
</p>
|
||||
<textarea
|
||||
value={targetAudience}
|
||||
onChange={(e) => setTargetAudience(e.target.value)}
|
||||
rows={8}
|
||||
className="admin-input w-full text-xs leading-relaxed p-3.5 bg-night-950/80 resize-y font-sans"
|
||||
placeholder="• Persona: หญิงสาววัยทำงาน 22-35 ปี • ไลฟ์สไตล์: ชอบความมินิมอล การพักผ่อน และสกินแคร์/อโรมากลิ่นละมุน..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Tone of Voice */}
|
||||
<div className="admin-card p-6 space-y-4 border-white/10 shadow-xl">
|
||||
<div className="flex items-center justify-between border-b border-white/10 pb-3">
|
||||
<h4 className="text-xs font-bold text-white uppercase tracking-wider flex items-center gap-2">
|
||||
<Feather className="w-4 h-4 text-purple-400" /> Tone of Voice & Messaging Style
|
||||
</h4>
|
||||
<span className="text-[10px] text-slate-500 font-mono">{toneOfVoice.length} chars</span>
|
||||
</div>
|
||||
<p className="text-[11px] text-slate-400">
|
||||
น้ำเสียง ภาษาที่ใช้ คำที่ชอบ vs คำต้องห้าม (เช่น ห้ามใช้คำโอ้อวดเกินจริง, สุภาพอบอุ่น)
|
||||
</p>
|
||||
<textarea
|
||||
value={toneOfVoice}
|
||||
onChange={(e) => setToneOfVoice(e.target.value)}
|
||||
rows={8}
|
||||
className="admin-input w-full text-xs leading-relaxed p-3.5 bg-night-950/80 resize-y font-sans"
|
||||
placeholder="• บุคลิก: อบอุ่น สุภาพ เป็นมิตร พรีเมียม อ่อนโยน • คำที่ชอบ: Serenity, Luminous glow, Mindful calm • คำต้องห้าม: ถูกที่สุด, การันตี 100%, ภาษาหยาบคาย..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TAB CONTENT 2: VISUAL ART DIRECTION */}
|
||||
{activeTab === 'visual' && (
|
||||
<div className="space-y-6 animate-fade-in">
|
||||
<div className="admin-card p-6 sm:p-8 space-y-6 border-white/10 shadow-2xl">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 border-b border-white/10 pb-4">
|
||||
<div>
|
||||
<h3 className="text-base font-bold text-white uppercase tracking-wider flex items-center gap-2">
|
||||
<Palette className="w-4 h-4 text-purple-400" /> Visual Art Direction & AI Prompt Directive
|
||||
</h3>
|
||||
<p className="text-xs text-slate-400 mt-0.5">
|
||||
คำสั่ง Art Direction แบบสมบูรณ์ สำหรับสร้างภาพ Midjourney, Imagen, DALL-E หรือควบคุม Mood & Tone ภาพถ่าย
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setVisualArtPrompt(visualArtPrompt)}
|
||||
className="px-3 py-1.5 bg-purple-500/10 hover:bg-purple-500/20 border border-purple-500/30 text-purple-300 rounded-lg text-xs font-semibold flex items-center gap-1.5 transition-all cursor-pointer"
|
||||
>
|
||||
<Wand2 className="w-3.5 h-3.5" />
|
||||
<span>AKÉRIA Art Directive</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[11px] uppercase font-bold tracking-widest text-slate-400">
|
||||
Master Visual Art Prompt (Markdown / Monospace)
|
||||
</span>
|
||||
<div className="flex items-center gap-3 text-[10px] font-mono text-slate-400">
|
||||
<span>Lines: {visualArtPrompt.split('\n').length}</span>
|
||||
<span>Chars: {visualArtPrompt.length.toLocaleString()}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<textarea
|
||||
value={visualArtPrompt}
|
||||
onChange={(e) => setVisualArtPrompt(e.target.value)}
|
||||
rows={18}
|
||||
className="admin-input w-full font-mono text-xs leading-relaxed p-4 bg-night-950/90 border-white/10 focus:border-purple-500/50 rounded-xl resize-y text-slate-200 placeholder:text-slate-600 outline-none transition-all shadow-inner"
|
||||
placeholder="You are the Visual Art Director for AKÉRIA... [Color Palette, Lighting, Photography Style, Materials, Avoid List]"
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TAB CONTENT 3: PRODUCTS & USPS */}
|
||||
{activeTab === 'products' && (
|
||||
<div className="space-y-6 animate-fade-in">
|
||||
<div className="admin-card p-6 sm:p-8 space-y-6 border-white/10 shadow-2xl">
|
||||
<div className="border-b border-white/10 pb-4">
|
||||
<h3 className="text-base font-bold text-white uppercase tracking-wider flex items-center gap-2">
|
||||
<PackageCheck className="w-4 h-4 text-purple-400" /> Products, Signatures & Unique Selling Points (USPs)
|
||||
</h3>
|
||||
<p className="text-xs text-slate-400 mt-0.5">
|
||||
จุดแข็งของสินค้า สารสกัด กลิ่นเอกลักษณ์ มาตรฐานความปลอดภัย และผลลัพธ์ทางอารมณ์ที่ลูกค้าจะได้รับ
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="text-[11px] uppercase font-bold tracking-widest text-slate-400">
|
||||
Product Lines, Ingredients & Quality Standards
|
||||
</label>
|
||||
<span className="text-[10px] text-slate-500 font-mono">{productUsps.length} chars</span>
|
||||
</div>
|
||||
<textarea
|
||||
value={productUsps}
|
||||
onChange={(e) => setProductUsps(e.target.value)}
|
||||
rows={14}
|
||||
className="admin-input w-full text-xs leading-relaxed p-4 bg-night-950/80 resize-y font-sans"
|
||||
placeholder="• สินค้าหลัก: Pure Essential Oil, Reed Diffuser, Room Spray, Skincare Rituals • จุดเด่น: สารสกัดธรรมชาติ 100%, ปราศจากแอลกอฮอล์และสารเคมีอันตราย, Cruelty-free • อารมณ์ที่ได้รับ: ผ่อนคลาย จิตใจสงบ รู้สึกสดชื่นและมีพลังบวก..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TAB CONTENT 4: MARKETING & RULES */}
|
||||
{activeTab === 'marketing' && (
|
||||
<div className="space-y-6 animate-fade-in">
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
{/* Marketing Guidelines */}
|
||||
<div className="admin-card p-6 space-y-4 border-white/10 shadow-xl">
|
||||
<div className="flex items-center justify-between border-b border-white/10 pb-3">
|
||||
<h4 className="text-xs font-bold text-white uppercase tracking-wider flex items-center gap-2">
|
||||
<Send className="w-4 h-4 text-purple-400" /> Content & Marketing Channels
|
||||
</h4>
|
||||
<span className="text-[10px] text-slate-500 font-mono">{marketingGuidelines.length} chars</span>
|
||||
</div>
|
||||
<p className="text-[11px] text-slate-400">
|
||||
แนวทางเขียนคอนเทนต์เฉพาะแพลตฟอร์ม (TikTok, Instagram, Website, LINE OA, CTAs, Hashtags)
|
||||
</p>
|
||||
<textarea
|
||||
value={marketingGuidelines}
|
||||
onChange={(e) => setMarketingGuidelines(e.target.value)}
|
||||
rows={14}
|
||||
className="admin-input w-full text-xs leading-relaxed p-3.5 bg-night-950/80 resize-y font-sans"
|
||||
placeholder="• TikTok: วิดีโอสั้น ASMR ปลอบประโลมใจ, unboxing สวยงาม • Instagram: คุมโทนสีพาสเทล นุ่มนวล คลีน • CTAs: สัมผัสความผ่อนคลายวันนี้, เริ่มต้นการดูแลตัวเองของคุณ • Hashtags: #AKERIA #SoftBeauty #AromaRitual"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Custom Rules & Compliance */}
|
||||
<div className="admin-card p-6 space-y-4 border-white/10 shadow-xl">
|
||||
<div className="flex items-center justify-between border-b border-white/10 pb-3">
|
||||
<h4 className="text-xs font-bold text-white uppercase tracking-wider flex items-center gap-2">
|
||||
<Sparkles className="w-4 h-4 text-amber-400" /> Strict Rules & Compliance
|
||||
</h4>
|
||||
<span className="text-[10px] text-slate-500 font-mono">{customRules.length} chars</span>
|
||||
</div>
|
||||
<p className="text-[11px] text-slate-400">
|
||||
กฎเหล็กและข้อบังคับพิเศษที่ AI หรือทีมงานต้องปฏิบัติตามเสมอในการผลิตสื่อทุกชิ้น
|
||||
</p>
|
||||
<textarea
|
||||
value={customRules}
|
||||
onChange={(e) => setCustomRules(e.target.value)}
|
||||
rows={14}
|
||||
className="admin-input w-full text-xs leading-relaxed p-3.5 bg-night-950/80 resize-y font-sans"
|
||||
placeholder="1. รักษาความรู้สึก Soft Asian / Japanese Elegance ตลอดเวลา 2. ห้ามใช้สีฉูดฉาดแบบนีออนเด็ดขาด 3. ทุกข้อความต้องสร้างความรู้สึกผ่อนคลาย พรีเมียม และเข้าถึงง่าย"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* TAB CONTENT 5: COMPILED MASTER LLM CONTEXT */}
|
||||
{activeTab === 'compiled' && (
|
||||
<div className="space-y-6 animate-fade-in">
|
||||
<div className="admin-card p-6 sm:p-8 space-y-6 border-purple-500/20 shadow-2xl bg-night-950/90">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 border-b border-white/10 pb-4">
|
||||
<div>
|
||||
<h3 className="text-base font-bold text-white uppercase tracking-wider flex items-center gap-2">
|
||||
<Sparkles className="w-5 h-5 text-amber-400" /> Compiled Master System Prompt for LLMs
|
||||
</h3>
|
||||
<p className="text-xs text-slate-400 mt-0.5">
|
||||
นี่คือข้อความรวมที่คอมไพล์จากทุกหมวดหมู่อัตโนมัติ พร้อมนำไปวางเป็น <b>System Instructions</b> ใน ChatGPT, Claude, Gemini หรือ Agent ของคุณ
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopyMarkdown}
|
||||
className={`px-3.5 py-1.5 rounded-lg text-xs font-bold flex items-center gap-1.5 transition-all border cursor-pointer ${
|
||||
copiedType === 'md'
|
||||
? 'bg-emerald-500/20 border-emerald-500/40 text-emerald-400'
|
||||
: 'bg-purple-600 hover:bg-purple-500 text-white'
|
||||
}`}
|
||||
>
|
||||
{copiedType === 'md' ? <Check className="w-3.5 h-3.5" /> : <Copy className="w-3.5 h-3.5" />}
|
||||
<span>{copiedType === 'md' ? 'Copied Markdown!' : 'Copy Markdown'}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleDownloadMarkdown}
|
||||
className="px-3 py-1.5 bg-white/5 hover:bg-white/10 border border-white/10 text-slate-300 rounded-lg text-xs font-semibold flex items-center gap-1.5 transition-all cursor-pointer"
|
||||
>
|
||||
<Download className="w-3.5 h-3.5" />
|
||||
<span>.md</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between text-[11px] text-slate-400">
|
||||
<span className="uppercase font-bold tracking-wider">Live System Prompt Preview</span>
|
||||
<span className="font-mono">{liveCompiledPrompt.length.toLocaleString()} Chars • {liveCompiledPrompt.split('\n').length} Lines</span>
|
||||
</div>
|
||||
<pre className="p-5 rounded-xl bg-black/60 border border-white/10 text-slate-200 font-mono text-xs leading-relaxed whitespace-pre-wrap max-h-[600px] overflow-y-auto shadow-inner select-all">
|
||||
{liveCompiledPrompt}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Bottom Save Bar */}
|
||||
<div className="flex items-center justify-between pt-6 border-t border-white/10">
|
||||
<div className="flex items-center gap-2 text-xs text-slate-400">
|
||||
<Sparkles className="w-4 h-4 text-purple-400" />
|
||||
<span>ข้อมูลที่บันทึกจะซิงค์ไปยัง API และพร้อมใช้งานในทุกโมดูล AI ของระบบ HomAroma / AKÉRIA</span>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSave}
|
||||
disabled={saving}
|
||||
className="btn-primary py-3 px-8 flex items-center gap-2 text-xs font-bold shadow-lg shadow-purple-900/30 cursor-pointer"
|
||||
>
|
||||
{saving ? <Loader2 className="w-4 h-4 animate-spin" /> : <Save className="w-4 h-4" />}
|
||||
บันทึกการเปลี่ยนแปลง
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -8,11 +8,115 @@ import {
|
||||
Image as ImageIcon,
|
||||
UploadCloud,
|
||||
Sparkles,
|
||||
CheckCircle2
|
||||
CheckCircle2,
|
||||
Palette,
|
||||
Wand2,
|
||||
Copy,
|
||||
Check,
|
||||
FileText
|
||||
} from 'lucide-react';
|
||||
import { useStoreSettings } from '../contexts/StoreSettingsContext';
|
||||
import MediaLibraryModal from '../components/MediaLibraryModal';
|
||||
|
||||
export const DEFAULT_BRAND_PROMPT = `You are the Visual Art Director for AKÉRIA.
|
||||
|
||||
AKÉRIA is a modern beauty e-commerce and lifestyle brand with a soft Asian/Japanese-inspired sensibility.
|
||||
|
||||
BRAND MOOD
|
||||
Bright, feminine, clean, fresh, premium-friendly, elegant, youthful, soft, luminous.
|
||||
|
||||
COLOR PALETTE
|
||||
Primary:
|
||||
- Coral Rose #F28F96
|
||||
- Sakura Blush #F5C2C9
|
||||
- Warm Ivory #F7EFE6
|
||||
|
||||
Secondary:
|
||||
- Soft Lavender #DCCDE3
|
||||
- Soft Peach #F4D4C2
|
||||
- Pure White #FFFFFF
|
||||
|
||||
Contrast:
|
||||
- Charcoal #2D2D2D
|
||||
|
||||
COLOR DISTRIBUTION
|
||||
- 55–65% Warm Ivory / White
|
||||
- 15–20% Coral Rose / Sakura Blush
|
||||
- 10–15% Soft Peach / Lavender
|
||||
- 5–10% Charcoal for contrast
|
||||
|
||||
LIGHTING
|
||||
Soft diffused beauty lighting.
|
||||
Bright but not overexposed.
|
||||
Warm-neutral white balance.
|
||||
Luminous skin.
|
||||
Soft highlights.
|
||||
Low-to-medium contrast.
|
||||
Fresh commercial beauty campaign feel.
|
||||
|
||||
PHOTOGRAPHY STYLE
|
||||
Modern Asian beauty editorial.
|
||||
Clean skin texture.
|
||||
Natural glossy makeup.
|
||||
Soft floral details.
|
||||
Elegant product styling.
|
||||
Light airy composition.
|
||||
Premium e-commerce campaign photography.
|
||||
|
||||
MATERIALS & TEXTURES
|
||||
Frosted glass.
|
||||
Clear acrylic.
|
||||
Soft satin.
|
||||
Glossy cosmetic packaging.
|
||||
Light marble.
|
||||
Soft petals.
|
||||
Minimal reflective surfaces.
|
||||
|
||||
BACKGROUND
|
||||
Prefer:
|
||||
Warm ivory.
|
||||
Soft blush gradient.
|
||||
White.
|
||||
Very pale peach.
|
||||
Subtle lavender.
|
||||
|
||||
WARDROBE
|
||||
Prefer:
|
||||
White.
|
||||
Ivory.
|
||||
Blush.
|
||||
Soft beige.
|
||||
Dusty rose.
|
||||
Pale lavender.
|
||||
|
||||
ACCENT ELEMENTS
|
||||
Use cherry blossom, soft petals, subtle glow, fine sparkles, or clean beauty props.
|
||||
Keep these subtle and refined.
|
||||
|
||||
AVOID
|
||||
Hot pink.
|
||||
Neon colors.
|
||||
Strong red.
|
||||
Electric blue.
|
||||
Bright green.
|
||||
Strong yellow.
|
||||
Dark dramatic backgrounds.
|
||||
Heavy black.
|
||||
Cyberpunk lighting.
|
||||
Overly luxurious gold.
|
||||
Overly mature fashion styling.
|
||||
|
||||
IMAGE COMPOSITION
|
||||
Keep generous negative space for typography or website UI.
|
||||
Use clean commercial framing.
|
||||
Hero subject should feel approachable, polished, and aspirational.
|
||||
|
||||
IMPORTANT
|
||||
All backgrounds, props, flowers, clothing, packaging accents, and decorative elements must remain within the AKÉRIA color palette.
|
||||
|
||||
The final image must immediately feel like AKÉRIA:
|
||||
bright beauty commerce, soft Asian elegance, modern, friendly, and premium.`;
|
||||
|
||||
const SUPPORTED_LOCALES = [
|
||||
{ code: 'th', label: 'TH', full: 'ภาษาไทย (Default)' },
|
||||
{ code: 'en', label: 'EN', full: 'English' },
|
||||
@@ -29,11 +133,13 @@ export default function StoreSettingsPage() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [saveSuccess, setSaveSuccess] = useState(false);
|
||||
const [promptCopied, setPromptCopied] = useState(false);
|
||||
|
||||
// Store Brand & Identity
|
||||
const [brandName, setBrandName] = useState('');
|
||||
const [logoUrl, setLogoUrl] = useState('');
|
||||
const [themePreset, setThemePreset] = useState('default');
|
||||
const [brandPrompt, setBrandPrompt] = useState('');
|
||||
const [facebookUrl, setFacebookUrl] = useState('');
|
||||
const [instagramUrl, setInstagramUrl] = useState('');
|
||||
const [tiktokUrl, setTiktokUrl] = useState('');
|
||||
@@ -56,6 +162,7 @@ export default function StoreSettingsPage() {
|
||||
setBrandName(data.brandName || '');
|
||||
setLogoUrl(data.logoUrl || '');
|
||||
setThemePreset(data.themePreset || 'default');
|
||||
setBrandPrompt(data.brandPrompt || DEFAULT_BRAND_PROMPT);
|
||||
setFacebookUrl(data.facebookUrl || '');
|
||||
setInstagramUrl(data.instagramUrl || '');
|
||||
setTiktokUrl(data.tiktokUrl || '');
|
||||
@@ -68,6 +175,17 @@ export default function StoreSettingsPage() {
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const handleCopyPrompt = async () => {
|
||||
if (!brandPrompt) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(brandPrompt);
|
||||
setPromptCopied(true);
|
||||
setTimeout(() => setPromptCopied(false), 2000);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
|
||||
const getLocalizedSeoValue = (field: 'defaultSeoTitle' | 'defaultSeoDescription'): string => {
|
||||
if (activeSeoLang === 'th') {
|
||||
const thTrans = translations.find((t) => t.langCode === 'th');
|
||||
@@ -122,6 +240,7 @@ export default function StoreSettingsPage() {
|
||||
brandName,
|
||||
logoUrl: logoUrl || null,
|
||||
themePreset,
|
||||
brandPrompt: brandPrompt || null,
|
||||
facebookUrl: facebookUrl || null,
|
||||
instagramUrl: instagramUrl || null,
|
||||
tiktokUrl: tiktokUrl || null,
|
||||
@@ -278,7 +397,101 @@ export default function StoreSettingsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 2. Global Default SEO & Social Sharing */}
|
||||
{/* 2. Brand Visual Art Direction & AI Prompt Directive */}
|
||||
<div className="admin-card p-8 space-y-6">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 border-b border-white/10 pb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2.5 bg-purple-500/10 border border-purple-500/20 rounded-xl">
|
||||
<Palette className="w-5 h-5 text-purple-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-bold uppercase tracking-widest text-white flex items-center gap-2">
|
||||
Brand Visual Art Direction & AI Prompt
|
||||
<span className="px-2 py-0.5 rounded-full bg-purple-500/20 text-purple-300 text-[10px] font-bold tracking-wider uppercase border border-purple-500/30">
|
||||
Master Directive
|
||||
</span>
|
||||
</h2>
|
||||
<p className="text-xs text-slate-400">
|
||||
Master prompt and guidelines used for AI generation, photography mood, color palette, lighting, and campaign art direction.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Actions Toolbar */}
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setBrandPrompt(DEFAULT_BRAND_PROMPT)}
|
||||
className="px-3 py-1.5 bg-purple-500/10 hover:bg-purple-500/20 border border-purple-500/30 text-purple-300 rounded-lg text-xs font-semibold flex items-center gap-1.5 transition-all cursor-pointer"
|
||||
title="โหลดแม่แบบ Master Prompt สำหรับ AKÉRIA"
|
||||
>
|
||||
<Wand2 className="w-3.5 h-3.5" />
|
||||
<span>Load AKÉRIA Preset</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopyPrompt}
|
||||
disabled={!brandPrompt}
|
||||
className={`px-3 py-1.5 border rounded-lg text-xs font-semibold flex items-center gap-1.5 transition-all cursor-pointer ${
|
||||
promptCopied
|
||||
? 'bg-emerald-500/20 border-emerald-500/40 text-emerald-400'
|
||||
: 'bg-white/5 hover:bg-white/10 border-white/10 text-slate-300 hover:text-white disabled:opacity-40'
|
||||
}`}
|
||||
title="คัดลอก Prompt ไปยัง Clipboard"
|
||||
>
|
||||
{promptCopied ? <Check className="w-3.5 h-3.5" /> : <Copy className="w-3.5 h-3.5" />}
|
||||
<span>{promptCopied ? 'Copied!' : 'Copy Prompt'}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setBrandPrompt('')}
|
||||
disabled={!brandPrompt}
|
||||
className="px-2.5 py-1.5 bg-white/5 hover:bg-rose-500/10 hover:border-rose-500/30 text-slate-400 hover:text-rose-400 border border-white/10 rounded-lg text-xs font-semibold transition-all disabled:opacity-40 cursor-pointer"
|
||||
title="ล้างข้อความในกล่อง"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="relative">
|
||||
<textarea
|
||||
value={brandPrompt}
|
||||
onChange={(e) => setBrandPrompt(e.target.value)}
|
||||
rows={18}
|
||||
className="admin-input w-full font-mono text-xs leading-relaxed p-4 bg-night-950/90 border-white/10 focus:border-purple-500/50 rounded-xl resize-y text-slate-200 placeholder:text-slate-600 outline-none transition-all shadow-inner"
|
||||
placeholder="Enter Brand Visual Art Direction and Master AI Prompt here..."
|
||||
spellCheck={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Prompt Stats & Guidelines Bar */}
|
||||
<div className="flex flex-wrap items-center justify-between gap-3 text-xs text-slate-400 pt-1">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="inline-flex items-center gap-1 px-2.5 py-1 rounded-md bg-night-950/60 border border-white/5 font-mono text-[11px] text-slate-300">
|
||||
<FileText className="w-3 h-3 text-purple-400" />
|
||||
Lines: {brandPrompt ? brandPrompt.split('\n').length : 0}
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1 px-2.5 py-1 rounded-md bg-night-950/60 border border-white/5 font-mono text-[11px] text-slate-300">
|
||||
Chars: {brandPrompt ? brandPrompt.length.toLocaleString() : 0}
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-1 px-2.5 py-1 rounded-md bg-night-950/60 border border-white/5 font-mono text-[11px] text-slate-300">
|
||||
Words: {brandPrompt ? brandPrompt.trim().split(/\s+/).filter(Boolean).length : 0}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="text-[11px] text-slate-500 flex items-center gap-1.5">
|
||||
<Sparkles className="w-3.5 h-3.5 text-purple-400 shrink-0" />
|
||||
<span>คำสั่ง Art Direction นี้จะถูกนำไปใช้เป็น System Instruction สำหรับการ Generate ภาพ และคุม Mood & Tone ของแบรนด์</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 3. Global Default SEO & Social Sharing */}
|
||||
<div className="admin-card p-8 space-y-6">
|
||||
<div className="flex items-center justify-between border-b border-white/10 pb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
|
||||
Reference in new issue
Block a user