diff --git a/src/components/calendar/BookingCalendar.tsx b/src/components/calendar/BookingCalendar.tsx index 10ac038..9dd2b4e 100644 --- a/src/components/calendar/BookingCalendar.tsx +++ b/src/components/calendar/BookingCalendar.tsx @@ -367,11 +367,11 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
{/* Legend */} -
+
{(['confirmed', 'checked_in', 'checked_out', 'inquiry'] as const).map(s => ( -
-
- {BOOKING_STATUS_LABELS[s]} +
+
+ {BOOKING_STATUS_LABELS[s]}
))}
@@ -386,7 +386,7 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook 'w-1.5 h-1.5 rounded-full', wsConnected ? 'bg-emerald-500' : 'bg-slate-300 dark:bg-slate-600 animate-pulse', )} /> - {wsConnected ? 'Онлайн' : 'Офлайн'} + {wsConnected ? 'Синхронизация' : 'Офлайн'}
)} @@ -489,7 +489,15 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook ) } // Room row - const roomBookings = bookings.filter(b => b.roomId === room.id && b.status !== 'cancelled') + const allRoomBookings = bookings.filter(b => b.roomId === room.id && b.status !== 'cancelled') + // Hide checked_out bookings that overlap with a newer active booking + const activeRoomBookings = allRoomBookings.filter(b => b.status !== 'checked_out') + const roomBookings = allRoomBookings.filter(b => { + if (b.status !== 'checked_out') return true + return !activeRoomBookings.some(other => + other.id !== b.id && b.checkIn < other.checkOut && b.checkOut > other.checkIn, + ) + }) const draftStyle = getDraftStyle(room.id) rows.push(
)} {isModuleActive('housekeeping') && ( - m.id === 'housekeeping')!.sidebarItem!.icon} - label="Уборка" - onClick={onClose} - /> + <> + m.id === 'housekeeping')!.sidebarItem!.icon} + label="Уборка" + onClick={onClose} + /> + {!isHousekeeper && ( + + )} + )} {!isHousekeeper && activeModuleItems.map(m => ( - {isModuleActive('channel-manager') && (

Приоритет

-
- {TECH_PRIORITY_ITEMS.map(p => ( - - ))} +
+ +
+ {TECH_PRIORITY_ITEMS.filter(p => p.value !== 'urgent').map(p => ( + + ))} +
{techPriority === 'urgent' && (

@@ -310,24 +321,27 @@ export function RoomContextMenu({ room, x, y, onClose, onStatusChange, onHkStatu className="hidden" onChange={e => { const f = e.target.files?.[0]; if (f) { handleTechPhotoUpload(f); e.target.value = '' } }} /> -

- {techPhotos.map((url, i) => ( -
- - -
- ))} - -
+ {techPhotos.length > 0 && ( +
+ {techPhotos.map((url, i) => ( +
+ + +
+ ))} +
+ )} +
- )} -
+ + + ))} {!isDone && ( handlePhotoUpload(task.id, file)} /> @@ -406,12 +412,25 @@ function TaskCreateModal({ const [rooms, setRooms] = useState<{ id: string; number: string }[]>([]) const [users, setUsers] = useState<{ id: string; name: string }[]>([]) const [saving, setSaving] = useState(false) + const [photos, setPhotos] = useState([]) + const [photoUploading, setPhotoUploading] = useState(false) + const photoRef = useRef(null) useEffect(() => { api.rooms.list(slug).then(r => setRooms(r.map(rm => ({ id: rm.id, number: rm.number })))).catch(() => {}) api.users.list(slug).then(u => setUsers(u)).catch(() => {}) }, [slug]) + const handlePhotoUpload = async (file: File) => { + setPhotoUploading(true) + try { + const url = await api.upload.photo(file, 'tasks') + setPhotos(prev => [...prev, url]) + } catch { /* ignore */ } finally { + setPhotoUploading(false) + } + } + const handleSave = async () => { if (!form.notes.trim()) return setSaving(true) @@ -424,6 +443,7 @@ function TaskCreateModal({ due_date: form.due_date || undefined, assignee_id: form.assignee_id || undefined, category: 'maintenance', + photos, }) onCreated(task) } catch (err) { @@ -450,7 +470,7 @@ function TaskCreateModal({
@@ -474,9 +494,33 @@ function TaskCreateModal({ {users.map(u => )}
+
+ + { const f = e.target.files?.[0]; if (f) { handlePhotoUpload(f); e.target.value = '' } }} /> +
+ {photos.map((url, i) => ( +
+ + +
+ ))} + +
+
-