This commit is contained in:
Your Name committed 2026-06-22 19:11:43 -04:00
1 parent 50b93eb629
commit cd4b89a0fc
1 file changed
+328 -260
+328 -260
View File
@@ -57,11 +57,11 @@ export default function PageBuilderPage() {
const [categories, setCategories] = useState<Category[]>([]);
const [scents, setScents] = useState<Scent[]>([]);
const [loading, setLoading] = useState(true);
const [isModalOpen, setIsModalOpen] = useState(false);
const [editingSection, setEditingSection] = useState<Partial<PageSection> | null>(null);
const [uploadTarget, setUploadTarget] = useState<{ type: 'main' | 'slide'; index?: number } | null>(null);
const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');
const [isSavingPageSettings, setIsSavingPageSettings] = useState(false);
const [isReorderModalOpen, setIsReorderModalOpen] = useState(false);
const [isMediaModalOpen, setIsMediaModalOpen] = useState(false);
@@ -440,6 +440,7 @@ export default function PageBuilderPage() {
if (res.ok) {
setSaveStatus('saved');
setIsReorderModalOpen(false);
setTimeout(() => setSaveStatus('idle'), 3000);
} else {
setSaveStatus('error');
@@ -497,12 +498,12 @@ export default function PageBuilderPage() {
buttonLink: '/shop'
}
});
setIsModalOpen(true);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const openEditModal = (section: PageSection) => {
setEditingSection(JSON.parse(JSON.stringify(section))); // deep copy
setIsModalOpen(true);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handleTypeChange = (type: string) => {
@@ -705,7 +706,7 @@ export default function PageBuilderPage() {
});
if (res.ok) {
setIsModalOpen(false);
setEditingSection(null);
if (selectedPageId) fetchPageDetails(selectedPageId);
} else {
const errData = await res.json();
@@ -718,157 +719,33 @@ export default function PageBuilderPage() {
return (
<div className="space-y-8 animate-fade-in text-slate-100">
{/* Global Header */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6">
<div className="space-y-2">
<div className="flex items-center gap-6">
<h1 className="text-3xl font-bold">Page Builder (CMS)</h1>
<div className="h-6 w-px bg-white/10 hidden md:block"></div>
<div className="flex items-center gap-3">
<span className="text-xs text-slate-400 uppercase tracking-widest font-bold">Select Page:</span>
<select
value={selectedPageId || ''}
onChange={(e) => setSelectedPageId(Number(e.target.value))}
className="bg-slate-900 border border-white/10 text-white rounded-xl px-3 py-1.5 text-sm focus:outline-none focus:border-cyan-glow cursor-pointer"
className="bg-slate-900 border border-white/10 text-white rounded-xl px-4 py-2 text-sm font-medium focus:outline-none focus:border-cyan-glow focus:ring-1 focus:ring-cyan-glow cursor-pointer transition-all min-w-[200px]"
>
{pages.map((p) => (
<option key={p.id} value={p.id}>
{p.title} ({p.slug})
{p.title} (/{p.slug})
</option>
))}
</select>
{page && (
<>
<a
href={getPageUrl(page.slug)}
target="_blank"
rel="noreferrer"
className="inline-flex items-center gap-2 px-3 py-1.5 rounded-xl bg-cyan-glow/10 border border-cyan-glow/20 text-cyan-glow hover:bg-cyan-glow/20 text-xs font-bold uppercase tracking-wider transition-all"
title={`Open ${page.title} live preview`}
>
<ExternalLink className="w-4 h-4" />
Open Live
</a>
<button
type="button"
onClick={handleSavePageSettings}
disabled={isSavingPageSettings}
className="inline-flex items-center gap-2 px-3 py-1.5 rounded-xl bg-white text-night-900 hover:bg-slate-200 disabled:opacity-50 text-xs font-bold uppercase tracking-wider transition-all"
>
{isSavingPageSettings ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Save className="w-4 h-4" />
)}
Save Page Settings
</button>
</>
)}
<button
onClick={() => setIsPageModalOpen(true)}
className="px-3 py-1.5 bg-white/5 border border-white/5 hover:bg-white/10 text-xs font-bold rounded-xl text-slate-300 hover:text-white transition-all uppercase tracking-wider cursor-pointer"
className="px-4 py-2 bg-transparent border border-dashed border-white/20 hover:border-white/50 text-sm font-bold rounded-xl text-slate-300 hover:text-white transition-all uppercase tracking-wider cursor-pointer flex items-center gap-2"
>
+ Create Page
<Plus className="w-4 h-4" /> Create Page
</button>
{page && page.slug !== 'home' && (
<button
onClick={handleDeletePage}
className="px-3 py-1.5 bg-rose-500/10 border border-rose-500/10 hover:bg-rose-500 hover:text-white text-xs font-bold rounded-xl text-rose-500 transition-all uppercase tracking-wider cursor-pointer"
>
Delete Page
</button>
)}
</div>
{page && (
<div className="mt-4 flex items-center gap-3 rounded-2xl border border-white/10 bg-white/5 px-4 py-3">
<label className="inline-flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={page.isPublished}
onChange={(e) => setPage({ ...page, isPublished: e.target.checked })}
className="h-4 w-4 rounded border-white/20 bg-slate-900 text-cyan-glow focus:ring-cyan-glow"
/>
<span className="text-xs font-bold uppercase tracking-widest text-slate-300">
Publish to Website
</span>
</label>
<span
className={`text-[10px] font-bold uppercase tracking-widest ${
page.isPublished ? 'text-emerald-400' : 'text-amber-400'
}`}
>
{page.isPublished ? 'Published' : 'Draft'}
</span>
</div>
)}
</div>
<div className="flex gap-4">
<button
onClick={openAddModal}
className="flex items-center gap-2 px-5 py-2.5 bg-cyan-glow/10 border border-cyan-glow/30 hover:bg-cyan-glow/20 text-cyan-glow rounded-xl font-bold text-xs uppercase tracking-wider transition-all"
>
<Plus className="w-4 h-4" />
Add Section Block
</button>
{sections.length > 0 && (
<button
onClick={handleSaveOrder}
disabled={saveStatus === 'saving'}
className="flex items-center gap-2 px-5 py-2.5 bg-white text-night-900 hover:bg-slate-200 disabled:opacity-50 rounded-xl font-bold text-xs uppercase tracking-wider transition-all"
>
{saveStatus === 'saving' ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Save className="w-4 h-4" />
)}
{saveStatus === 'saved' ? 'Saved Order!' : 'Save Layout Order'}
</button>
)}
</div>
</div>
{pages.length > 0 && (
<div className="bg-night-950/40 border border-white/5 rounded-2xl p-4">
<div className="flex items-center justify-between gap-4 mb-3">
<div>
<h2 className="text-sm font-bold uppercase tracking-widest text-slate-400">Quick Page Links</h2>
<p className="text-xs text-slate-500 mt-1">Open any page directly in the storefront.</p>
</div>
</div>
<div className="flex flex-wrap gap-2">
{pages.map((p) => (
<div
key={p.id}
className={`flex items-center gap-2 px-3 py-2 rounded-xl border text-xs transition-all ${
selectedPageId === p.id
? 'bg-cyan-glow/10 border-cyan-glow/20 text-cyan-glow'
: 'bg-white/5 border-white/5 text-slate-300 hover:bg-white/10'
}`}
>
<button
type="button"
onClick={() => setSelectedPageId(p.id)}
className="font-bold"
title={`Select ${p.title}`}
>
{p.title}
</button>
<span className="text-slate-500">/ {p.slug}</span>
<a
href={getPageUrl(p.slug)}
target="_blank"
rel="noreferrer"
className="ml-1 inline-flex items-center gap-1 hover:text-white transition-colors"
title={`Open ${p.title} live`}
>
<ExternalLink className="w-3.5 h-3.5" />
</a>
</div>
))}
</div>
</div>
)}
{saveStatus === 'saved' && (
<div className="p-4 bg-cyan-glow/10 border border-cyan-glow/30 text-cyan-glow rounded-xl flex items-center gap-2">
@@ -890,149 +767,196 @@ export default function PageBuilderPage() {
))}
</div>
) : (
<div className="bg-night-950/40 border border-white/5 rounded-2xl overflow-hidden">
<div className="p-6 border-b border-white/5 bg-white/[0.01]">
<h2 className="text-lg font-bold">Homepage Blocks list</h2>
</div>
<div className="divide-y divide-white/5">
{sections.length === 0 ? (
<div className="p-16 text-center text-slate-500 space-y-4">
<Layers className="w-12 h-12 mx-auto text-slate-700" />
<p>No content sections in this page yet. Start by adding one!</p>
</div>
) : (
sections.map((section, idx) => (
<div
key={section.id}
className={`p-6 flex items-center justify-between transition-all duration-300 hover:bg-white/[0.02] ${
!section.isActive ? 'opacity-50' : ''
}`}
>
<div className="flex items-center gap-6">
{/* Index & Order controls */}
<div className="flex flex-col gap-1 items-center">
<button
onClick={() => handleMove(idx, 'up')}
disabled={idx === 0}
className="p-1 hover:bg-white/5 rounded-md disabled:opacity-20 text-slate-400 hover:text-white"
>
<ArrowUp className="w-4 h-4" />
</button>
<span className="text-xs font-mono text-slate-500 font-bold">
{idx + 1}
</span>
<button
onClick={() => handleMove(idx, 'down')}
disabled={idx === sections.length - 1}
className="p-1 hover:bg-white/5 rounded-md disabled:opacity-20 text-slate-400 hover:text-white"
>
<ArrowDown className="w-4 h-4" />
</button>
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 items-start">
<div className="lg:col-span-5 space-y-4">
{/* Page Configuration Card */}
{page && (
<div className="bg-night-900/50 border border-white/5 rounded-2xl p-6 shadow-lg backdrop-blur-sm">
<div className="flex flex-col gap-6">
<div className="space-y-4">
<div className="flex flex-col gap-2">
<label className="text-[10px] font-bold uppercase tracking-widest text-slate-400">
Page Title
</label>
<input
type="text"
value={page.title || ''}
onChange={(e) => setPage({ ...page, title: e.target.value })}
className="bg-slate-900/50 border border-white/10 text-white rounded-xl px-4 py-2 text-sm focus:outline-none focus:border-cyan-glow/50 transition-colors w-full"
placeholder="Enter page title..."
/>
</div>
{/* Section Description */}
<div>
<div className="flex items-center gap-3">
<span className="text-xs font-bold font-mono px-2 py-0.5 bg-slate-800 rounded border border-white/5 uppercase">
{section.type}
<div className="flex items-center gap-4">
<label className="inline-flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={page.isPublished}
onChange={(e) => setPage({ ...page, isPublished: e.target.checked })}
className="h-4 w-4 rounded border-white/20 bg-slate-900 text-cyan-glow focus:ring-cyan-glow focus:ring-offset-slate-900"
/>
<span className="text-sm font-bold text-slate-200">
Publish to Website
</span>
{!section.isActive && (
<span className="text-[10px] uppercase font-bold tracking-widest text-slate-500">
Hidden
</span>
)}
</div>
<h3 className="text-base font-bold mt-1">
{getAdminDisplayString(
section.payload?.title
|| section.payload?.tagline
|| section.payload?.quote
|| (section.type?.toUpperCase() === 'ANNOUNCEMENT_BAR' ? section.payload?.slides?.[0] : undefined)
) || `Block #${section.id}`}
</h3>
{section.type?.toUpperCase() === 'PRODUCTS' && (
<p className="text-xs text-slate-400 mt-0.5">
Type: {section.payload?.type} • Limit: {section.payload?.limit || 3} products
</p>
)}
{section.type?.toUpperCase() === 'ANNOUNCEMENT_BAR' && (
<p className="text-xs text-slate-400 mt-0.5">
Slides: {section.payload?.slides?.length || 0} messages
</p>
)}
</label>
<span
className={`text-[10px] font-bold uppercase tracking-widest px-2 py-1 rounded-md ${
page.isPublished ? 'bg-emerald-500/10 text-emerald-400' : 'bg-amber-500/10 text-amber-400'
}`}
>
{page.isPublished ? 'Published' : 'Draft'}
</span>
</div>
</div>
{/* Actions */}
<div className="flex items-center gap-3">
<div className="flex flex-col sm:flex-row items-center gap-3 w-full">
<button
onClick={() => handleToggleActive(section)}
className={`p-2.5 rounded-xl border transition-all ${
section.isActive
? 'bg-cyan-glow/10 border-cyan-glow/20 text-cyan-glow hover:bg-cyan-glow/20'
: 'bg-white/5 border-white/5 text-slate-400 hover:bg-white/10 hover:text-white'
}`}
title={section.isActive ? 'Hide Section' : 'Show Section'}
type="button"
onClick={handleSavePageSettings}
disabled={isSavingPageSettings}
className="inline-flex flex-1 justify-center items-center gap-2 px-5 py-2 rounded-xl bg-white text-night-900 hover:bg-slate-200 disabled:opacity-50 text-xs font-bold uppercase tracking-wider transition-all shadow-md w-full sm:w-auto"
>
{section.isActive ? <Eye className="w-4 h-4" /> : <EyeOff className="w-4 h-4" />}
{isSavingPageSettings ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Save className="w-4 h-4" />
)}
Save Settings
</button>
<button
onClick={() => openEditModal(section)}
className="p-2.5 bg-white/5 border border-white/5 rounded-xl text-slate-400 hover:bg-white/10 hover:text-white transition-all"
title="Edit Block Content"
<a
href={getPageUrl(page.slug)}
target="_blank"
rel="noreferrer"
className="inline-flex flex-1 justify-center items-center gap-2 px-4 py-2 rounded-xl bg-white/5 hover:bg-white/10 border border-white/10 text-white text-xs font-bold uppercase tracking-wider transition-all w-full sm:w-auto"
title={`Open ${page.title} live preview`}
>
<Edit className="w-4 h-4" />
</button>
<button
onClick={() => handleDeleteSection(section.id)}
className="p-2.5 bg-rose-500/10 border border-rose-500/10 rounded-xl text-rose-500 hover:bg-rose-500 hover:text-white transition-all"
title="Delete Block"
>
<Trash2 className="w-4 h-4" />
</button>
<ExternalLink className="w-4 h-4" />
Preview Live
</a>
</div>
{page.slug !== 'home' && (
<button
onClick={handleDeletePage}
className="text-xs font-bold text-rose-500 hover:text-rose-400 hover:underline uppercase tracking-wider transition-all mt-2 w-fit mx-auto"
>
Delete this page
</button>
)}
</div>
))
</div>
)}
</div>
</div>
)}
{/* Editor Modal */}
{isModalOpen && editingSection && (
<div className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4 md:p-8">
<div className="bg-night-900 border border-white/10 rounded-3xl shadow-2xl w-full max-w-5xl flex flex-col max-h-full overflow-hidden animate-pop-in text-slate-200">
<div className="p-6 border-b border-white/5 flex items-center justify-between bg-white/[0.02]">
<h3 className="text-xl font-bold flex items-center gap-2">
<Settings2 className="w-5 h-5 text-cyan-glow" />
{editingSection.id ? `Edit ${editingSection.type} Block` : 'Create Layout Block'}
</h3>
<button
type="button"
onClick={() => setIsModalOpen(false)}
className="p-2 rounded-xl hover:bg-white/5 text-slate-400 hover:text-white transition-all"
{/* Section Management Header */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 bg-night-950/40 border border-white/5 p-4 rounded-2xl">
<div>
<h2 className="text-lg font-bold">Page Sections</h2>
<p className="text-xs text-slate-400 mt-1">Manage the content blocks for this page.</p>
</div>
<div className="flex gap-3">
<button
onClick={() => setIsReorderModalOpen(true)}
className="flex items-center gap-2 px-5 py-2.5 bg-white/10 hover:bg-white/20 border border-white/5 text-white rounded-xl font-bold text-xs uppercase tracking-wider transition-all"
>
<Layers className="w-4 h-4" />
Reorder Layout
</button>
<button
onClick={openAddModal}
className="flex items-center gap-2 px-6 py-2.5 bg-cyan-glow text-night-900 hover:bg-cyan-glow/90 shadow-lg shadow-cyan-glow/20 rounded-xl font-bold text-xs uppercase tracking-wider transition-all"
>
<Plus className="w-4 h-4" />
Add Section
</button>
</div>
</div>
{/* Block Selection Dropdown */}
<div className="bg-night-950/40 border border-white/5 p-4 rounded-2xl space-y-3">
<label className="text-[10px] font-bold uppercase tracking-widest text-slate-400">
Select Block to Edit
</label>
<select
value={editingSection?.id?.toString() || ''}
onChange={(e) => {
const selectedId = e.target.value;
if (!selectedId) {
setEditingSection(null);
return;
}
const sec = sections.find(s => s.id?.toString() === selectedId);
if (sec) openEditModal(sec);
}}
className="bg-slate-900 border border-white/10 text-white rounded-xl px-4 py-2 text-sm focus:outline-none focus:border-cyan-glow/50 focus:ring-1 focus:ring-cyan-glow transition-all w-full"
>
<option value="">-- Choose a block --</option>
{sections.map((section, idx) => {
const title = getAdminDisplayString(
section.payload?.title || section.payload?.tagline || section.payload?.quote || (section.type?.toUpperCase() === 'ANNOUNCEMENT_BAR' ? section.payload?.slides?.[0] : undefined)
) || `Block #${section.id}`;
return (
<option key={section.id} value={section.id?.toString()}>
{idx + 1}. {section.type} - {title} {section.isActive ? '' : '(Hidden)'}
</option>
);
})}
</select>
</div>
</div>
<div className="lg:col-span-7 sticky top-8">
{editingSection ? (
<div className="bg-night-900 border border-white/10 rounded-3xl shadow-2xl w-full flex flex-col max-h-[calc(100vh-4rem)] overflow-hidden animate-pop-in text-slate-200">
<div className="px-8 py-6 border-b border-white/5 flex items-center justify-between bg-gradient-to-r from-white/[0.03] to-transparent shrink-0">
<div className="flex flex-col gap-1">
<h3 className="text-xl font-bold flex items-center gap-3">
<Settings2 className="w-5 h-5 text-cyan-glow" />
{editingSection.id ? 'Edit Block Settings' : 'Create Layout Block'}
</h3>
{editingSection.id && (
<div className="flex items-center gap-2 mt-1 ml-8 flex-wrap">
<span className="px-2 py-0.5 text-[10px] font-bold font-mono bg-cyan-glow/10 text-cyan-glow rounded border border-cyan-glow/20 uppercase tracking-wider">
Type: {editingSection.type}
</span>
<span className="px-2 py-0.5 text-[10px] font-bold font-mono bg-white/5 text-slate-400 rounded border border-white/10 tracking-wider">
Block ID: {editingSection.id}
</span>
<div className="w-px h-3 bg-white/10 mx-1"></div>
<span className="px-2 py-0.5 text-[10px] font-bold font-mono bg-fuchsia-500/10 text-fuchsia-400 rounded border border-fuchsia-500/20 uppercase tracking-wider">
Page: {page?.slug || 'Unknown'}
</span>
<span className="px-2 py-0.5 text-[10px] font-bold font-mono bg-white/5 text-slate-400 rounded border border-white/10 tracking-wider">
Page ID: {page?.id || '-'}
</span>
</div>
)}
</div>
<button
type="button"
onClick={() => setEditingSection(null)}
className="p-2 rounded-xl hover:bg-white/5 text-slate-400 hover:text-white transition-all"
>
<X className="w-6 h-6" />
</button>
</div>
{/* Sub-header for Language Tabs */}
<div className="px-8 pt-4 pb-0 border-b border-white/5 bg-night-900/90 backdrop-blur-md flex gap-6 overflow-x-auto hide-scrollbar">
{['th', 'en', 'ja', 'zh', 'vi', 'ko'].map((lang) => (
<button
key={lang}
type="button"
onClick={() => setEditingLang(lang)}
className={`relative pb-3 text-xs font-bold uppercase tracking-widest border-b-2 transition-all whitespace-nowrap ${
editingLang === lang
? 'border-cyan-glow text-cyan-glow'
: 'border-transparent text-slate-500 hover:text-slate-300 hover:border-white/20'
}`}
>
{lang}
</button>
))}
{/* Premium Sub-header for Language Tabs */}
<div className="px-8 py-4 border-b border-white/5 bg-night-950/50 flex items-center overflow-x-auto hide-scrollbar">
<div className="bg-black/40 p-1 rounded-xl flex gap-1 border border-white/5">
{['th', 'en', 'ja', 'zh', 'vi', 'ko'].map((lang) => (
<button
key={lang}
type="button"
onClick={() => setEditingLang(lang)}
className={`relative px-4 py-2 text-xs font-bold uppercase tracking-widest transition-all rounded-lg whitespace-nowrap ${
editingLang === lang
? 'bg-cyan-glow text-night-900 shadow-sm'
: 'text-slate-400 hover:text-white hover:bg-white/5'
}`}
>
{lang}
</button>
))}
</div>
</div>
<form onSubmit={handleFormSubmit} className="flex-1 flex flex-col min-h-0">
@@ -1264,7 +1188,7 @@ export default function PageBuilderPage() {
</button>
<button
type="button"
onClick={() => setIsModalOpen(false)}
onClick={() => setEditingSection(null)}
className="px-8 py-3 bg-white/5 hover:bg-white/10 text-white font-bold rounded-xl transition-all"
>
Cancel
@@ -1272,6 +1196,150 @@ export default function PageBuilderPage() {
</div>
</form>
</div>
) : (
<div className="h-full flex flex-col items-center justify-center p-16 text-center text-slate-500 bg-white/[0.02] border border-white/5 rounded-3xl min-h-[400px]">
<Settings2 className="w-12 h-12 mx-auto text-slate-700 mb-4" />
<p>Select a block from the left to edit its content</p>
</div>
)}
</div>
</div>
)}
{/* Reorder Layout Modal */}
{isReorderModalOpen && (
<div className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
<div className="bg-night-900 border border-white/10 rounded-3xl shadow-2xl w-full max-w-xl flex flex-col max-h-[85vh] overflow-hidden animate-pop-in text-slate-200">
<div className="p-6 border-b border-white/5 flex items-center justify-between bg-white/[0.02]">
<h3 className="text-xl font-bold flex items-center gap-2">
<Layers className="w-5 h-5 text-cyan-glow" />
Reorder Layout
</h3>
<button
onClick={() => setIsReorderModalOpen(false)}
className="p-2 rounded-xl hover:bg-white/5 text-slate-400 hover:text-white transition-all"
>
<X className="w-6 h-6" />
</button>
</div>
<div className="flex-1 overflow-y-auto p-6 space-y-3">
{sections.length === 0 ? (
<div className="p-16 text-center text-slate-500 space-y-4 bg-white/[0.02] border border-white/5 rounded-2xl">
<Layers className="w-12 h-12 mx-auto text-slate-700" />
<p>No content sections in this page yet.</p>
</div>
) : (
sections.map((section, idx) => (
<div
key={section.id}
className={`py-2 px-3 flex items-center justify-between bg-white/[0.02] border rounded-xl transition-all group ${
section.isActive ? 'border-l-2 border-l-cyan-glow border-y-white/5 border-r-white/5 hover:border-cyan-glow/30' : 'border-l-2 border-l-slate-600 border-y-white/5 border-r-white/5 opacity-80 hover:border-slate-500'
}`}
>
<div className="flex items-center gap-3 overflow-hidden flex-1">
{/* Index & Order controls */}
<div className="flex items-center gap-1 bg-black/20 px-1.5 py-1 rounded-lg border border-white/5 shrink-0">
<span className="text-[10px] font-mono text-slate-400 font-bold w-3 text-center">{idx + 1}</span>
<div className="flex flex-col gap-0.5">
<button
onClick={() => handleMove(idx, 'up')}
disabled={idx === 0}
className="hover:text-white disabled:opacity-20 text-slate-400 transition-colors"
>
<ArrowUp className="w-3 h-3" />
</button>
<button
onClick={() => handleMove(idx, 'down')}
disabled={idx === sections.length - 1}
className="hover:text-white disabled:opacity-20 text-slate-400 transition-colors"
>
<ArrowDown className="w-3 h-3" />
</button>
</div>
</div>
{/* Section Description */}
<div className="flex items-center gap-2 truncate">
<span className="text-[9px] font-bold font-mono px-1.5 py-0.5 bg-slate-800 rounded border border-white/5 text-slate-300 uppercase tracking-wider shrink-0">
{section.type}
</span>
{!section.isActive && (
<span className="text-[9px] uppercase font-bold tracking-widest text-slate-500 bg-slate-900 px-1.5 py-0.5 rounded border border-white/10 shrink-0">
Hidden
</span>
)}
<h3 className="text-xs font-bold text-slate-100 truncate">
{getAdminDisplayString(
section.payload?.title
|| section.payload?.tagline
|| section.payload?.quote
|| (section.type?.toUpperCase() === 'ANNOUNCEMENT_BAR' ? section.payload?.slides?.[0] : undefined)
) || `Block #${section.id}`}
</h3>
</div>
</div>
{/* Actions */}
<div className="flex items-center gap-1 opacity-100 sm:opacity-50 group-hover:opacity-100 transition-opacity shrink-0 ml-2">
<button
onClick={() => handleToggleActive(section)}
className={`p-1.5 rounded-lg border transition-all ${
section.isActive
? 'bg-cyan-glow/10 border-cyan-glow/20 text-cyan-glow hover:bg-cyan-glow/20'
: 'bg-white/5 border-white/5 text-slate-400 hover:bg-white/10 hover:text-white'
}`}
title={section.isActive ? 'Hide Section' : 'Show Section'}
>
{section.isActive ? <Eye className="w-3.5 h-3.5" /> : <EyeOff className="w-3.5 h-3.5" />}
</button>
<button
onClick={() => {
setIsReorderModalOpen(false);
openEditModal(section);
}}
className="p-1.5 bg-white/5 border border-white/5 rounded-lg text-slate-300 hover:bg-white/10 hover:text-white transition-all"
title="Edit Block Content"
>
<Edit className="w-3.5 h-3.5" />
</button>
<button
onClick={() => handleDeleteSection(section.id)}
className="p-1.5 bg-rose-500/10 border border-rose-500/10 rounded-lg text-rose-500 hover:bg-rose-500 hover:text-white transition-all"
title="Delete Block"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
</div>
</div>
))
)}
</div>
<div className="p-6 border-t border-white/5 flex gap-3 bg-white/[0.02]">
{sections.length > 0 && (
<button
onClick={handleSaveOrder}
disabled={saveStatus === 'saving'}
className="flex-1 flex justify-center items-center gap-2 px-5 py-2.5 bg-cyan-glow hover:bg-cyan-glow/90 text-night-900 shadow-lg shadow-cyan-glow/20 disabled:opacity-50 rounded-xl font-bold text-sm uppercase tracking-wider transition-all"
>
{saveStatus === 'saving' ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<Save className="w-4 h-4" />
)}
Save Order
</button>
)}
<button
type="button"
onClick={() => setIsReorderModalOpen(false)}
className="px-6 py-2.5 bg-white/5 hover:bg-white/10 text-white font-bold rounded-xl transition-all text-sm uppercase tracking-wider"
>
Close
</button>
</div>
</div>
</div>
)}