Rental: connect to API, improve cell UI; remove price from room labels
Backend: - Migration 015_rental.sql: rental_objects + rental_bookings tables, seed demo data - New routes: GET/POST/PATCH/DELETE rental-objects and rental-bookings per hotel - Register rentalRoutes in app.ts Frontend: - api.ts: add rental.listObjects, listBookings, createBooking, updateBooking, deleteBooking - CalendarPage: fetch rental objects + bookings from API instead of mock data - BookingCalendar: rental cell UI redesigned — colored squares + working hours + time slots (matches design spec) - BookingCalendar: remove base rate price from desktop room label column (irrelevant with dynamic pricing) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -443,11 +443,6 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{!compact && !isMobile && (
|
||||
<div className="ml-auto text-xs text-slate-400 dark:text-slate-500">
|
||||
{room.baseRate.toLocaleString('ru-RU')} ₽
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Day cells + booking blocks */}
|
||||
@@ -693,42 +688,34 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
|
||||
{hasFullDay ? (
|
||||
/* Full day booking */
|
||||
<div className={cn(
|
||||
'absolute inset-1.5 rounded flex items-center justify-center text-white text-[10px] font-medium',
|
||||
'absolute inset-1.5 rounded flex items-center justify-center text-white text-[10px] font-semibold',
|
||||
obj.color,
|
||||
)}>
|
||||
Весь день
|
||||
</div>
|
||||
) : dayBookings.length > 0 ? (
|
||||
/* Hourly booking bars */
|
||||
<div className="absolute inset-x-1 bottom-1" style={{ top: 6 }}>
|
||||
{/* Time scale bar (background) */}
|
||||
<div className="w-full h-2.5 rounded-sm bg-slate-100 dark:bg-slate-600 relative overflow-hidden">
|
||||
{dayBookings.map(b => {
|
||||
const leftPct = ((b.startHour - obj.openHour) / totalSpan) * 100
|
||||
const widthPct = ((b.endHour - b.startHour) / totalSpan) * 100
|
||||
return (
|
||||
<div
|
||||
key={b.id}
|
||||
className={cn('absolute h-full rounded-sm', obj.color)}
|
||||
style={{ left: `${leftPct}%`, width: `${widthPct}%` }}
|
||||
title={`${b.guestName} · ${b.startHour}:00–${b.endHour}:00`}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{/* Hour labels */}
|
||||
<div className="flex justify-between mt-0.5 px-0.5">
|
||||
<span className="text-[9px] text-slate-400">{obj.openHour}:00</span>
|
||||
<span className="text-[9px] text-slate-400">{obj.closeHour}:00</span>
|
||||
</div>
|
||||
{/* Booking count */}
|
||||
<div className="flex flex-wrap gap-0.5 mt-1">
|
||||
/* Hourly bookings — squares + times */
|
||||
<div className="absolute inset-x-1 top-1.5 flex flex-col gap-0.5">
|
||||
{/* One colored square per booking */}
|
||||
<div className="flex gap-0.5 flex-wrap">
|
||||
{dayBookings.map(b => (
|
||||
<span key={b.id} className="text-[9px] text-slate-500 dark:text-slate-400 bg-slate-100 dark:bg-slate-700 px-1 rounded truncate max-w-full">
|
||||
{b.startHour}–{b.endHour}
|
||||
</span>
|
||||
<div
|
||||
key={b.id}
|
||||
className={cn('w-3 h-3 rounded-[3px]', obj.color)}
|
||||
title={`${b.guestName} · ${b.startHour}:00–${b.endHour}:00`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{/* Working hours range */}
|
||||
<span className="text-[9px] text-slate-400 dark:text-slate-500 leading-none">
|
||||
{obj.openHour}:00–{obj.closeHour}:00
|
||||
</span>
|
||||
{/* Booked time slots */}
|
||||
{dayBookings.map(b => (
|
||||
<span key={b.id} className="text-[9px] text-slate-600 dark:text-slate-300 font-medium leading-none">
|
||||
{b.startHour}–{b.endHour}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
/* Empty — show "+" hint on hover */
|
||||
|
||||
Reference in New Issue
Block a user