ContentManagement.tsx
Golden-Hour-backend-layer-2/pages/ContentManagement.tsx
import React, { useState, useRef } from 'react';
import { useNavigate, useParams, Link } from 'react-router-dom';
interface Room {
no: string;
type: string;
unit: string;
price: string;
color: string;
status: string;
}
interface ContentManagementProps {
view?: 'listing' | 'maisons' | 'experiences' | 'services' | 'products' | 'blog' | 'downloads';
createNew?: boolean;
}
const ProtocolBar: React.FC<{ onCreate?: () => void }> = ({ onCreate }) => (
<div className="fixed bottom-12 left-1/2 -translate-x-1/2 bg-white/90 backdrop-blur-md border border-[#c5a059]/20 px-10 py-5 rounded-full gallery-shadow flex items-center space-x-12 z-50 transition-all hover:border-[#c5a059]/40 group">
<button className="flex items-center space-x-3 text-slate-400 hover:text-slate-900 transition-all">
<i className="fa-solid fa-magnifying-glass text-[9px] text-[#c5a059]"></i>
<span className="text-[8px] font-bold uppercase tracking-[0.3em]">Search</span>
</button>
<div className="w-[0.5px] h-4 bg-[#c5a059]/20"></div>
<button onClick={onCreate} className="flex items-center space-x-3 text-slate-400 hover:text-slate-900 transition-all">
<i className="fa-solid fa-plus text-[9px] text-[#c5a059]"></i>
<span className="text-[8px] font-bold uppercase tracking-[0.3em]">Create</span>
</button>
</div>
);
const ListingIconsModal: React.FC<{
isOpen: boolean;
onClose: () => void;
}> = ({ isOpen, onClose }) => {
const [icons, setIcons] = useState([
{ label: 'Bedroom', icon: 'fa-bed' },
{ label: 'Bathroom', icon: 'fa-bath' },
{ label: 'Kitchen', icon: 'fa-utensils' },
{ label: 'WiFi', icon: 'fa-wifi' },
{ label: 'Parking', icon: 'fa-car' },
{ label: 'Pool', icon: 'fa-person-swimming' }
]);
const [newIconLabel, setNewIconLabel] = useState('');
const [newIconClass, setNewIconClass] = useState('');
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-slate-900/40 backdrop-blur-sm fade-in-section">
<div className="bg-white rounded-3xl gallery-shadow w-full max-w-lg overflow-hidden border border-[#c5a059]/20">
<div className="p-8 border-b border-[#c5a059]/10 flex items-center justify-between bg-[#fdfcf9]">
<div>
<h3 className="font-serif italic text-2xl text-slate-900">Icon Archive Registry</h3>
<p className="text-[7px] font-bold uppercase tracking-[0.3em] text-[#c5a059] mt-1 font-serif">Visual Asset Allocation</p>
</div>
<button onClick={onClose} className="text-slate-300 hover:text-slate-900 transition-all">
<i className="fa-solid fa-xmark text-lg"></i>
</button>
</div>
<div className="p-8 space-y-6">
<div className="grid grid-cols-2 gap-4 max-h-[300px] overflow-y-auto no-scrollbar">
{icons.map((item, idx) => (
<div key={idx} className="flex items-center p-4 rounded-xl border border-[#c5a059]/5 bg-[#fdfcf9]/50 group relative">
<div className="w-10 h-10 rounded-full bg-white border border-[#c5a059]/10 flex items-center justify-center text-[#c5a059] mr-4">
<i className={`fa-solid ${item.icon} text-sm`}></i>
</div>
<div>
<p className="text-[9px] font-bold uppercase tracking-widest text-slate-800">{item.label}</p>
<p className="text-[7px] font-serif italic text-slate-400">{item.icon}</p>
</div>
<button
onClick={() => setIcons(icons.filter((_, i) => i !== idx))}
className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 text-rose-500 hover:scale-110 transition-all"
>
<i className="fa-solid fa-circle-xmark text-xs"></i>
</button>
</div>
))}
</div>
<div className="pt-6 border-t border-[#c5a059]/10 space-y-3">
<p className="text-[8px] font-bold uppercase tracking-widest text-slate-300">Add New Symbol Protocol</p>
<div className="flex space-x-2">
<input
value={newIconLabel}
onChange={(e) => setNewIconLabel(e.target.value)}
placeholder="Label..."
className="flex-1 bg-transparent border border-[#c5a059]/10 rounded-full px-4 py-2 text-[10px] font-medium uppercase tracking-wider focus:border-[#c5a059] outline-none transition-all"
/>
<input
value={newIconClass}
onChange={(e) => setNewIconClass(e.target.value)}
placeholder="fa-icon-name..."
className="flex-1 bg-transparent border border-[#c5a059]/10 rounded-full px-4 py-2 text-[10px] font-medium tracking-wider focus:border-[#c5a059] outline-none transition-all"
/>
<button
onClick={() => { if(newIconLabel && newIconClass) { setIcons([...icons, {label: newIconLabel, icon: newIconClass}]); setNewIconLabel(''); setNewIconClass(''); } }}
className="bg-black text-white px-6 py-2 rounded-full text-[8px] font-bold uppercase tracking-widest hover:bg-[#c5a059] transition-all"
>
Register
</button>
</div>
</div>
</div>
<div className="p-6 bg-[#fdfcf9] border-t border-[#c5a059]/10 flex justify-end">
<button onClick={onClose} className="text-[8px] font-bold uppercase tracking-[0.2em] text-slate-400 hover:text-slate-900 transition-all">Close Registry</button>
</div>
</div>
</div>
);
};
const CategoriesModal: React.FC<{
isOpen: boolean;
onClose: () => void;
title: string;
initialCategories: string[];
}> = ({ isOpen, onClose, title, initialCategories }) => {
const [categories, setCategories] = useState(initialCategories);
const [newCat, setNewCat] = useState('');
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-slate-900/40 backdrop-blur-sm fade-in-section">
<div className="bg-white rounded-3xl gallery-shadow w-full max-w-md overflow-hidden border border-[#c5a059]/20">
<div className="p-8 border-b border-[#c5a059]/10 flex items-center justify-between bg-[#fdfcf9]">
<div>
<h3 className="font-serif italic text-2xl text-slate-900">{title} Settings</h3>
<p className="text-[7px] font-bold uppercase tracking-[0.3em] text-[#c5a059] mt-1 font-serif">Registry Configuration</p>
</div>
<button onClick={onClose} className="text-slate-300 hover:text-slate-900 transition-all">
<i className="fa-solid fa-xmark text-lg"></i>
</button>
</div>
<div className="p-8 space-y-6">
<div className="space-y-3 max-h-[300px] overflow-y-auto no-scrollbar">
{categories.map((cat, idx) => (
<div key={idx} className="flex items-center justify-between group p-3 rounded-xl border border-[#c5a059]/5 hover:border-[#c5a059]/20 hover:bg-[#fdfcf9] transition-all">
<span className="font-serif italic text-base text-slate-800">{cat}</span>
<div className="flex space-x-3 opacity-0 group-hover:opacity-100 transition-all">
<button className="text-slate-300 hover:text-[#c5a059] transition-all"><i className="fa-solid fa-pen-nib text-[10px]"></i></button>
<button
onClick={() => setCategories(categories.filter((_, i) => i !== idx))}
className="text-slate-300 hover:text-rose-500 transition-all"
>
<i className="fa-solid fa-trash-can text-[10px]"></i>
</button>
</div>
</div>
))}
</div>
<div className="pt-6 border-t border-[#c5a059]/10">
<div className="flex space-x-2">
<input
value={newCat}
onChange={(e) => setNewCat(e.target.value)}
placeholder="New type..."
className="flex-1 bg-transparent border border-[#c5a059]/10 rounded-full px-4 py-2 text-[10px] font-medium uppercase tracking-wider focus:border-[#c5a059] outline-none transition-all"
/>
<button
onClick={() => { if(newCat) { setCategories([...categories, newCat]); setNewCat(''); } }}
className="bg-black text-white px-4 py-2 rounded-full text-[8px] font-bold uppercase tracking-widest hover:bg-[#c5a059] transition-all"
>
Add
</button>
</div>
</div>
</div>
<div className="p-6 bg-[#fdfcf9] border-t border-[#c5a059]/10 flex justify-end">
<button onClick={onClose} className="text-[8px] font-bold uppercase tracking-[0.2em] text-slate-400 hover:text-slate-900 transition-all">Close Registry</button>
</div>
</div>
</div>
);
};
const HeaderSection: React.FC<{
title: string;
sub: string;
onOpenCategories?: () => void;
onOpenIcons?: () => void;
}> = ({ title, sub, onOpenCategories, onOpenIcons }) => (
<div className="flex flex-col md:flex-row md:items-end justify-between border-b border-[#c5a059]/10 pb-8 mb-10 gap-6">
<div>
<h2 className="font-serif italic text-4xl text-slate-900 tracking-tighter">{title}</h2>
<p className="text-[8px] font-bold uppercase tracking-[0.3em] text-[#c5a059] mt-3 font-serif italic">{sub}</p>
</div>
<div className="flex items-center space-x-4">
{onOpenIcons && (
<button
onClick={onOpenIcons}
className="bg-white border border-[#c5a059]/10 text-slate-400 px-6 py-2.5 rounded-full text-[8px] font-bold uppercase tracking-[0.2em] hover:text-[#c5a059] hover:border-[#c5a059]/30 transition-all shadow-sm flex items-center"
>
<i className="fa-solid fa-shapes mr-2 opacity-50"></i> Asset Symbols
</button>
)}
{onOpenCategories && (
<button
onClick={onOpenCategories}
className="bg-[#fdfcf9] border border-[#c5a059]/20 text-[#c5a059] px-6 py-2.5 rounded-full text-[8px] font-bold uppercase tracking-[0.2em] hover:bg-[#c5a059] hover:text-white transition-all shadow-sm"
>
<i className="fa-solid fa-list-check mr-2"></i> Categories setting
</button>
)}
</div>
</div>
);
const ActionButtons: React.FC<{ onEdit?: () => void }> = ({ onEdit }) => (
<div className="flex justify-center items-center space-x-6">
<button onClick={onEdit} className="text-slate-300 hover:text-[#c5a059] transition-all" title="Edit Content">
<i className="fa-solid fa-pen-nib text-[11px]"></i>
</button>
<button className="text-slate-300 hover:text-[#c5a059] transition-all" title="Content Settings">
<i className="fa-solid fa-sliders text-[11px]"></i>
</button>
<button className="text-slate-300 hover:text-rose-600 transition-all" title="Archive Content">
<i className="fa-solid fa-trash-can text-[11px]"></i>
</button>
</div>
);
const InputLabel: React.FC<React.PropsWithChildren<{}>> = ({ children }) => (
<label className="text-[9px] font-bold uppercase tracking-[0.25em] text-[#c5a059] mb-2 block font-serif italic">{children}</label>
);
const MaisonCreateView: React.FC<{ onCancel: () => void; onCommit: (room: Room) => void; initialRoom?: Room }> = ({ onCancel, onCommit, initialRoom }) => {
const isEdit = !!initialRoom;
const [activeSection, setActiveSection] = useState('Fundamental Identity');
const [ref, setRef] = useState(initialRoom?.no ?? '');
const [name, setName] = useState(initialRoom?.unit ?? '');
const [type, setType] = useState(initialRoom?.type ?? 'Maison');
const [price, setPrice] = useState(initialRoom?.price?.replace('€', '') ?? '');
const scrollContainerRef = useRef<HTMLDivElement>(null);
const coreSections = [
'Fundamental Identity',
'Interior Inventory',
'Property Highlights',
'Place Narrative',
'What this place offers'
];
const operationalSections = [
'Geography',
'Visual Archives',
'Fiscal Matrix',
'Operational Protocol',
'Statutory Documents'
];
const allSections = [...coreSections, ...operationalSections];
const generateRef = () => {
const newRef = 'GH-' + Math.floor(100 + Math.random() * 900);
setRef(newRef);
};
const handleScroll = (direction: 'left' | 'right') => {
if (scrollContainerRef.current) {
const scrollAmount = 300;
scrollContainerRef.current.scrollBy({
left: direction === 'left' ? -scrollAmount : scrollAmount,
behavior: 'smooth'
});
}
};
const handleCommit = () => {
if (!name || !ref) {
alert("Please ensure Name and Reference are completed.");
return;
}
const newRoom: Room = {
no: ref,
type: type,
unit: name,
price: `€${price || '0'}`,
color: initialRoom?.color ?? '#c5a059',
status: initialRoom?.status ?? 'Published'
};
onCommit(newRoom);
};
// Fix: Explicitly type NavItem as React.FC to avoid errors when passing 'key' prop
const NavItem: React.FC<{ section: string }> = ({ section }) => (
<button
onMouseEnter={() => setActiveSection(section)}
className={`text-[8px] font-bold uppercase tracking-[0.3em] transition-all relative pb-2 font-serif italic whitespace-nowrap px-1 ${
activeSection === section ? 'text-slate-900' : 'text-slate-300 hover:text-slate-500'
}`}
>
{section}
<div className={`absolute bottom-0 left-0 right-0 h-[1.5px] bg-[#c5a059] transition-all duration-700 ${
activeSection === section ? 'opacity-100 scale-x-100' : 'opacity-0 scale-x-0'
}`}></div>
</button>
);
return (
<div className="fade-in-section space-y-12 max-w-7xl mx-auto pb-20 px-4">
<div className="flex items-center justify-between border-b border-[#c5a059]/10 pb-8">
<div>
<nav className="text-[8px] font-bold uppercase tracking-[0.2em] text-slate-400 mb-3 flex items-center gap-2">
<Link to="/content/maisons" className="hover:text-[#c5a059] transition-colors">Content Management</Link>
<span>/</span>
<Link to="/content/maisons" className="hover:text-[#c5a059] transition-colors">Maisons</Link>
<span>/</span>
<span className="text-slate-600">{isEdit ? 'Edit Property' : 'New Property'}</span>
</nav>
<h2 className="font-serif italic text-5xl text-slate-900 tracking-tighter">{isEdit ? 'Edit Property Registry' : 'New Property Registry'}</h2>
<p className="text-[10px] font-bold uppercase tracking-[0.5em] text-[#c5a059] mt-3 font-serif italic">{isEdit ? 'Edit Drafting Protocol' : 'Sanctuary Drafting Protocol'}</p>
</div>
<div className="flex space-x-4">
<button onClick={onCancel} className="px-8 py-2.5 rounded-full text-[8px] font-bold uppercase tracking-[0.3em] text-slate-400 hover:text-slate-900 transition-all">{isEdit ? 'Cancel' : 'Cancel Draft'}</button>
<button onClick={handleCommit} className="bg-black text-white px-10 py-3 rounded-full text-[8px] font-black uppercase tracking-[0.4em] hover:bg-[#c5a059] transition-all shadow-xl">{isEdit ? 'Save Changes' : 'Commit Registry'}</button>
</div>
</div>
{/* Navigation Cluster: Fixed width scroller integrated with buttons */}
<div className="bg-white/50 backdrop-blur-sm sticky top-14 z-20 border-b border-[#c5a059]/10 py-4 w-full flex items-center">
{/* Far Left Navigation Scroll Button */}
<button
onClick={() => handleScroll('left')}
className="w-8 h-8 rounded-full border border-[#c5a059]/10 flex items-center justify-center text-[#c5a059]/40 hover:text-[#c5a059] hover:border-[#c5a059]/40 hover:bg-[#fdfcf9] transition-all flex-shrink-0 z-10 bg-white/80"
title="Scroll Left"
>
<i className="fa-solid fa-chevron-left text-[8px]"></i>
</button>
{/* Scrollable Container focused within the page width */}
<div
ref={scrollContainerRef}
className="flex-1 overflow-x-auto no-scrollbar flex items-center px-4 space-x-8"
>
{/* Core identity Group */}
<div className="flex items-center space-x-8">
{coreSections.map((s) => <NavItem key={s} section={s} />)}
</div>
{/* Junction Line */}
<div className="w-[1px] h-8 bg-[#c5a059]/10 flex-shrink-0"></div>
{/* Operational Group */}
<div className="flex items-center space-x-8">
{operationalSections.map((s) => <NavItem key={s} section={s} />)}
</div>
</div>
{/* Far Right Navigation Scroll Button */}
<button
onClick={() => handleScroll('right')}
className="w-8 h-8 rounded-full border border-[#c5a059]/10 flex items-center justify-center text-[#c5a059]/40 hover:text-[#c5a059] hover:border-[#c5a059]/40 hover:bg-[#fdfcf9] transition-all flex-shrink-0 z-10 bg-white/80 ml-2"
title="Scroll Right"
>
<i className="fa-solid fa-chevron-right text-[8px]"></i>
</button>
</div>
<div className="min-h-[500px] flex flex-col items-center">
{activeSection === 'Fundamental Identity' && (
<div className="w-full max-w-3xl fade-in-section bg-white p-12 rounded-[3rem] border border-[#c5a059]/10 gallery-shadow">
<h4 className="font-serif italic text-3xl mb-10 text-slate-900">Fundamental Identity</h4>
<div className="space-y-8">
<div className="flex gap-10">
<div className="flex-1">
<InputLabel>Room Type</InputLabel>
<select
value={type}
onChange={(e) => setType(e.target.value)}
className="w-full bg-transparent border-b border-[#c5a059]/10 py-4 text-base focus:border-[#c5a059] transition-all outline-none font-serif italic"
>
<option value="Maison">Maison</option>
<option value="Apartment">Apartment</option>
<option value="Villa">Villa</option>
<option value="Studio">Studio</option>
</select>
</div>
<div className="w-1/3">
<InputLabel>Reference</InputLabel>
<div className="flex items-center border-b border-[#c5a059]/10">
<input value={ref} onChange={(e) => setRef(e.target.value)} placeholder="e.g. GH-001" className="bg-transparent w-full py-4 text-sm font-mono outline-none" />
<button onClick={generateRef} className="text-[#c5a059] hover:text-slate-900 ml-2" title="Generate Reference">
<i className="fa-solid fa-wand-magic-sparkles text-xs"></i>
</button>
</div>
</div>
</div>
<div>
<InputLabel>Name of Property</InputLabel>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter the Atelier name..."
className="w-full bg-transparent border-b border-[#c5a059]/10 py-4 text-xl font-serif italic focus:border-[#c5a059] outline-none"
/>
</div>
<div className="flex gap-10">
<div className="flex-1">
<InputLabel>Guest Range (Max Capacity)</InputLabel>
<input type="number" placeholder="4" className="w-full bg-transparent border-b border-[#c5a059]/10 py-4 text-base focus:border-[#c5a059] outline-none" />
</div>
</div>
</div>
</div>
)}
{activeSection === 'Interior Inventory' && (
<div className="w-full max-w-3xl fade-in-section bg-white p-12 rounded-[3rem] border border-[#c5a059]/10 gallery-shadow">
<h4 className="font-serif italic text-3xl mb-10 text-slate-900">Interior Inventory</h4>
<div className="grid grid-cols-2 gap-10">
{[
{ label: 'Double Beds', icon: 'fa-bed' },
{ label: 'Single Beds', icon: 'fa-bed-pulse' },
{ label: 'Bath w/ Shower', icon: 'fa-shower' },
{ label: 'Bath w/ Bathtub', icon: 'fa-bath' },
].map((item, i) => (
<div key={i} className="flex items-center space-x-8 border-b border-[#c5a059]/5 pb-6">
<div className="w-12 h-12 rounded-full bg-[#fdfcf9] border border-[#c5a059]/10 flex items-center justify-center text-[#c5a059]">
<i className={`fa-solid ${item.icon} text-base`}></i>
</div>
<div className="flex-1">
<span className="text-[8px] font-bold uppercase tracking-widest text-slate-300 block mb-1">{item.label}</span>
<input type="number" defaultValue={0} className="w-full bg-transparent text-xl font-serif italic outline-none" />
</div>
</div>
))}
</div>
</div>
)}
{activeSection === 'Property Highlights' && (
<div className="w-full max-w-3xl fade-in-section bg-white p-12 rounded-[3rem] border border-[#c5a059]/10 gallery-shadow">
<h4 className="font-serif italic text-3xl mb-10 text-slate-900">Property Highlights</h4>
<div className="space-y-6">
{[1, 2].map(i => (
<div key={i} className="flex items-start space-x-8 p-8 rounded-[2rem] bg-[#fdfcf9] border border-[#c5a059]/5 group">
<div className="w-24 h-24 rounded-2xl bg-slate-100 flex items-center justify-center text-slate-300 border border-dashed border-[#c5a059]/20 overflow-hidden relative">
<i className="fa-solid fa-image text-2xl"></i>
<input type="file" className="absolute inset-0 opacity-0 cursor-pointer" />
</div>
<div className="flex-1 space-y-4">
<input placeholder="Highlight Title..." className="w-full bg-transparent text-[10px] font-bold uppercase tracking-widest outline-none border-b border-transparent focus:border-[#c5a059]/20" />
<textarea placeholder="Short captivating description..." className="w-full bg-transparent text-xs font-serif italic text-slate-400 outline-none resize-none h-16" />
</div>
<button className="text-slate-200 hover:text-rose-500 transition-all opacity-0 group-hover:opacity-100 p-2">
<i className="fa-solid fa-trash-can text-sm"></i>
</button>
</div>
))}
<button className="w-full py-6 border border-dashed border-[#c5a059]/10 rounded-[2rem] text-[9px] font-bold uppercase tracking-[0.3em] text-slate-300 hover:text-[#c5a059] transition-all mt-4">
<i className="fa-solid fa-plus mr-3"></i> Add Highlight
</button>
</div>
</div>
)}
{activeSection === 'Place Narrative' && (
<div className="w-full max-w-3xl fade-in-section bg-white p-12 rounded-[3rem] border border-[#c5a059]/10 gallery-shadow">
<h4 className="font-serif italic text-3xl mb-10 text-slate-900">Place Narrative</h4>
<div className="space-y-6">
<InputLabel>Full Descriptive Ledger</InputLabel>
<textarea placeholder="Tell the story of this property..." className="w-full h-80 bg-[#fdfcf9] border border-[#c5a059]/10 rounded-[2rem] p-10 text-base font-serif italic leading-relaxed outline-none focus:border-[#c5a059] transition-all resize-none shadow-inner" />
<p className="text-[8px] font-serif italic text-slate-300 text-center uppercase tracking-widest mt-4">Craft an evocative narrative for prospective guests.</p>
</div>
</div>
)}
{activeSection === 'What this place offers' && (
<div className="w-full max-w-3xl fade-in-section bg-white p-12 rounded-[3rem] border border-[#c5a059]/10 gallery-shadow">
<h4 className="font-serif italic text-3xl mb-10 text-slate-900">What this place offers</h4>
<div className="grid grid-cols-1 gap-6">
{[1, 2, 3].map(i => (
<div key={i} className="flex items-center space-x-8 p-6 rounded-[2rem] border border-[#c5a059]/5 hover:bg-[#fdfcf9] transition-all relative group">
<input type="checkbox" className="w-5 h-5 rounded-lg border-[#c5a059]/20 text-[#c5a059] focus:ring-[#c5a059]" />
<div className="w-16 h-16 rounded-xl bg-slate-50 flex items-center justify-center text-slate-300 border border-dashed border-[#c5a059]/10 relative overflow-hidden">
<i className="fa-solid fa-square-plus text-xl"></i>
<input type="file" className="absolute inset-0 opacity-0 cursor-pointer" />
</div>
<div className="flex-1">
<input placeholder="Amenity Name..." className="text-[11px] font-bold uppercase tracking-widest text-slate-800 bg-transparent border-none outline-none mb-1" />
<p className="text-[9px] font-serif italic text-slate-400">Capturing the essence of service...</p>
</div>
<button className="text-slate-200 hover:text-rose-500 opacity-0 group-hover:opacity-100 transition-opacity"><i className="fa-solid fa-xmark text-sm"></i></button>
</div>
))}
<button className="py-6 border border-dashed border-[#c5a059]/10 rounded-[2rem] text-[9px] font-bold uppercase tracking-widest text-slate-300 hover:text-[#c5a059] mt-4">
Create New Amenity Template
</button>
</div>
</div>
)}
{activeSection === 'Geography' && (
<div className="w-full max-w-3xl fade-in-section bg-white p-12 rounded-[3rem] border border-[#c5a059]/10 gallery-shadow space-y-12">
<h4 className="font-serif italic text-3xl mb-10 text-slate-900">Geography</h4>
<div className="space-y-10">
<div>
<InputLabel>Map Registry URL</InputLabel>
<input placeholder="Google Maps or Mapbox URL..." className="w-full bg-transparent border-b border-[#c5a059]/10 py-4 text-base outline-none font-mono text-[#c5a059]" />
</div>
<div>
<InputLabel>Literal Address</InputLabel>
<textarea placeholder="Full postal registry address..." className="w-full h-32 bg-transparent border border-[#c5a059]/10 rounded-[2rem] p-8 text-base font-serif italic outline-none focus:border-[#c5a059] transition-all" />
</div>
</div>
</div>
)}
{activeSection === 'Visual Archives' && (
<div className="w-full max-w-3xl fade-in-section bg-white p-12 rounded-[3rem] border border-[#c5a059]/10 gallery-shadow space-y-12">
<h4 className="font-serif italic text-3xl mb-10 text-slate-900">Visual Archives</h4>
<div className="space-y-10">
<div>
<InputLabel>Gallery Repository</InputLabel>
<div className="grid grid-cols-4 gap-6">
{[1, 2, 3, 4, 5].map(i => (
<div key={i} className="aspect-square bg-[#fdfcf9] border border-dashed border-[#c5a059]/20 rounded-2xl flex items-center justify-center text-slate-200 hover:border-[#c5a059] hover:text-[#c5a059] transition-all cursor-pointer group relative">
<i className="fa-solid fa-camera text-2xl group-hover:scale-110 transition-transform"></i>
<input type="file" multiple className="absolute inset-0 opacity-0 cursor-pointer" />
</div>
))}
</div>
</div>
<div>
<InputLabel>Motion Archive (YouTube URL)</InputLabel>
<div className="flex items-center border-b border-[#c5a059]/10 py-2">
<i className="fa-brands fa-youtube text-rose-500 text-xl mr-5"></i>
<input placeholder="https://youtube.com/watch?v=..." className="w-full bg-transparent py-4 text-sm outline-none font-mono" />
</div>
</div>
</div>
</div>
)}
{activeSection === 'Fiscal Matrix' && (
<div className="w-full max-w-3xl fade-in-section bg-white p-12 rounded-[3rem] border border-[#c5a059]/10 gallery-shadow space-y-12">
<h4 className="font-serif italic text-3xl mb-10 text-slate-900">Fiscal Matrix</h4>
<div className="grid grid-cols-2 gap-12">
<div>
<InputLabel>Standard Nightly Rate</InputLabel>
<div className="flex items-center border-b border-[#c5a059]/10 py-2">
<span className="text-slate-300 text-xl mr-4 font-serif italic">€</span>
<input
type="number"
value={price}
onChange={(e) => setPrice(e.target.value)}
placeholder="0.00"
className="bg-transparent w-full py-4 text-2xl font-serif tracking-tighter outline-none"
/>
</div>
</div>
<div>
<InputLabel>Seasonal (Premium) Rate</InputLabel>
<div className="flex items-center border-b border-[#c5a059]/10 py-2">
<span className="text-slate-300 text-xl mr-4 font-serif italic">€</span>
<input type="number" placeholder="0.00" className="bg-transparent w-full py-4 text-2xl font-serif tracking-tighter outline-none" />
</div>
</div>
</div>
<div>
<h5 className="text-[9px] font-black uppercase tracking-[0.4em] text-slate-300 mb-8">Ancillary Services (Add-ons)</h5>
<div className="space-y-6">
<div className="grid grid-cols-3 gap-8 items-end">
<div>
<label className="text-[7px] font-bold text-slate-200 uppercase tracking-widest block mb-2">Service Identity</label>
<input placeholder="Private Chef..." className="w-full bg-transparent border-b border-[#c5a059]/10 py-3 text-sm font-serif italic" />
</div>
<div>
<label className="text-[7px] font-bold text-slate-200 uppercase tracking-widest block mb-2">Unit Value</label>
<input type="number" placeholder="€0.00" className="w-full bg-transparent border-b border-[#c5a059]/10 py-3 text-sm font-serif italic" />
</div>
<div>
<label className="text-[7px] font-bold text-slate-200 uppercase tracking-widest block mb-2">Quota Max</label>
<input type="number" placeholder="Qty" className="w-full bg-transparent border-b border-[#c5a059]/10 py-3 text-sm font-serif italic" />
</div>
</div>
<button className="w-full py-4 bg-[#fdfcf9] border border-[#c5a059]/10 rounded-2xl text-[8px] font-bold uppercase tracking-[0.3em] text-[#c5a059] hover:bg-[#c5a059] hover:text-white transition-all">Add Service Row</button>
</div>
</div>
</div>
)}
{activeSection === 'Operational Protocol' && (
<div className="w-full max-w-3xl fade-in-section bg-white p-12 rounded-[3rem] border border-[#c5a059]/10 gallery-shadow space-y-12">
<h4 className="font-serif italic text-3xl mb-10 text-slate-900">Operational Protocol</h4>
<div className="space-y-10">
<div>
<InputLabel>House Rules & Etiquette</InputLabel>
<textarea placeholder="Conduct expectations..." className="w-full h-32 bg-transparent border border-[#c5a059]/10 rounded-[2rem] p-8 text-sm font-serif italic outline-none focus:border-[#c5a059] transition-all" />
</div>
<div>
<InputLabel>Cancellation Protocol</InputLabel>
<textarea placeholder="Terms of withdrawal..." className="w-full h-32 bg-transparent border border-[#c5a059]/10 rounded-[2rem] p-8 text-sm font-serif italic outline-none focus:border-[#c5a059] transition-all" />
</div>
<div>
<InputLabel>Safety & Emergency Registry</InputLabel>
<textarea placeholder="Safety features, emergency protocols..." className="w-full h-32 bg-transparent border border-[#c5a059]/10 rounded-[2rem] p-8 text-sm font-serif italic outline-none focus:border-[#c5a059] transition-all" />
</div>
</div>
</div>
)}
{activeSection === 'Statutory Documents' && (
<div className="w-full max-w-3xl fade-in-section bg-white p-12 rounded-[3rem] border border-[#c5a059]/10 gallery-shadow space-y-12">
<h4 className="font-serif italic text-3xl mb-10 text-slate-900">Statutory Documents</h4>
<div>
<InputLabel>General Terms & Conditions</InputLabel>
<textarea placeholder="Formal binding agreement text..." className="w-full h-40 bg-transparent border border-[#c5a059]/10 rounded-[2rem] p-10 text-sm font-serif italic outline-none focus:border-[#c5a059] transition-all" />
</div>
<div className="grid grid-cols-1 gap-6">
<div className="p-10 rounded-[2.5rem] bg-[#fdfcf9] border border-dashed border-[#c5a059]/20 flex items-center justify-between group">
<div>
<span className="text-[10px] font-bold uppercase tracking-widest text-slate-800 block">General Information PDF</span>
<span className="text-[8px] font-serif italic text-slate-400 mt-1">Formal operational documentation</span>
</div>
<label className="bg-black text-white px-8 py-3 rounded-full text-[8px] font-black uppercase tracking-[0.3em] cursor-pointer hover:bg-[#c5a059] transition-all">
Upload Registry
<input type="file" className="hidden" />
</label>
</div>
</div>
</div>
)}
</div>
<div className="pt-20 border-t border-[#c5a059]/10 flex flex-col items-center">
<button onClick={handleCommit} className="bg-black text-white px-24 py-6 rounded-full text-[10px] font-black uppercase tracking-[0.5em] hover:bg-[#c5a059] hover:scale-105 transition-all duration-700 shadow-[0_30px_70px_-20px_rgba(197,160,89,0.3)]">
Inaugurate Sanctuary
</button>
<p className="text-[9px] font-serif italic text-slate-300 mt-8 uppercase tracking-[0.3em] opacity-60">Finalizing this registry will commit it to the Atelier Index.</p>
</div>
</div>
);
};
const ContentManagement: React.FC<ContentManagementProps> = ({ view = 'listing', createNew = false }) => {
const navigate = useNavigate();
const { id: editId } = useParams();
const [rooms, setRooms] = useState<Room[]>([
{ no: '001', type: 'Maison', unit: 'Villa Golden Hour', price: '€2.5k', color: '#c5a059', status: 'Published' },
{ no: '002', type: 'Apartment', unit: 'Azure Suite 4B', price: '€1.8k', color: '#64748b', status: 'Draft' },
{ no: '003', type: 'Room', unit: 'Starlight Room', price: '€850', color: '#94a3b8', status: 'Published' },
]);
const addNewMaison = (newRoom: Room) => {
setRooms(prev => [...prev, newRoom]);
};
const updateMaison = (updatedRoom: Room) => {
setRooms(prev => prev.map(r => r.no === updatedRoom.no ? updatedRoom : r));
};
const roomToEdit = editId ? rooms.find(r => r.no === editId) : null;
if (createNew && view === 'maisons') {
return (
<MaisonCreateView
onCancel={() => navigate('/content/maisons')}
onCommit={(room) => {
addNewMaison(room);
navigate('/content/maisons');
}}
/>
);
}
if (editId && view === 'maisons' && roomToEdit) {
return (
<MaisonCreateView
initialRoom={roomToEdit}
onCancel={() => navigate('/content/maisons')}
onCommit={(room) => {
updateMaison(room);
navigate('/content/maisons');
}}
/>
);
}
return (
<div className="relative pb-32">
<div className="fade-in-section">
{(() => {
switch (view) {
case 'listing': return <ManageListingView />;
case 'maisons': return <MaisonsTemplateView rooms={rooms} onEnterCreate={() => navigate('/content/maisons/new')} onEditRoom={(room) => navigate(`/content/maisons/${room.no}/edit`)} />;
case 'experiences': return <ExperiencesServicesView type="Experience" />;
case 'services': return <ExperiencesServicesView type="Service" />;
case 'products': return <ProductsTemplateView />;
case 'blog': return <BlogTemplateView />;
case 'downloads': return <DownloadsTemplateView />;
default: return <ManageListingView />;
}
})()}
</div>
<ProtocolBar onCreate={() => view === 'maisons' ? navigate('/content/maisons/new') : undefined} />
</div>
);
};
const ExperiencesServicesView: React.FC<{ type: 'Experience' | 'Service' }> = ({ type }) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [isIconsModalOpen, setIsIconsModalOpen] = useState(false);
const categories = type === 'Experience'
? ['City guided tour', 'Handmade Course', 'Tuscany guided tour']
: ['Transportation', 'Active', 'Event', 'Professionals', 'Rental'];
const items = [
{ id: '#001', category: categories[0], name: 'The Golden Masterclass', type: 'Course', price: '€32.00', color: '#c5a059', status: 'Available' },
{ id: '#002', category: categories[1], name: 'Handmade Pasta Session', type: 'Workshop', price: '€45.00', color: '#94a3b8', status: 'Booked' },
{ id: '#003', category: categories[2], name: 'Sunset Chianti Tour', type: 'Tour', price: '€120.00', color: '#64748b', status: 'Available' },
];
return (
<div className="space-y-6">
<HeaderSection
title={`Livre des ${type}s`}
sub="Operational Portfolio Ledger"
onOpenCategories={() => setIsModalOpen(true)}
onOpenIcons={() => setIsIconsModalOpen(true)}
/>
<div className="bg-white rounded-xl border border-[#c5a059]/5 overflow-hidden gallery-shadow">
<table className="w-full text-left">
<thead className="text-stone-300 font-bold uppercase tracking-[0.25em] text-[7px] border-b border-[#c5a059]/5 italic font-serif">
<tr>
<th className="px-6 py-5">Ref..</th>
<th className="px-6 py-5">Category</th>
<th className="px-6 py-5">{type}</th>
<th className="px-6 py-5">Type</th>
<th className="px-6 py-5">Price</th>
<th className="px-6 py-5">Calendar</th>
<th className="px-6 py-5">Tag Color</th>
<th className="px-6 py-5">Status</th>
<th className="px-6 py-5 text-center">Action</th>
</tr>
</thead>
<tbody className="divide-y divide-[#c5a059]/5">
{items.map((item, idx) => (
<tr key={idx} className="hover:bg-[#fdfcf9] transition-all duration-500 group">
<td className="px-6 py-7 serif-numeral text-sm text-[#c5a059]">{item.id}</td>
<td className="px-6 py-7">
<span className="text-[8px] font-bold uppercase tracking-[0.1em] text-stone-400 font-serif italic">{item.category}</span>
</td>
<td className="px-6 py-7">
<p className="font-serif italic text-lg text-slate-800 leading-none">{item.name}</p>
</td>
<td className="px-6 py-7 text-[9px] font-bold uppercase text-slate-400 tracking-widest">{item.type}</td>
<td className="px-6 py-7 serif-numeral text-base text-slate-900 tracking-tighter">{item.price}</td>
<td className="px-6 py-7">
<button className="text-[#c5a059]/30 hover:text-[#c5a059] transition-all">
<i className="fa-solid fa-calendar-days text-sm"></i>
</button>
</td>
<td className="px-6 py-7">
<div className="w-2.5 h-2.5 rounded-full" style={{ backgroundColor: item.color }}></div>
</td>
<td className="px-6 py-7">
<span className={`text-[6px] font-bold uppercase tracking-[0.1em] px-2.5 py-1 rounded-full border ${
item.status === 'Available' ? 'border-emerald-100 text-emerald-600 bg-emerald-50/5' : 'border-stone-50 text-stone-300'
}`}>{item.status}</span>
</td>
<td className="px-6 py-7">
<ActionButtons />
</td>
</tr>
))}
</tbody>
</table>
</div>
<CategoriesModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
title={`${type} Type`}
initialCategories={categories}
/>
<ListingIconsModal
isOpen={isIconsModalOpen}
onClose={() => setIsIconsModalOpen(false)}
/>
</div>
);
};
const ProductsTemplateView: React.FC = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [isIconsModalOpen, setIsIconsModalOpen] = useState(false);
const products = [
{ name: 'Huile d\'Olive Elite', sku: 'UY-2501', price: '€99.49', stock: 49, cat: 'Épicerie', img: 'https://picsum.photos/seed/p1/150/150' },
{ name: 'Chianti Reserva \'18', sku: 'UY-2502', price: '€89.49', stock: 103, cat: 'Cave', img: 'https://picsum.photos/seed/p2/150/150' },
{ name: 'Collection de Lin', sku: 'UY-2503', price: '€299.49', stock: 68, cat: 'Maison', img: 'https://picsum.photos/seed/p3/150/150' },
];
return (
<div className="space-y-10">
<HeaderSection
title="Collections Boutique"
sub="Curated Assets"
onOpenCategories={() => setIsModalOpen(true)}
onOpenIcons={() => setIsIconsModalOpen(true)}
/>
<div className="grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-5 gap-8">
{products.map((p, idx) => (
<div key={idx} className="group cursor-default">
<div className="relative overflow-hidden rounded-xl bg-[#fdfcf9] border border-[#c5a059]/10 mb-4 aspect-[4/5] gallery-shadow">
<img src={p.img} alt="" className="w-full h-full object-cover grayscale group-hover:grayscale-0 group-hover:scale-105 transition-all duration-[1s] opacity-80 group-hover:opacity-100" />
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center space-x-6">
<button className="bg-white w-8 h-8 rounded-full flex items-center justify-center text-slate-800 hover:text-[#c5a059] transition-all"><i className="fa-solid fa-pen-nib text-[10px]"></i></button>
<button className="bg-white w-8 h-8 rounded-full flex items-center justify-center text-slate-800 hover:text-[#c5a059] transition-all"><i className="fa-solid fa-sliders text-[10px]"></i></button>
<button className="bg-white w-8 h-8 rounded-full flex items-center justify-center text-slate-800 hover:text-rose-600 transition-all"><i className="fa-solid fa-trash-can text-[10px]"></i></button>
</div>
</div>
<div>
<p className="text-[7px] font-bold text-[#c5a059] uppercase tracking-[0.2em] font-serif italic mb-0.5">{p.cat}</p>
<h4 className="font-serif italic text-base text-slate-800 leading-tight">{p.name}</h4>
<p className="serif-numeral text-sm text-stone-900 font-medium mt-1.5">{p.price}</p>
</div>
</div>
))}
</div>
<CategoriesModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
title="Product Categories"
initialCategories={['Épicerie', 'Cave', 'Maison', 'Wellness']}
/>
<ListingIconsModal
isOpen={isIconsModalOpen}
onClose={() => setIsIconsModalOpen(false)}
/>
</div>
);
};
const BlogTemplateView: React.FC = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const posts = [
{ id: 1, title: 'The Art of Italian Slow Living', author: 'Clara Rossi', date: 'May 12, 2025', cat: 'Lifestyle', img: 'https://picsum.photos/seed/blog1/600/400' },
];
return (
<div className="space-y-10">
<HeaderSection
title="Journal Editorial"
sub="Bureau d'Édition"
onOpenCategories={() => setIsModalOpen(true)}
/>
<div className="space-y-16">
{posts.map((post, idx) => (
<div key={idx} className="group flex flex-col md:flex-row gap-10 items-center">
<div className="md:w-1/2 relative">
<div className="overflow-hidden rounded-lg aspect-video gallery-shadow border border-[#c5a059]/5 relative">
<img src={post.img} className="w-full h-full object-cover grayscale group-hover:grayscale-0 transition-all duration-[2s]" alt="" />
<div className="absolute bottom-4 right-4 flex space-x-4 opacity-0 group-hover:opacity-100 transition-all">
<button className="bg-white/90 backdrop-blur-sm p-3 rounded-full text-slate-800 hover:text-[#c5a059] transition-all shadow-sm"><i className="fa-solid fa-pen-nib"></i></button>
<button className="bg-white/90 backdrop-blur-sm p-3 rounded-full text-slate-800 hover:text-[#c5a059] transition-all shadow-sm"><i className="fa-solid fa-sliders"></i></button>
<button className="bg-white/90 backdrop-blur-sm p-3 rounded-full text-slate-800 hover:text-rose-600 transition-all shadow-sm"><i className="fa-solid fa-trash-can"></i></button>
</div>
</div>
</div>
<div className="md:w-1/2 space-y-4">
<p className="text-[8px] font-bold text-[#c5a059] uppercase tracking-[0.2em] font-serif italic">{post.cat} • {post.date}</p>
<h3 className="font-serif italic text-4xl text-slate-900 tracking-tight leading-tight">{post.title}</h3>
<p className="font-serif italic text-base text-stone-400">Rédigé par {post.author}</p>
</div>
</div>
))}
</div>
<CategoriesModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
title="Blog Topics"
initialCategories={['Lifestyle', 'Travel', 'News', 'Culture']}
/>
</div>
);
};
const DownloadsTemplateView: React.FC = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const assets = [
{ id: 1, name: 'Brochure_Maison_2025', size: '12.4 MB', type: 'PDF', cover: 'https://picsum.photos/seed/cover1/400/400' },
{ id: 2, name: 'Experience_Video_Teaser', size: '85.1 MB', type: 'MP4', cover: 'https://picsum.photos/seed/cover2/400/400' },
{ id: 3, name: 'Heritage_Photography_Set', size: '256 MB', type: 'ZIP', cover: 'https://picsum.photos/seed/cover3/400/400' },
{ id: 4, name: 'Operational_Manual_v4', size: '4.2 MB', type: 'DOCX', cover: 'https://picsum.photos/seed/cover4/400/400' },
];
return (
<div className="space-y-12">
<HeaderSection
title="Archives Digitales"
sub="Media & Assets Repository"
onOpenCategories={() => setIsModalOpen(true)}
/>
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-10">
{assets.map((asset) => (
<div key={asset.id} className="group flex flex-col items-center text-center">
<div className="relative w-full aspect-square overflow-hidden rounded-[2rem] bg-stone-100 border border-[#c5a059]/10 gallery-shadow mb-6">
<img src={asset.cover} alt="" className="w-full h-full object-cover grayscale opacity-60 group-hover:grayscale-0 group-hover:opacity-100 group-hover:scale-105 transition-all duration-[1s] opacity-80 group-hover:opacity-100" />
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity bg-black/30 backdrop-blur-[2px] space-x-6">
<button className="bg-white/90 w-10 h-10 rounded-full flex items-center justify-center text-slate-800 hover:text-[#c5a059] transition-all"><i className="fa-solid fa-download text-[11px]"></i></button>
<button className="bg-white/90 w-10 h-10 rounded-full flex items-center justify-center text-slate-800 hover:text-[#c5a059] transition-all"><i className="fa-solid fa-pen-nib text-[11px]"></i></button>
<button className="bg-white/90 w-10 h-10 rounded-full flex items-center justify-center text-slate-800 hover:text-[#c5a059] transition-all"><i className="fa-solid fa-sliders text-[11px]"></i></button>
<button className="bg-white/90 w-10 h-10 rounded-full flex items-center justify-center text-slate-800 hover:text-rose-600 transition-all"><i className="fa-solid fa-trash-can text-[11px]"></i></button>
</div>
<div className="absolute top-4 left-4">
<span className="text-[7px] font-bold text-white bg-[#c5a059] px-2 py-0.5 rounded uppercase tracking-widest">{asset.type}</span>
</div>
</div>
<div>
<h4 className="font-serif italic text-lg text-slate-800 leading-tight mb-1">{asset.name}</h4>
<p className="serif-numeral text-xs text-stone-300 font-medium uppercase tracking-widest">{asset.size}</p>
</div>
</div>
))}
<button
className="aspect-square border-2 border-dashed border-[#c5a059]/10 rounded-[2rem] flex flex-col items-center justify-center text-slate-300 hover:text-[#c5a059] hover:border-[#c5a059]/30 transition-all group"
>
<i className="fa-solid fa-plus text-2xl mb-3 opacity-20 group-hover:opacity-100 transition-opacity"></i>
<span className="text-[8px] font-bold uppercase tracking-[0.2em]">Upload Media</span>
</button>
</div>
<CategoriesModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
title="Media Categories"
initialCategories={['Documents', 'Photography', 'Videography', 'Press Kits']}
/>
</div>
);
};
const ManageListingView: React.FC = () => {
const sections = [
{ title: 'Catégories du Portefeuille', items: [{ name: 'Maison', count: 3 }] },
];
return (
<div className="space-y-10">
{sections.map((section, idx) => (
<div key={idx} className="bg-white rounded-xl border border-[#c5a059]/5 overflow-hidden gallery-shadow">
<div className="px-8 py-5 flex items-center justify-between bg-[#fdfcf9]/30 border-b border-[#c5a059]/5">
<h3 className="font-serif italic text-xl text-slate-900">{section.title}</h3>
</div>
<table className="w-full text-left">
<thead className="text-stone-300 text-[8px] font-bold uppercase tracking-[0.2em] italic font-serif border-b border-[#c5a059]/5">
<tr>
<th className="px-8 py-4">Identité</th>
<th className="px-8 py-4">Registre</th>
<th className="px-8 py-4">État Protocolaire</th>
<th className="px-8 py-4 text-center">Action</th>
</tr>
</thead>
<tbody className="divide-y divide-[#c5a059]/5">
{section.items.map((item, i) => (
<tr key={i} className="hover:bg-[#fdfcf9] transition-all duration-500 group">
<td className="px-8 py-6 font-serif italic text-xl text-slate-800">{item.name}</td>
<td className="px-8 py-6 serif-numeral text-sm text-stone-300">{item.count} unités</td>
<td className="px-8 py-6">
<span className="text-emerald-600 text-[8px] font-bold uppercase tracking-[0.15em] italic font-serif opacity-70">Vérifié</span>
</td>
<td className="px-8 py-6">
<ActionButtons />
</td>
</tr>
))}
</tbody>
</table>
</div>
))}
</div>
);
};
const MaisonsTemplateView: React.FC<{ rooms: Room[]; onEnterCreate: () => void; onEditRoom?: (room: Room) => void }> = ({ rooms, onEnterCreate, onEditRoom }) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [isIconsModalOpen, setIsIconsModalOpen] = useState(false);
return (
<div className="space-y-10">
<HeaderSection
title="Registre des Propriétés"
sub="Maison Portfolio Ledger"
onOpenCategories={() => setIsModalOpen(true)}
onOpenIcons={() => setIsIconsModalOpen(true)}
/>
<div className="bg-white rounded-xl border border-[#c5a059]/5 overflow-hidden gallery-shadow">
<table className="w-full text-left">
<thead className="text-stone-300 text-[8px] font-bold uppercase tracking-[0.25em] italic font-serif border-b border-[#c5a059]/5">
<tr>
<th className="px-8 py-5">Ref..</th>
<th className="px-8 py-5">Type</th>
<th className="px-8 py-5">Unit</th>
<th className="px-8 py-5">Price</th>
<th className="px-8 py-5">Calendar</th>
<th className="px-8 py-5">Tag Color</th>
<th className="px-8 py-5">Status</th>
<th className="px-8 py-5 text-center">Action</th>
</tr>
</thead>
<tbody className="divide-y divide-[#c5a059]/5">
{rooms.map((room, idx) => (
<tr key={idx} className="hover:bg-[#fdfcf9] transition-all duration-700 group">
<td className="px-8 py-7 serif-numeral text-lg text-[#c5a059]">{room.no}</td>
<td className="px-8 py-7">
<span className="text-[9px] font-bold uppercase tracking-widest text-slate-400 font-serif italic">{room.type}</span>
</td>
<td className="px-8 py-7">
<p className="font-serif italic text-xl text-slate-900 leading-none">{room.unit}</p>
</td>
<td className="px-8 py-7 serif-numeral text-lg text-slate-900 tracking-tighter">{room.price}</td>
<td className="px-8 py-7">
<button className="text-[#c5a059]/40 hover:text-[#c5a059] transition-all">
<i className="fa-solid fa-calendar-days text-sm"></i>
</button>
</td>
<td className="px-8 py-7">
<div className="w-2.5 h-2.5 rounded-full" style={{ backgroundColor: room.color }}></div>
</td>
<td className="px-8 py-7">
<span className={`text-[7px] font-bold uppercase tracking-[0.15em] px-2 py-0.5 rounded-full border ${
room.status === 'Published' ? 'border-emerald-100 text-emerald-600 bg-emerald-50/5' : 'border-slate-100 text-slate-400'
}`}>
{room.status}
</span>
</td>
<td className="px-8 py-7">
<ActionButtons onEdit={onEditRoom ? () => onEditRoom(room) : undefined} />
</td>
</tr>
))}
</tbody>
</table>
</div>
<CategoriesModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
title="Estate Type"
initialCategories={['Maison', 'Apartment', 'Room']}
/>
<ListingIconsModal
isOpen={isIconsModalOpen}
onClose={() => setIsIconsModalOpen(false)}
/>
</div>
);
};
export default ContentManagement;
相关文章
App.tsx
App.tsx — typescript source code from the Golden-Hour-backend-layer-2 learning materials (Golden-Hour-backend-layer-2/App.tsx).
阅读文章 →Header.tsx
Header.tsx — typescript source code from the Golden-Hour-backend-layer-2 learning materials (Golden-Hour-backend-layer-2/components/Header.tsx).
阅读文章 →Sidebar.tsx
Sidebar.tsx — typescript source code from the Golden-Hour-backend-layer-2 learning materials (Golden-Hour-backend-layer-2/components/Sidebar.tsx).
阅读文章 →index.tsx
index.tsx — typescript source code from the Golden-Hour-backend-layer-2 learning materials (Golden-Hour-backend-layer-2/index.tsx).
阅读文章 →CustomerManagement.tsx
CustomerManagement.tsx — typescript source code from the Golden-Hour-backend-layer-2 learning materials (Golden-Hour-backend-layer-2/pages/CustomerManagement.tsx).
阅读文章 →CustomerService.tsx
CustomerService.tsx — typescript source code from the Golden-Hour-backend-layer-2 learning materials (Golden-Hour-backend-layer-2/pages/CustomerService.tsx).
阅读文章 →