feat: room amenities selection in room editor

This commit is contained in:
2026-06-29 20:49:51 -05:00
parent 262cd4745c
commit 91fce014fd
3 changed files with 78 additions and 7 deletions
+35 -2
View File
@@ -4,7 +4,7 @@ import { useState, useEffect, useRef, useCallback } from 'react';
type Image = { id: number; filename: string; data: string; category: string; room_id: number | null; location_target: string; uploaded_at: string };
type Slide = { id: number; title: string; subtitle: string; image_url: string | null; image_id: number | null; active: boolean; sort_order: number };
type Room = { id: number; name: string; description: string; price: string; photos: string[]; featured_photo: string | null; active: boolean; sort_order: number };
type Room = { id: number; name: string; description: string; price: string; photos: string[]; featured_photo: string | null; active: boolean; sort_order: number; amenity_ids?: number[] };
type CalendarEvent = { id: number; date: string; title: string; type: string; color: string; description: string | null; active: boolean };
type SocialEvent = { id: number; name: string; description: string; price: string | null; date: string | null; photos: string[]; featured_photo: string | null; active: boolean; sort_order: number };
type MenuItem = { id: number; category: string; name: string; description: string; price: string; is_daily_special: boolean; active: boolean; sort_order: number };
@@ -848,7 +848,22 @@ function RoomsSection({ rooms, setRooms, images, onToast }: any) {
function RoomEditor({ room, setRoom, images, onSave, onCancel }: any) {
const [showPicker, setShowPicker] = useState(false);
const [localPhotos, setLocalPhotos] = useState<string[]>(room.photos || []);
const r = { ...room, photos: localPhotos };
const [allAmenities, setAllAmenities] = useState<Amenity[]>([]);
const [selectedAmenities, setSelectedAmenities] = useState<number[]>(room.amenity_ids || []);
const r = { ...room, photos: localPhotos, amenity_ids: selectedAmenities };
useEffect(() => {
fetch('/api/amenities')
.then(res => res.json())
.then(d => setAllAmenities(d.data || []))
.catch(() => {});
}, []);
const toggleAmenity = (id: number) => {
setSelectedAmenities(prev =>
prev.includes(id) ? prev.filter(a => a !== id) : [...prev, id]
);
};
const addPhotos = async (files: FileList) => {
const newPhotos: string[] = [];
@@ -891,6 +906,24 @@ function RoomEditor({ room, setRoom, images, onSave, onCancel }: any) {
</div>
</div>
<Tgl label="Active" checked={r.active} onChange={(v: boolean) => setRoom({ ...r, active: v })} />
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<label style={{ fontSize: '13px', fontWeight: 600, color: C.text }}>Amenities</label>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '6px' }}>
{allAmenities.map(a => (
<button key={a.id} onClick={() => toggleAmenity(a.id)}
style={{
display: 'flex', alignItems: 'center', gap: '4px',
padding: '6px 10px', borderRadius: 6, border: `1px solid ${selectedAmenities.includes(a.id) ? C.gold : C.border}`,
background: selectedAmenities.includes(a.id) ? C.goldLight : '#fff',
cursor: 'pointer', fontSize: '12px', transition: 'all 150ms',
color: selectedAmenities.includes(a.id) ? C.gold : C.text
}}>
<span style={{ width: 16, height: 16 }} dangerouslySetInnerHTML={{ __html: a.icon_svg.replace(/width="24"/g, 'width="16"').replace(/height="24"/g, 'height="16"') }} />
{a.name}
</button>
))}
</div>
</div>
</div>
<div style={{ display: 'flex', gap: '10px', justifyContent: 'flex-end', marginTop: '24px' }}>
<Btn onClick={onCancel} variant="secondary">Cancel</Btn>