diff --git a/src/components/calendar/BookingCalendar.tsx b/src/components/calendar/BookingCalendar.tsx
index 8301110..6ee6a67 100644
--- a/src/components/calendar/BookingCalendar.tsx
+++ b/src/components/calendar/BookingCalendar.tsx
@@ -215,12 +215,14 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
if (draftRoom && (draftRoom.status === 'maintenance' || draftRoom.status === 'blocked')) {
const bookIn = new Date(checkIn + 'T00:00:00')
const bookOut = new Date(checkOut + 'T00:00:00')
- const mFrom = draftRoom.maintenanceFrom ? new Date(draftRoom.maintenanceFrom + 'T00:00:00') : null
- const mTo = draftRoom.maintenanceTo ? new Date(draftRoom.maintenanceTo + 'T00:00:00') : null
+ const mFrom = draftRoom.maintenanceFrom ? new Date(draftRoom.maintenanceFrom.slice(0, 10) + 'T00:00:00') : null
+ const mTo = draftRoom.maintenanceTo ? new Date(draftRoom.maintenanceTo.slice(0, 10) + 'T00:00:00') : null
const overlaps = (!mFrom || bookOut > mFrom) && (!mTo || bookIn < mTo)
if (overlaps) {
- const period = mFrom
- ? ` (${draftRoom.maintenanceFrom}${mTo ? ` — ${draftRoom.maintenanceTo}` : ' — без срока'})`
+ const fromStr = draftRoom.maintenanceFrom?.slice(0, 10)
+ const toStr = draftRoom.maintenanceTo?.slice(0, 10)
+ const period = fromStr
+ ? ` (${fromStr}${toStr ? ` — ${toStr}` : ' — без срока'})`
: ' (без срока)'
setMaintenanceWarningDraft({ draft, roomStatus: draftRoom.status, period })
setDragStart(null); setDragEnd(null); setDraft(null)
@@ -522,16 +524,16 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
{/* Maintenance / blocked overlay — with optional date range */}
{(room.status === 'maintenance' || room.status === 'blocked') && (() => {
- const from = room.maintenanceFrom ? new Date(room.maintenanceFrom + 'T00:00:00') : null
- const to = room.maintenanceTo ? new Date(room.maintenanceTo + 'T00:00:00') : null
- // Calculate left offset and width if dates are set
- let left = 0, width = '100%'
+ // slice(0,10) normalises both 'YYYY-MM-DD' and full ISO timestamps from Postgres
+ const from = room.maintenanceFrom ? new Date(room.maintenanceFrom.slice(0, 10) + 'T00:00:00') : null
+ const to = room.maintenanceTo ? new Date(room.maintenanceTo.slice(0, 10) + 'T00:00:00') : null
+ let left = 0, width: string | number = '100%'
if (from || to) {
- const visibleFrom = from ? Math.max(0, differenceInDays(from, startDate)) : 0
- const visibleTo = to ? Math.min(dates.length - 1, differenceInDays(to, startDate)) : dates.length - 1
- if (visibleTo < 0 || visibleFrom >= dates.length) return null
- left = visibleFrom * CELL_WIDTH
- width = `${(visibleTo - visibleFrom + 1) * CELL_WIDTH}px`
+ const vFrom = from ? Math.max(0, differenceInDays(from, startDate)) : 0
+ const vTo = to ? Math.min(dates.length, differenceInDays(to, startDate)) : dates.length
+ if (vTo <= 0 || vFrom >= dates.length || vFrom >= vTo) return null
+ left = vFrom * CELL_WIDTH
+ width = (vTo - vFrom) * CELL_WIDTH
}
return (
)
diff --git a/src/components/rooms/RoomContextMenu.tsx b/src/components/rooms/RoomContextMenu.tsx
index 0c17494..b412c02 100644
--- a/src/components/rooms/RoomContextMenu.tsx
+++ b/src/components/rooms/RoomContextMenu.tsx
@@ -1,9 +1,12 @@
import { useEffect, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
-import { Wrench, Ban, CheckCircle, Sparkles, ClipboardList, Pencil, ChevronRight, X as XIcon } from 'lucide-react'
+import { Wrench, Ban, CheckCircle, Sparkles, ClipboardList, Pencil, ChevronRight, X as XIcon, CalendarDays } from 'lucide-react'
+import { format } from 'date-fns'
import type { Room, RoomStatus, HousekeepingStatus } from '../../types'
import { cn } from '../../lib/utils'
+const today = () => format(new Date(), 'yyyy-MM-dd')
+
interface RoomContextMenuProps {
room: Room
x: number
@@ -16,9 +19,9 @@ interface RoomContextMenuProps {
const HK_ITEMS: { status: HousekeepingStatus; label: string; icon: React.ReactNode; color: string }[] = [
{ status: 'clean', label: 'Чисто', icon:
, color: 'text-emerald-600 dark:text-emerald-400' },
- { status: 'dirty', label: 'Убрать', icon:
, color: 'text-red-500 dark:text-red-400' },
- { status: 'cleaning', label: 'Убирается', icon:
, color: 'text-blue-500 dark:text-blue-400' },
{ status: 'inspect', label: 'Проверить', icon:
, color: 'text-amber-600 dark:text-amber-400' },
+ { status: 'cleaning', label: 'Убирается', icon:
, color: 'text-blue-500 dark:text-blue-400' },
+ { status: 'dirty', label: 'Убрать', icon:
, color: 'text-red-500 dark:text-red-400' },
]
type SubForm = 'maintenance' | 'blocked' | null
@@ -26,9 +29,9 @@ type SubForm = 'maintenance' | 'blocked' | null
export function RoomContextMenu({ room, x, y, onClose, onStatusChange, onHkStatusChange, onEdit }: RoomContextMenuProps) {
const ref = useRef
(null)
const [subForm, setSubForm] = useState(null)
- const [dateFrom, setDateFrom] = useState(room.maintenanceFrom ?? '')
- const [dateTo, setDateTo] = useState(room.maintenanceTo ?? '')
- const [indefinite, setIndefinite] = useState(!room.maintenanceTo)
+ const [dateFrom, setDateFrom] = useState(room.maintenanceFrom ? room.maintenanceFrom.slice(0, 10) : today())
+ const [dateTo, setDateTo] = useState(room.maintenanceTo ? room.maintenanceTo.slice(0, 10) : '')
+ const [indefinite, setIndefinite] = useState(false)
useEffect(() => {
const handler = (e: MouseEvent) => {
@@ -200,6 +203,30 @@ export function RoomContextMenu({ room, x, y, onClose, onStatusChange, onHkStatu
)
}
+function DateInput({ value, onChange, min }: { value: string; onChange: (v: string) => void; min?: string }) {
+ const ref = useRef(null)
+ return (
+
+ onChange(e.target.value)}
+ />
+
+
+ )
+}
+
function DateRangeForm({
dateFrom, setDateFrom, dateTo, setDateTo,
indefinite, setIndefinite, label, onApply, onCancel,
@@ -217,12 +244,7 @@ function DateRangeForm({