feat: room amenities selection in room editor
This commit is contained in:
+35
-2
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user