diff --git a/src/App.tsx b/src/App.tsx index 26b24d9..4160ac8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -18,6 +18,7 @@ import { BookingWidgetPage } from './pages/BookingWidgetPage' import { AvailabilityPage } from './pages/AvailabilityPage' import { FloorMapPage } from './pages/FloorMapPage' import { AdminDashboard } from './pages/AdminDashboard' +import { MigrationPage } from './pages/MigrationPage' export default function App() { return ( @@ -45,6 +46,7 @@ export default function App() { } /> } /> } /> + } /> {/* Admin routes */} diff --git a/src/components/bookings/BookingModal.tsx b/src/components/bookings/BookingModal.tsx index bc3d3f8..0f18526 100644 --- a/src/components/bookings/BookingModal.tsx +++ b/src/components/bookings/BookingModal.tsx @@ -8,6 +8,8 @@ import { FloorMapModal } from '../floormap/FloorMapModal' const GUEST_TAGS = ['VIP', 'Постоянный гость', 'Корпоративный', 'Медовый месяц', 'День рождения', 'Особые пожелания'] +const HOURS = Array.from({ length: 24 }, (_, i) => i) + interface BookingModalProps { open: boolean draft: DraftBooking @@ -35,11 +37,22 @@ export function BookingModal({ open, draft, rooms, onClose, onSave, existing }: const [paidAmount, setPaidAmount] = useState(existing?.paidAmount ?? 0) const [showFloorMap, setShowFloorMap] = useState(false) + // Hourly booking state + const [isHourly, setIsHourly] = useState(false) + const [hourlyDate, setHourlyDate] = useState(draft.checkIn) + const [startHour, setStartHour] = useState(10) + const [endHour, setEndHour] = useState(12) + const room = rooms.find(r => r.id === form.roomId) const nights = form.checkIn && form.checkOut ? Math.max(0, (new Date(form.checkOut).getTime() - new Date(form.checkIn).getTime()) / 86400000) : 0 - const total = (room?.baseRate ?? 0) * nights + + const hourlyHours = Math.max(0, endHour - startHour) + const total = isHourly && room?.allowHourly + ? (room.hourlyRate ?? 0) * hourlyHours + : (room?.baseRate ?? 0) * nights + const debt = Math.max(0, total - paidAmount) const isFutureBooking = form.checkIn > format(new Date(), 'yyyy-MM-dd') @@ -50,9 +63,25 @@ export function BookingModal({ open, draft, rooms, onClose, onSave, existing }: setGuestTags(prev => prev.includes(tag) ? prev.filter(t => t !== tag) : [...prev, tag]) const handleSave = () => { - if (!form.guestName || !form.checkIn || !form.checkOut) return + if (!form.guestName) return + if (isHourly && room?.allowHourly) { + if (!hourlyDate) return + } else { + if (!form.checkIn || !form.checkOut) return + } + + const hourlyPrefix = isHourly && room?.allowHourly + ? `[Почасово: ${String(startHour).padStart(2, '0')}:00–${String(endHour).padStart(2, '0')}:00] ` + : '' + + const checkIn = isHourly && room?.allowHourly ? hourlyDate : form.checkIn + const checkOut = isHourly && room?.allowHourly ? hourlyDate : form.checkOut + onSave({ ...form, + checkIn, + checkOut, + notes: hourlyPrefix + form.notes, totalAmount: total, paidAmount, id: existing?.id ?? `b-${Date.now()}`, @@ -62,13 +91,15 @@ export function BookingModal({ open, draft, rooms, onClose, onSave, existing }: }) } + const showHourlyTab = room?.allowHourly === true + return ( <> @@ -78,284 +109,374 @@ export function BookingModal({ open, draft, rooms, onClose, onSave, existing }: } > -
- {/* Room */} -
-
- - -
- -
- - {/* Guest */} -
+
+ {/* ── LEFT COLUMN ── */} +
+ {/* Room */}
- - set('guestName', e.target.value)} - /> -
-
- - set('guestEmail', e.target.value)} - /> -
-
- - {/* Guest tags */} -
- -
- {GUEST_TAGS.map(tag => { - const isVip = tag === 'VIP' - const selected = guestTags.includes(tag) - return ( - - ) - })} -
-
- - {/* Dates */} -
-
- - set('checkIn', e.target.value)} - /> -
-
- - set('checkOut', e.target.value)} - /> -
-
- - {/* Guests count */} -
-
- - set('adults', parseInt(e.target.value) || 1)} - /> -
-
- - set('children', parseInt(e.target.value) || 0)} - /> -
-
- +
+ + +
-
- {/* Source */} -
- -
- {(Object.entries(SOURCE_LABELS) as [BookingSource, string][]).map(([k, v]) => ( + {/* Guest name + email */} +
+
+ + set('guestName', e.target.value)} + /> +
+
+ + set('guestEmail', e.target.value)} + /> +
+
+ + {/* Hourly / Daily toggle */} + {showHourlyTab && ( +
+ - ))} -
-
- - {/* Payment */} - {nights > 0 && total > 0 && ( -
-
-

Оплата

-
- {/* Payment method */} -
- -
- - -
-
+ )} - {/* Amount paid */} + {/* Dates */} + {isHourly && room?.allowHourly ? ( +
-
+
+
+ + +
+
+ + +
+
+
+ ) : ( +
+
+ + set('checkIn', e.target.value)} + /> +
+
+ + set('checkOut', e.target.value)} />
-
- )} + )} - {/* Notes */} -
- -