feat: show occupied slots in new rental step-1 modal
This commit is contained in:
@@ -199,6 +199,17 @@ export function BookingsPage() {
|
||||
)
|
||||
}
|
||||
|
||||
// Step-1 modal: occupied slots for selected object + date
|
||||
const pickObj = rentalObjects.find(o => o.id === rentalPickObj)
|
||||
const pickBookings = rentalBookings.filter(
|
||||
b => b.objectId === rentalPickObj && b.date === rentalPickDate && b.status === 'confirmed',
|
||||
)
|
||||
const pickOpenMin = (pickObj?.openHour ?? 8) * 60
|
||||
const pickCloseMin = (pickObj?.closeHour ?? 22) * 60
|
||||
const pickTotalMin = pickCloseMin - pickOpenMin
|
||||
const pickHasFullDay = pickBookings.some(b => b.isFullDay)
|
||||
const fmtPickMin = (m: number) => `${Math.floor(m / 60)}:${String(m % 60).padStart(2, '0')}`
|
||||
|
||||
return (
|
||||
<div className="p-4 md:p-6 space-y-4">
|
||||
{/* Header */}
|
||||
@@ -506,6 +517,63 @@ export function BookingsPage() {
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">Дата</label>
|
||||
<input type="date" className="input" value={rentalPickDate} onChange={e => setRentalPickDate(e.target.value)} />
|
||||
</div>
|
||||
|
||||
{/* Occupied slots preview */}
|
||||
{pickObj && (
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wide">
|
||||
Занятость · {new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'long' }).format(new Date(rentalPickDate))}
|
||||
</p>
|
||||
{pickHasFullDay ? (
|
||||
<div className="flex items-center gap-2 px-3 py-2 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-700">
|
||||
<span className="w-2 h-2 rounded-full bg-red-500 shrink-0" />
|
||||
<span className="text-sm text-red-700 dark:text-red-300 font-medium">Весь день занят</span>
|
||||
</div>
|
||||
) : pickBookings.length === 0 ? (
|
||||
<div className="flex items-center gap-2 px-3 py-2 rounded-lg bg-emerald-50 dark:bg-emerald-900/20 border border-emerald-200 dark:border-emerald-700">
|
||||
<span className="w-2 h-2 rounded-full bg-emerald-500 shrink-0" />
|
||||
<span className="text-sm text-emerald-700 dark:text-emerald-300 font-medium">Свободно весь день</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{/* Timeline bar */}
|
||||
<div className="relative h-5 rounded-md overflow-hidden bg-emerald-100 dark:bg-emerald-900/20">
|
||||
{pickBookings.filter(b => !b.isFullDay).map(b => {
|
||||
const left = ((b.startHour - pickOpenMin) / pickTotalMin) * 100
|
||||
const width = ((b.endHour - b.startHour) / pickTotalMin) * 100
|
||||
return (
|
||||
<div
|
||||
key={b.id}
|
||||
className="absolute top-0 h-full bg-red-400 dark:bg-red-600 opacity-80"
|
||||
style={{ left: `${Math.max(0, left)}%`, width: `${Math.min(100 - Math.max(0, left), width)}%` }}
|
||||
title={`${b.guestName} ${fmtPickMin(b.startHour)}–${fmtPickMin(b.endHour)}`}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{/* Hour labels */}
|
||||
<div className="flex justify-between text-[10px] text-slate-400 dark:text-slate-500">
|
||||
<span>{fmtPickMin(pickOpenMin)}</span>
|
||||
<span>{fmtPickMin(Math.round((pickOpenMin + pickCloseMin) / 2))}</span>
|
||||
<span>{fmtPickMin(pickCloseMin)}</span>
|
||||
</div>
|
||||
{/* Booking list */}
|
||||
<div className="space-y-1 max-h-24 overflow-y-auto">
|
||||
{pickBookings.filter(b => !b.isFullDay).map(b => (
|
||||
<div key={b.id} className="flex items-center gap-2 text-xs">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-red-400 shrink-0" />
|
||||
<span className="font-medium text-slate-700 dark:text-slate-300 tabular-nums">
|
||||
{fmtPickMin(b.startHour)}–{fmtPickMin(b.endHour)}
|
||||
</span>
|
||||
<span className="text-slate-400 dark:text-slate-500 truncate">{b.guestName}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-2 justify-end">
|
||||
<button onClick={() => setNewRentalStep('idle')} className="btn-secondary">Отмена</button>
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user