This commit is contained in:
Your Name committed 2026-09-28 07:45:46 +07:00
1 parent a39f5b8102
commit 6d3b13d360
5 files changed
+502 -64

No files matched your search

@@ -1,4 +1,4 @@
import { UploadCloud, Image as ImageIcon } from 'lucide-react';
import { UploadCloud, Image as ImageIcon, Monitor, Smartphone, RotateCcw } from 'lucide-react';
import { type PageSection } from '@homaroma/types';
import FocalPointPicker from './FocalPointPicker';
@@ -12,6 +12,18 @@ interface HeroFormProps {
setIsMediaModalOpen: (open: boolean) => void;
}
const GRID_POSITIONS = [
{ id: 'top-left', label: 'Top Left (บนซ้าย)', short: 'TL', icon: '↖' },
{ id: 'top-center', label: 'Top Center (บนกลาง)', short: 'TC', icon: '↑' },
{ id: 'top-right', label: 'Top Right (บนขวา)', short: 'TR', icon: '↗' },
{ id: 'center-left', label: 'Center Left (กลางซ้าย)', short: 'CL', icon: '←' },
{ id: 'center', label: 'Center (กึ่งกลาง)', short: 'C', icon: '•' },
{ id: 'center-right', label: 'Center Right (กลางขวา)', short: 'CR', icon: '→' },
{ id: 'bottom-left', label: 'Bottom Left (ล่างซ้าย)', short: 'BL', icon: '↙' },
{ id: 'bottom-center', label: 'Bottom Center (ล่างกลาง)', short: 'BC', icon: '↓' },
{ id: 'bottom-right', label: 'Bottom Right (ล่างขวา)', short: 'BR', icon: '↘' },
];
export default function HeroForm({
editingSection,
getLocalizedValue,
@@ -59,35 +71,35 @@ export default function HeroForm({
)}
<div className="space-y-2">
<label className="text-[10px] uppercase font-bold tracking-widest text-slate-400">Tagline</label>
<label className="text-[10px] uppercase font-bold tracking-widest text-slate-400">Tagline (เว้นว่างได้)</label>
<input
type="text"
required
value={getLocalizedValue(editingSection.payload, 'tagline')}
onChange={(e) => updateLocalizedValue('tagline', e.target.value)}
placeholder="เว้นว่างได้หากไม่ต้องการแสดง"
className="admin-input w-full"
/>
</div>
<div className="space-y-2">
<label className="text-[10px] uppercase font-bold tracking-widest text-slate-400">Title</label>
<label className="text-[10px] uppercase font-bold tracking-widest text-slate-400">Title (เว้นว่างได้)</label>
<input
type="text"
required
value={getLocalizedValue(editingSection.payload, 'title')}
onChange={(e) => updateLocalizedValue('title', e.target.value)}
placeholder="เว้นว่างได้หากไม่ต้องการแสดง"
className="admin-input w-full"
/>
<p className="text-[9px] text-slate-500 font-medium">Use [brackets] for golden accent serif text (e.g. Inhale [Serenity]).</p>
</div>
<div className="md:col-span-2 space-y-2">
<label className="text-[10px] uppercase font-bold tracking-widest text-slate-400">Description</label>
<label className="text-[10px] uppercase font-bold tracking-widest text-slate-400">Description (เว้นว่างได้)</label>
<textarea
rows={3}
required
value={getLocalizedValue(editingSection.payload, 'description')}
onChange={(e) => updateLocalizedValue('description', e.target.value)}
placeholder="เว้นว่างได้หากไม่ต้องการแสดง"
className="admin-input w-full resize-none"
/>
</div>
@@ -96,9 +108,9 @@ export default function HeroForm({
<label className="text-[10px] uppercase font-bold tracking-widest text-slate-400">Button Text</label>
<input
type="text"
required
value={getLocalizedValue(editingSection.payload, 'buttonText')}
onChange={(e) => updateLocalizedValue('buttonText', e.target.value)}
placeholder="เช่น Explore Collection"
className="admin-input w-full"
/>
</div>
@@ -107,12 +119,125 @@ export default function HeroForm({
<label className="text-[10px] uppercase font-bold tracking-widest text-slate-400">Button Link</label>
<input
type="text"
required
value={editingSection.payload?.buttonLink || ''}
onChange={(e) => updatePayloadValue('buttonLink', e.target.value)}
placeholder="เช่น /shop"
className="admin-input w-full"
/>
</div>
{/* 9-Box Grid Button Position Selector (Desktop & Mobile) */}
<div className="md:col-span-2 space-y-4 pt-2">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2 border-b border-white/5 pb-2">
<div>
<label className="text-[11px] uppercase font-bold tracking-widest text-slate-300">
ตำแหน่งปุ่ม & เนื้อหา (Button & Content Position)
</label>
<p className="text-[11px] text-slate-400 mt-0.5">
สามารถแยกตำแหน่งระหว่าง <strong>Desktop (PC)</strong> และ <strong>Mobile (มือถือ)</strong> ได้อิสระ
</p>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{/* Desktop (PC) */}
<div className="bg-night-950/40 border border-white/5 p-4 rounded-2xl space-y-3">
<div className="flex items-center justify-between">
<span className="flex items-center gap-2 text-xs font-bold text-white uppercase tracking-wider">
<Monitor className="w-4 h-4 text-cyan-glow" />
Desktop (PC)
</span>
<span className="px-2 py-0.5 rounded-md bg-cyan-glow/10 text-cyan-glow font-mono font-bold text-[10px] border border-cyan-glow/20">
{editingSection.payload?.buttonPosition || 'center'}
</span>
</div>
<div className="flex items-center gap-4">
<div className="grid grid-cols-3 gap-1.5 p-1.5 bg-night-900 border border-white/10 rounded-xl w-32 h-32 shrink-0 shadow-inner">
{GRID_POSITIONS.map((pos) => {
const isSelected = (editingSection.payload?.buttonPosition || 'center') === pos.id;
return (
<button
key={pos.id}
type="button"
onClick={() => updatePayloadValue('buttonPosition', pos.id)}
className={`flex flex-col items-center justify-center rounded-lg transition-all cursor-pointer ${
isSelected
? 'bg-cyan-glow text-night-950 font-black shadow-md shadow-cyan-glow/20 scale-105 ring-1 ring-white/30'
: 'bg-white/5 hover:bg-white/10 text-slate-400 hover:text-white'
}`}
title={`Desktop: ${pos.label}`}
>
<span className="text-xs leading-none">{pos.icon}</span>
<span className="text-[7px] font-mono mt-0.5">{pos.short}</span>
</button>
);
})}
</div>
<p className="text-[11px] text-slate-400 leading-relaxed">
ตำแหน่งที่จะแสดงเมื่อเปิดบนหน้าจอคอมพิวเตอร์ / จอกว้าง (ขนาด &ge; 768px)
</p>
</div>
</div>
{/* Mobile (Smartphone) */}
<div className="bg-night-950/40 border border-white/5 p-4 rounded-2xl space-y-3">
<div className="flex items-center justify-between">
<span className="flex items-center gap-2 text-xs font-bold text-white uppercase tracking-wider">
<Smartphone className="w-4 h-4 text-pop-pink" />
Mobile (มือถือ)
</span>
<div className="flex items-center gap-1.5">
{editingSection.payload?.mobileButtonPosition && (
<button
type="button"
onClick={() => updatePayloadValue('mobileButtonPosition', undefined)}
className="flex items-center gap-1 text-[10px] text-slate-400 hover:text-white px-2 py-0.5 rounded bg-white/5 hover:bg-white/10 border border-white/10 transition-colors"
title="รีเซ็ตให้เหมือน Desktop"
>
<RotateCcw className="w-3 h-3" />
ตาม PC
</button>
)}
<span className="px-2 py-0.5 rounded-md bg-pop-pink/10 text-pop-pink font-mono font-bold text-[10px] border border-pop-pink/20">
{editingSection.payload?.mobileButtonPosition || `${editingSection.payload?.buttonPosition || 'center'} (ตาม PC)`}
</span>
</div>
</div>
<div className="flex items-center gap-4">
<div className="grid grid-cols-3 gap-1.5 p-1.5 bg-night-900 border border-white/10 rounded-xl w-32 h-32 shrink-0 shadow-inner">
{GRID_POSITIONS.map((pos) => {
const currentMobilePos = editingSection.payload?.mobileButtonPosition || editingSection.payload?.buttonPosition || 'center';
const isSelected = currentMobilePos === pos.id;
const isExplicit = editingSection.payload?.mobileButtonPosition === pos.id;
return (
<button
key={pos.id}
type="button"
onClick={() => updatePayloadValue('mobileButtonPosition', pos.id)}
className={`flex flex-col items-center justify-center rounded-lg transition-all cursor-pointer ${
isSelected
? isExplicit
? 'bg-pop-pink text-white font-black shadow-md shadow-pop-pink/20 scale-105 ring-1 ring-white/30'
: 'bg-pop-pink/40 text-white font-bold ring-1 ring-pop-pink/40'
: 'bg-white/5 hover:bg-white/10 text-slate-400 hover:text-white'
}`}
title={`Mobile: ${pos.label}`}
>
<span className="text-xs leading-none">{pos.icon}</span>
<span className="text-[7px] font-mono mt-0.5">{pos.short}</span>
</button>
);
})}
</div>
<p className="text-[11px] text-slate-400 leading-relaxed">
ตำแหน่งที่จะแสดงเมื่อเปิดบนโทรศัพท์มือถือ (หากไม่เลือก จะใช้ตาม Desktop อัตโนมัติ)
</p>
</div>
</div>
</div>
</div>
</div>
);
}