Calendar: compact mobile layout — narrow left column, hide View btn, move + to month row

- Left column: 80px on mobile (was 160px), hide room name/price/type badge
- Category headers: truncate text, reduce padding on mobile
- Toolbar: hide "Вид" button on mobile (sm:hidden)
- Toolbar: add compact "+" button next to month label on mobile only
- Original "+" button hidden on mobile (hidden sm:inline-flex)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 14:52:32 +03:00
parent 7bd0e038ba
commit 3f4d0dac4c

View File

@@ -98,6 +98,15 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
)
const rowHeight = compact ? ROW_HEIGHT_COMPACT : ROW_HEIGHT
// Mobile detection
const [isMobile, setIsMobile] = useState(() => window.innerWidth < 640)
useEffect(() => {
const h = () => setIsMobile(window.innerWidth < 640)
window.addEventListener('resize', h)
return () => window.removeEventListener('resize', h)
}, [])
const labelWidth = isMobile ? 80 : LABEL_WIDTH
// Modals
const [bookingModalDraft, setBookingModalDraft] = useState<DraftBooking | null>(null)
const [selectedBooking, setSelectedBooking] = useState<Booking | null>(null)
@@ -264,9 +273,22 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
<button onClick={() => shiftDays(7)} className="btn-ghost p-2"><ChevronRight size={16} /></button>
</div>
<span className="text-sm font-medium text-slate-700 dark:text-slate-300 capitalize">
{format(startDate, 'LLLL yyyy', { locale: ru })}
</span>
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-slate-700 dark:text-slate-300 capitalize">
{format(startDate, 'LLLL yyyy', { locale: ru })}
</span>
{/* Mobile-only + button next to month name */}
<button
className="sm:hidden btn-primary p-1.5"
onClick={() => {
const today = format(new Date(), 'yyyy-MM-dd')
const tomorrow = format(addDays(new Date(), 1), 'yyyy-MM-dd')
setBookingModalDraft({ roomId: rooms[0]?.id ?? '', checkIn: today, checkOut: tomorrow })
}}
>
<Plus size={15} />
</button>
</div>
<div className="flex-1" />
@@ -298,7 +320,7 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
onClick={() => setCompact(v => { const next = !v; localStorage.setItem('calendarCompact', String(next)); return next })}
title={compact ? 'Обычный вид' : 'Компактный вид'}
className={cn(
'btn-ghost flex items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium',
'hidden sm:flex btn-ghost items-center gap-1.5 px-2.5 py-1.5 text-xs font-medium',
compact && 'bg-brand-50 dark:bg-brand-900/20 text-brand-600 dark:text-brand-400',
)}
>
@@ -312,7 +334,7 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
const tomorrow = format(addDays(new Date(), 1), 'yyyy-MM-dd')
setBookingModalDraft({ roomId: rooms[0]?.id ?? '', checkIn: today, checkOut: tomorrow })
}}
className="btn-primary"
className="hidden sm:inline-flex btn-primary"
>
<Plus size={15} />
<span className="hidden sm:inline">Новое бронирование</span>
@@ -321,7 +343,7 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
{/* Grid */}
<div className="flex-1 overflow-auto" ref={gridRef}>
<div style={{ minWidth: LABEL_WIDTH + visibleDays * CELL_WIDTH }}>
<div style={{ minWidth: labelWidth + visibleDays * CELL_WIDTH }}>
{/* Date header row */}
<div
@@ -330,7 +352,7 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
>
<div
className="shrink-0 sticky left-0 z-30 bg-white dark:bg-slate-800 border-r border-slate-200 dark:border-slate-700 flex items-center px-3"
style={{ width: LABEL_WIDTH }}
style={{ width: labelWidth }}
>
<span className="text-xs font-semibold text-slate-500 uppercase tracking-wide">Номер</span>
</div>
@@ -379,10 +401,10 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
style={{ height: 28 }}
>
<div
className="shrink-0 sticky left-0 z-10 bg-slate-50 dark:bg-slate-700/40 border-r border-slate-200 dark:border-slate-600 flex items-center px-4"
style={{ width: LABEL_WIDTH }}
className="shrink-0 sticky left-0 z-10 bg-slate-50 dark:bg-slate-700/40 border-r border-slate-200 dark:border-slate-600 flex items-center px-2 overflow-hidden"
style={{ width: labelWidth }}
>
<span className={cn('text-xs font-semibold uppercase tracking-wide', getRoomTypeColor(room.type).split(' ').filter(c => c.startsWith('text-')).join(' '))}>
<span className={cn('text-xs font-semibold uppercase tracking-wide truncate', getRoomTypeColor(room.type).split(' ').filter(c => c.startsWith('text-')).join(' '))}>
{room.type}
</span>
</div>
@@ -407,21 +429,21 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
>
{/* Room label */}
<div
className="shrink-0 sticky left-0 z-10 bg-white dark:bg-slate-800 border-r border-slate-200 dark:border-slate-700 flex items-center gap-2 px-3 group-hover:bg-slate-50 dark:group-hover:bg-slate-800"
style={{ width: LABEL_WIDTH }}
className={cn('shrink-0 sticky left-0 z-10 bg-white dark:bg-slate-800 border-r border-slate-200 dark:border-slate-700 flex items-center gap-2 group-hover:bg-slate-50 dark:group-hover:bg-slate-800', isMobile ? 'px-2' : 'px-3')}
style={{ width: labelWidth }}
>
<div>
<div className="flex items-center gap-1.5">
<span className="text-sm font-bold text-slate-900 dark:text-slate-100">{room.number}</span>
{room.name && <span className="text-xs text-slate-500 dark:text-slate-400">{room.name}</span>}
{room.name && !isMobile && <span className="text-xs text-slate-500 dark:text-slate-400">{room.name}</span>}
</div>
{!compact && (
{!compact && !isMobile && (
<span className={cn('text-xs px-1.5 py-0.5 rounded-md font-medium', getRoomTypeColor(room.type))}>
{room.type}
</span>
)}
</div>
{!compact && (
{!compact && !isMobile && (
<div className="ml-auto text-xs text-slate-400 dark:text-slate-500">
{room.baseRate.toLocaleString('ru-RU')}
</div>
@@ -612,7 +634,7 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
style={{ height: 32 }}>
<div
className="shrink-0 sticky left-0 z-10 bg-slate-100 dark:bg-slate-700/60 border-r border-slate-200 dark:border-slate-600 flex items-center px-4"
style={{ width: LABEL_WIDTH }}
style={{ width: labelWidth }}
>
<span className="text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide">
Аренда объектов
@@ -635,7 +657,7 @@ export function BookingCalendar({ rooms, bookings, slug, onBookingCreate, onBook
{/* Label */}
<div
className="shrink-0 sticky left-0 z-10 bg-white dark:bg-slate-800 border-r border-slate-200 dark:border-slate-700 flex items-center gap-2 px-3"
style={{ width: LABEL_WIDTH }}
style={{ width: labelWidth }}
>
<span className="text-base leading-none shrink-0">{obj.icon}</span>
<div className="min-w-0">