x
This commit is contained in:
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">
|
||||
ตำแหน่งที่จะแสดงเมื่อเปิดบนหน้าจอคอมพิวเตอร์ / จอกว้าง (ขนาด ≥ 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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user