Fix mobile responsiveness across all pages
- BookingsPage: hide Номер/Выезд/Источник columns on small screens, show nights inline on mobile - GuestsPage: stats grid 2→4 cols (grid-cols-2 lg:grid-cols-4), table columns hidden on mobile (md/lg breakpoints), GuestModal stats 2→4 cols on sm+ - BookingDetailPanel: full-width on mobile (w-full sm:w-[400px]) - Topbar: notifications panel full-width on mobile (w-[calc(100vw-1rem)] sm:w-96) - LoyaltyPage, MaintenancePage, DiscountsPage, TariffsPage: stats grids grid-cols-2 lg:grid-cols-4 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -171,7 +171,7 @@ export function BookingDetailPanel({ booking, room, rooms, allBookings, onClose,
|
|||||||
<>
|
<>
|
||||||
<div className="fixed inset-0 z-40" onClick={onClose} />
|
<div className="fixed inset-0 z-40" onClick={onClose} />
|
||||||
|
|
||||||
<div className="fixed right-0 top-0 bottom-0 z-50 w-[400px] bg-white dark:bg-slate-800 border-l border-slate-200 dark:border-slate-700 shadow-2xl flex flex-col">
|
<div className="fixed right-0 top-0 bottom-0 z-50 w-full sm:w-[400px] bg-white dark:bg-slate-800 border-l border-slate-200 dark:border-slate-700 shadow-2xl flex flex-col">
|
||||||
|
|
||||||
{/* Toast notification */}
|
{/* Toast notification */}
|
||||||
{toast && (
|
{toast && (
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ function NotificationsPanel({ onClose }: { onClose: () => void }) {
|
|||||||
: notifications
|
: notifications
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="absolute right-0 top-full mt-2 w-96 max-w-[calc(100vw-1rem)] bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-2xl shadow-2xl z-20 flex flex-col" style={{ maxHeight: 'calc(100vh - 80px)' }}>
|
<div className="absolute right-0 top-full mt-2 w-[calc(100vw-1rem)] sm:w-96 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-2xl shadow-2xl z-20 flex flex-col" style={{ maxHeight: 'calc(100vh - 80px)' }}>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between px-4 py-3 border-b border-slate-100 dark:border-slate-700 shrink-0">
|
<div className="flex items-center justify-between px-4 py-3 border-b border-slate-100 dark:border-slate-700 shrink-0">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
@@ -187,7 +187,11 @@ export function BookingsPage() {
|
|||||||
{COLUMNS.map(col => (
|
{COLUMNS.map(col => (
|
||||||
<th
|
<th
|
||||||
key={col.label}
|
key={col.label}
|
||||||
className="text-left px-4 py-3 text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide"
|
className={cn(
|
||||||
|
'text-left px-4 py-3 text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide',
|
||||||
|
(col.label === 'Выезд' || col.label === 'Источник') && 'hidden sm:table-cell',
|
||||||
|
col.label === 'Номер' && 'hidden md:table-cell',
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{col.key ? (
|
{col.key ? (
|
||||||
<button
|
<button
|
||||||
@@ -221,7 +225,7 @@ export function BookingsPage() {
|
|||||||
<p className="font-medium text-slate-900 dark:text-slate-100">{b.guestName}</p>
|
<p className="font-medium text-slate-900 dark:text-slate-100">{b.guestName}</p>
|
||||||
<p className="text-xs text-slate-500 dark:text-slate-400">{b.guestEmail}</p>
|
<p className="text-xs text-slate-500 dark:text-slate-400">{b.guestEmail}</p>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3 hidden md:table-cell">
|
||||||
<p className="font-medium text-slate-900 dark:text-slate-100">
|
<p className="font-medium text-slate-900 dark:text-slate-100">
|
||||||
{r ? `№${r.number}` : '—'}
|
{r ? `№${r.number}` : '—'}
|
||||||
</p>
|
</p>
|
||||||
@@ -229,8 +233,9 @@ export function BookingsPage() {
|
|||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3 text-slate-700 dark:text-slate-300">
|
<td className="px-4 py-3 text-slate-700 dark:text-slate-300">
|
||||||
{new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'short' }).format(new Date(b.checkIn))}
|
{new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'short' }).format(new Date(b.checkIn))}
|
||||||
|
<span className="ml-1 text-xs text-slate-400 sm:hidden">· {nights}н</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3 text-slate-700 dark:text-slate-300">
|
<td className="px-4 py-3 text-slate-700 dark:text-slate-300 hidden sm:table-cell">
|
||||||
<span>{new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'short' }).format(new Date(b.checkOut))}</span>
|
<span>{new Intl.DateTimeFormat('ru-RU', { day: 'numeric', month: 'short' }).format(new Date(b.checkOut))}</span>
|
||||||
<span className="ml-1.5 text-xs text-slate-400">{nights}н</span>
|
<span className="ml-1.5 text-xs text-slate-400">{nights}н</span>
|
||||||
</td>
|
</td>
|
||||||
@@ -239,7 +244,7 @@ export function BookingsPage() {
|
|||||||
{BOOKING_STATUS_LABELS[b.status]}
|
{BOOKING_STATUS_LABELS[b.status]}
|
||||||
</Badge>
|
</Badge>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3 hidden sm:table-cell">
|
||||||
<Badge className={SOURCE_COLORS[b.source]}>
|
<Badge className={SOURCE_COLORS[b.source]}>
|
||||||
{SOURCE_LABELS[b.source]}
|
{SOURCE_LABELS[b.source]}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ export function DiscountsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
<div className="grid grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
|
||||||
{[
|
{[
|
||||||
{ label: 'Всего скидок', value: discounts.length },
|
{ label: 'Всего скидок', value: discounts.length },
|
||||||
{ label: 'Активных', value: discounts.filter(d => d.isActive).length },
|
{ label: 'Активных', value: discounts.filter(d => d.isActive).length },
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ function GuestModal({ guest, onClose }: { guest: Guest; onClose: () => void }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats strip */}
|
{/* Stats strip */}
|
||||||
<div className="grid grid-cols-4 divide-x divide-slate-200 dark:divide-slate-700 border-b border-slate-200 dark:border-slate-700">
|
<div className="grid grid-cols-2 sm:grid-cols-4 divide-x divide-slate-200 dark:divide-slate-700 border-b border-slate-200 dark:border-slate-700">
|
||||||
{[
|
{[
|
||||||
{ label: 'Визитов', value: guest.totalStays },
|
{ label: 'Визитов', value: guest.totalStays },
|
||||||
{ label: 'Потрачено', value: `${guest.totalSpent.toLocaleString('ru-RU')} ₽` },
|
{ label: 'Потрачено', value: `${guest.totalSpent.toLocaleString('ru-RU')} ₽` },
|
||||||
@@ -588,14 +588,14 @@ export function GuestsPage() {
|
|||||||
{pageTab === 'list' && <>
|
{pageTab === 'list' && <>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
<div className="grid grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
|
||||||
{[
|
{[
|
||||||
{ icon: Users, label: 'Всего гостей', value: MOCK_GUESTS.length, color: 'text-brand-600 dark:text-brand-400', bg: 'bg-brand-50 dark:bg-brand-900/20' },
|
{ icon: Users, label: 'Всего гостей', value: MOCK_GUESTS.length, color: 'text-brand-600 dark:text-brand-400', bg: 'bg-brand-50 dark:bg-brand-900/20' },
|
||||||
{ icon: Award, label: 'VIP гостей', value: vipCount, color: 'text-amber-600 dark:text-amber-400', bg: 'bg-amber-50 dark:bg-amber-900/20' },
|
{ icon: Award, label: 'VIP гостей', value: vipCount, color: 'text-amber-600 dark:text-amber-400', bg: 'bg-amber-50 dark:bg-amber-900/20' },
|
||||||
{ icon: Repeat2, label: 'Постоянных', value: repeatCount, color: 'text-blue-600 dark:text-blue-400', bg: 'bg-blue-50 dark:bg-blue-900/20' },
|
{ icon: Repeat2, label: 'Постоянных', value: repeatCount, color: 'text-blue-600 dark:text-blue-400', bg: 'bg-blue-50 dark:bg-blue-900/20' },
|
||||||
{ icon: Star, label: 'Средний рейтинг',value: avgRating, color: 'text-emerald-600 dark:text-emerald-400', bg: 'bg-emerald-50 dark:bg-emerald-900/20' },
|
{ icon: Star, label: 'Средний рейтинг',value: avgRating, color: 'text-emerald-600 dark:text-emerald-400', bg: 'bg-emerald-50 dark:bg-emerald-900/20' },
|
||||||
].map(s => (
|
].map(s => (
|
||||||
<div key={s.label} className="card p-4 flex items-center gap-4">
|
<div key={s.label} className="card p-4 flex items-center gap-3">
|
||||||
<div className={cn('w-10 h-10 rounded-xl flex items-center justify-center shrink-0', s.bg)}>
|
<div className={cn('w-10 h-10 rounded-xl flex items-center justify-center shrink-0', s.bg)}>
|
||||||
<s.icon size={20} className={s.color} />
|
<s.icon size={20} className={s.color} />
|
||||||
</div>
|
</div>
|
||||||
@@ -646,12 +646,12 @@ export function GuestsPage() {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr className="border-b border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-700/50">
|
<tr className="border-b border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-700/50">
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide">Гость</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide">Гость</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide">Контакты</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide hidden md:table-cell">Контакты</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide">Визиты</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide hidden sm:table-cell">Визиты</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide">Потрачено</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide hidden lg:table-cell">Потрачено</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide">Рейтинг</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide">Рейтинг</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide">Последний визит</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide hidden lg:table-cell">Последний визит</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide">Метки</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wide hidden sm:table-cell">Метки</th>
|
||||||
<th className="px-4 py-3" />
|
<th className="px-4 py-3" />
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -679,7 +679,7 @@ export function GuestsPage() {
|
|||||||
<span className="text-sm font-medium text-slate-900 dark:text-slate-100">{guest.name}</span>
|
<span className="text-sm font-medium text-slate-900 dark:text-slate-100">{guest.name}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3 hidden md:table-cell">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
{guest.phone && (
|
{guest.phone && (
|
||||||
<div className="flex items-center gap-1 text-xs text-slate-500 dark:text-slate-400">
|
<div className="flex items-center gap-1 text-xs text-slate-500 dark:text-slate-400">
|
||||||
@@ -693,13 +693,13 @@ export function GuestsPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3 hidden sm:table-cell">
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<TrendingUp size={14} className="text-slate-400" />
|
<TrendingUp size={14} className="text-slate-400" />
|
||||||
<span className="text-sm font-medium text-slate-900 dark:text-slate-100">{guest.totalStays}</span>
|
<span className="text-sm font-medium text-slate-900 dark:text-slate-100">{guest.totalStays}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3 hidden lg:table-cell">
|
||||||
<span className="text-sm font-medium text-slate-900 dark:text-slate-100">
|
<span className="text-sm font-medium text-slate-900 dark:text-slate-100">
|
||||||
{guest.totalSpent.toLocaleString('ru-RU')} ₽
|
{guest.totalSpent.toLocaleString('ru-RU')} ₽
|
||||||
</span>
|
</span>
|
||||||
@@ -707,10 +707,10 @@ export function GuestsPage() {
|
|||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3">
|
||||||
<StarRating value={guest.rating} />
|
<StarRating value={guest.rating} />
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3 hidden lg:table-cell">
|
||||||
<span className="text-sm text-slate-600 dark:text-slate-400">{formatDate(guest.lastVisit)}</span>
|
<span className="text-sm text-slate-600 dark:text-slate-400">{formatDate(guest.lastVisit)}</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3">
|
<td className="px-4 py-3 hidden sm:table-cell">
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{guest.tags.map(tag => (
|
{guest.tags.map(tag => (
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -837,7 +837,7 @@ export function LoyaltyPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
<div className="grid grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
|
||||||
{[
|
{[
|
||||||
{ label: 'Участников программы', value: activeGuests, icon: User, color: 'text-brand-600 dark:text-brand-400', bg: 'bg-brand-50 dark:bg-brand-900/20' },
|
{ label: 'Участников программы', value: activeGuests, icon: User, color: 'text-brand-600 dark:text-brand-400', bg: 'bg-brand-50 dark:bg-brand-900/20' },
|
||||||
{ label: 'Всего баллов в обороте', value: totalPoints.toLocaleString('ru-RU'), icon: TrendingUp, color: 'text-amber-600 dark:text-amber-400', bg: 'bg-amber-50 dark:bg-amber-900/20' },
|
{ label: 'Всего баллов в обороте', value: totalPoints.toLocaleString('ru-RU'), icon: TrendingUp, color: 'text-amber-600 dark:text-amber-400', bg: 'bg-amber-50 dark:bg-amber-900/20' },
|
||||||
|
|||||||
@@ -378,7 +378,7 @@ export function MaintenancePage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
<div className="grid grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
|
||||||
{[
|
{[
|
||||||
{ label: 'Всего записей', value: records.length, cls: '' },
|
{ label: 'Всего записей', value: records.length, cls: '' },
|
||||||
{ label: 'Активных', value: active.length, cls: 'text-amber-600 dark:text-amber-400' },
|
{ label: 'Активных', value: active.length, cls: 'text-amber-600 dark:text-amber-400' },
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ export function TariffsPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stats */}
|
{/* Stats */}
|
||||||
<div className="grid grid-cols-4 gap-4">
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
|
||||||
{[
|
{[
|
||||||
{ label: 'Всего тарифов', value: tariffs.length, color: 'text-slate-900 dark:text-slate-100' },
|
{ label: 'Всего тарифов', value: tariffs.length, color: 'text-slate-900 dark:text-slate-100' },
|
||||||
{ label: 'Активных', value: activeTariffs.length, color: 'text-emerald-600 dark:text-emerald-400' },
|
{ label: 'Активных', value: activeTariffs.length, color: 'text-emerald-600 dark:text-emerald-400' },
|
||||||
|
|||||||
Reference in New Issue
Block a user