Add floor map, rental module wiring, VIP tags and payment tracking in BookingModal

- New: Поэтажный план (/floor-map) — room status visualization per floor with color coding
  (occupied/arriving/departing/available/dirty/maintenance), click free room to create booking
- New: FloorMapModal — embeddable in BookingModal via "Поэтажный план" button near room selector
- New: RentalBookingModal — full-day toggle, hour start/end selects, conflict detection, price summary
- CalendarPage: rental objects/bookings shown in шахматка when rental module is active
- BookingsPage: "Аренда" tab with rental bookings table when rental module is active
- BookingModal: guest tags (VIP, Постоянный гость, etc.), payment method (cash/terminal),
  paidAmount input, debt/задолженность display, floor map quick-access button
- Sidebar: "План этажей" nav link added under Управление

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 17:08:10 +03:00
parent 74e0223c3c
commit 57c22be60d
11 changed files with 1432 additions and 297 deletions

134
src/data/rentalData.ts Normal file
View File

@@ -0,0 +1,134 @@
import { addDays, format } from 'date-fns'
export interface RentalObject {
id: string
name: string
icon: string
color: string // tailwind bg class
textColor: string // tailwind text class
pricePerHour: number
pricePerDay: number
openHour: number // 8
closeHour: number // 22
maxHoursPerSlot?: number
}
export interface RentalBooking {
id: string
objectId: string
date: string // 'yyyy-MM-dd'
isFullDay: boolean
startHour: number // 10 (ignored if isFullDay)
endHour: number // 12 (ignored if isFullDay)
guestName: string
guestPhone: string
linkedRoomId?: string
totalAmount: number
status: 'confirmed' | 'cancelled'
notes?: string
}
export const RENTAL_OBJECTS: RentalObject[] = [
{
id: 'court',
name: 'Теннисный корт',
icon: '🎾',
color: 'bg-green-500',
textColor: 'text-green-700 dark:text-green-400',
pricePerHour: 1500,
pricePerDay: 8000,
openHour: 8,
closeHour: 22,
},
{
id: 'sauna',
name: 'Баня',
icon: '🛁',
color: 'bg-orange-500',
textColor: 'text-orange-700 dark:text-orange-400',
pricePerHour: 2500,
pricePerDay: 12000,
openHour: 10,
closeHour: 23,
maxHoursPerSlot: 4,
},
{
id: 'hall',
name: 'Конференц-зал',
icon: '🏛️',
color: 'bg-violet-500',
textColor: 'text-violet-700 dark:text-violet-400',
pricePerHour: 3000,
pricePerDay: 15000,
openHour: 9,
closeHour: 20,
},
]
function todayPlus(days: number): string {
return format(addDays(new Date(), days), 'yyyy-MM-dd')
}
export const MOCK_RENTAL_BOOKINGS: RentalBooking[] = [
{
id: 'rb1',
objectId: 'court',
date: todayPlus(0),
isFullDay: false,
startHour: 10,
endHour: 12,
guestName: 'Иванов А.П.',
guestPhone: '+7 999 111 22 33',
totalAmount: 3000,
status: 'confirmed',
},
{
id: 'rb2',
objectId: 'court',
date: todayPlus(0),
isFullDay: false,
startHour: 15,
endHour: 17,
guestName: 'Петрова М.С.',
guestPhone: '+7 912 345 67 89',
totalAmount: 3000,
status: 'confirmed',
},
{
id: 'rb3',
objectId: 'sauna',
date: todayPlus(1),
isFullDay: false,
startHour: 18,
endHour: 21,
guestName: 'Сидоров К.В.',
guestPhone: '+7 926 555 44 33',
linkedRoomId: 'r2',
totalAmount: 7500,
status: 'confirmed',
},
{
id: 'rb4',
objectId: 'hall',
date: todayPlus(2),
isFullDay: true,
startHour: 9,
endHour: 20,
guestName: 'ООО "Конференция"',
guestPhone: '+7 495 000 11 22',
totalAmount: 15000,
status: 'confirmed',
},
{
id: 'rb5',
objectId: 'court',
date: todayPlus(3),
isFullDay: false,
startHour: 9,
endHour: 11,
guestName: 'Козлов Д.Р.',
guestPhone: '+7 900 222 33 44',
totalAmount: 3000,
status: 'confirmed',
},
]