feat: add Spa/Pool gallery tabs, rename Social to Public Events, add Private Events

- Add 'Spa Gallery' (🧖) section in Image Gallery
- Add 'Pool Gallery' (🏊) section in Image Gallery
- Rename 'Social Events' to 'Public Events' (🎪) in sidebar
- Add 'Private Events' (🔒) placeholder section in sidebar
- Update category filters for spa/pool/public/private images
This commit is contained in:
2026-07-02 20:26:16 -05:00
parent 5b30d0ca24
commit 361fd67e49
+23 -5
View File
@@ -27,7 +27,8 @@ const NAV = [
{ id: 'gallery', label: 'Images', icon: '🖼️' }, { id: 'gallery', label: 'Images', icon: '🖼️' },
{ id: 'slides', label: 'Slideshow', icon: '🎞️' }, { id: 'slides', label: 'Slideshow', icon: '🎞️' },
{ id: 'calendar', label: 'Calendar', icon: '📅' }, { id: 'calendar', label: 'Calendar', icon: '📅' },
{ id: 'social', label: 'Social Events', icon: '🎪' }, { id: 'social', label: 'Public Events', icon: '🎪' },
{ id: 'private-events', label: 'Private Events', icon: '🔒' },
{ id: 'rooms', label: 'Rooms', icon: '🏨' }, { id: 'rooms', label: 'Rooms', icon: '🏨' },
{ id: 'amenities', label: 'Amenities', icon: '✨' }, { id: 'amenities', label: 'Amenities', icon: '✨' },
{ id: 'trips', label: 'Trips', icon: '✈️' }, { id: 'trips', label: 'Trips', icon: '✈️' },
@@ -273,8 +274,11 @@ function ImageGallery({ images, onDelete }: { images: Image[]; onDelete: (id: nu
const sectionCategories = [ const sectionCategories = [
{ key: 'slides', label: '🎞️ Slideshow Gallery', filter: (i: Image) => i.category === 'slides' || i.location_target === 'slides' }, { key: 'slides', label: '🎞️ Slideshow Gallery', filter: (i: Image) => i.category === 'slides' || i.location_target === 'slides' },
{ key: 'food', label: '🍽️ Food Gallery', filter: (i: Image) => i.category === 'food' || i.location_target === 'food' }, { key: 'food', label: '🍽️ Food Gallery', filter: (i: Image) => i.category === 'food' || i.location_target === 'food' },
{ key: 'spa', label: '🧖 Spa Gallery', filter: (i: Image) => i.category === 'spa' || i.location_target === 'spa' },
{ key: 'pool', label: '🏊 Pool Gallery', filter: (i: Image) => i.category === 'pool' || i.location_target === 'pool' },
{ key: 'calendar', label: '📅 Calendar Events Gallery', filter: (i: Image) => i.category === 'calendar' || i.location_target === 'calendar' }, { key: 'calendar', label: '📅 Calendar Events Gallery', filter: (i: Image) => i.category === 'calendar' || i.location_target === 'calendar' },
{ key: 'social', label: '🎪 Social Events Gallery', filter: (i: Image) => i.category === 'social' || i.location_target === 'social' }, { key: 'public', label: '🎪 Public Events Gallery', filter: (i: Image) => i.category === 'public' || i.location_target === 'public' || i.category === 'social' || i.location_target === 'social' },
{ key: 'private', label: '🔒 Private Events Gallery', filter: (i: Image) => i.category === 'private' || i.location_target === 'private' },
]; ];
// Group room images by room location_target (e.g. "room-1", "room-2") // Group room images by room location_target (e.g. "room-1", "room-2")
@@ -292,6 +296,10 @@ function ImageGallery({ images, onDelete }: { images: Image[]; onDelete: (id: nu
if (selectedCategory === 'all') return true; if (selectedCategory === 'all') return true;
if (selectedCategory === 'rooms') return img.category === 'rooms' || img.location_target?.startsWith('room-'); if (selectedCategory === 'rooms') return img.category === 'rooms' || img.location_target?.startsWith('room-');
if (selectedCategory === 'food') return img.category === 'food' || img.location_target === 'food'; if (selectedCategory === 'food') return img.category === 'food' || img.location_target === 'food';
if (selectedCategory === 'spa') return img.category === 'spa' || img.location_target === 'spa';
if (selectedCategory === 'pool') return img.category === 'pool' || img.location_target === 'pool';
if (selectedCategory === 'public') return img.category === 'public' || img.location_target === 'public' || img.category === 'social' || img.location_target === 'social';
if (selectedCategory === 'private') return img.category === 'private' || img.location_target === 'private';
return img.category === selectedCategory || img.location_target === selectedCategory; return img.category === selectedCategory || img.location_target === selectedCategory;
}); });
@@ -389,7 +397,7 @@ function ImageGallery({ images, onDelete }: { images: Image[]; onDelete: (id: nu
border: `1px solid ${C.border}`, background: '#fff', border: `1px solid ${C.border}`, background: '#fff',
boxShadow: C.shadow, boxShadow: C.shadow,
}}> }}>
{['all', 'slides', 'food', 'calendar', 'social', 'rooms', 'other'].map((cat) => ( {['all', 'slides', 'food', 'spa', 'pool', 'calendar', 'public', 'private', 'rooms', 'other'].map((cat) => (
<Btn <Btn
key={cat} key={cat}
onClick={() => setSelectedCategory(cat)} onClick={() => setSelectedCategory(cat)}
@@ -786,9 +794,9 @@ function SocialSection({ events, setEvents, images, onToast }: any) {
return ( return (
<div> <div>
<SecHead title="🎪 Social Events" count={events.length} <SecHead title="🎪 Public Events" count={events.length}
action={<Btn onClick={() => setEditing({ id: 0, name: '', description: '', price: null, date: null, photos: [], featured_photo: null, active: true, sort_order: events.length })} size="sm">+ New Event</Btn>} /> action={<Btn onClick={() => setEditing({ id: 0, name: '', description: '', price: null, date: null, photos: [], featured_photo: null, active: true, sort_order: events.length })} size="sm">+ New Event</Btn>} />
{events.length === 0 ? <Empty icon="🎪" title="No social events" /> : ( {events.length === 0 ? <Empty icon="🎪" title="No public events" /> : (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: '16px' }}> <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: '16px' }}>
{events.map((ev: SocialEvent) => ( {events.map((ev: SocialEvent) => (
<EntityCard key={ev.id} title={ev.name} desc={ev.description || ''} meta={`${ev.price || 'Free'}${ev.date ? ' • ' + ev.date : ''}`} <EntityCard key={ev.id} title={ev.name} desc={ev.description || ''} meta={`${ev.price || 'Free'}${ev.date ? ' • ' + ev.date : ''}`}
@@ -868,6 +876,15 @@ function SocialEditor({ event, setEvent, images, onSave, onCancel }: any) {
} }
/* ─── Rooms Section ─── */ /* ─── Rooms Section ─── */
function PrivateEventsSection({ onToast }: { onToast: (msg: string, type: 'success' | 'error') => void }) {
return (
<div>
<SecHead title="🔒 Private Events" count={0} />
<Empty icon="🔒" title="Private Events" desc="Manage private event inquiries and bookings here. Contact form submissions for private events will appear in this section." />
</div>
);
}
function RoomsSection({ rooms, setRooms, images, onToast }: any) { function RoomsSection({ rooms, setRooms, images, onToast }: any) {
const [editing, setEditing] = useState<Room | null>(null); const [editing, setEditing] = useState<Room | null>(null);
const [confirmDel, setConfirmDel] = useState<number | null>(null); const [confirmDel, setConfirmDel] = useState<number | null>(null);
@@ -2548,6 +2565,7 @@ export default function AdminPage() {
case 'slides': return <SlidesSection slides={slides} setSlides={setSlides} images={images} onToast={showToast} onRefreshImages={loadImages} />; case 'slides': return <SlidesSection slides={slides} setSlides={setSlides} images={images} onToast={showToast} onRefreshImages={loadImages} />;
case 'calendar': return <CalendarSection events={events} setEvents={setEvents} images={images} onToast={showToast} />; case 'calendar': return <CalendarSection events={events} setEvents={setEvents} images={images} onToast={showToast} />;
case 'social': return <SocialSection events={socialEvents} setEvents={setSocialEvents} images={images} onToast={showToast} />; case 'social': return <SocialSection events={socialEvents} setEvents={setSocialEvents} images={images} onToast={showToast} />;
case 'private-events': return <PrivateEventsSection onToast={showToast} />;
case 'rooms': return <RoomsSection rooms={rooms} setRooms={setRooms} images={images} onToast={showToast} />; case 'rooms': return <RoomsSection rooms={rooms} setRooms={setRooms} images={images} onToast={showToast} />;
case 'amenities': return <AmenitiesSection onToast={showToast} />; case 'amenities': return <AmenitiesSection onToast={showToast} />;
case 'menu': return <MenuSection items={menuItems} setItems={setMenuItems} images={images} onToast={showToast} />; case 'menu': return <MenuSection items={menuItems} setItems={setMenuItems} images={images} onToast={showToast} />;