Fix mobile UI bugs from screenshots
- Notifications panel: fixed positioning on mobile (full-width below topbar, no more overflow behind calendar) - BookingModal: two-column layout stacks vertically on mobile (flex-col sm:flex-row) - TariffsPage cards: price + actions merged into right column, no text overlap - DiscountsPage cards: same fix, price text reduced to text-lg - ReviewsPage: action buttons moved inside content div (no longer overlap guest name) - RoomCategoriesPage: edit/delete buttons stack as column, 'Номера' text hidden on mobile - PosPage: show desktop-only message on mobile instead of broken layout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -223,7 +223,7 @@ export function BookingModal({ open, draft, rooms, onClose, onSave, existing }:
|
||||
{/* ── BOOKING TAB ── */}
|
||||
{activeTab === 'booking' && (
|
||||
<div className="space-y-4">
|
||||
<div className="flex gap-6">
|
||||
<div className="flex flex-col sm:flex-row gap-4 sm:gap-6">
|
||||
{/* ── LEFT COLUMN ── */}
|
||||
<div className="flex-1 space-y-3 min-w-0">
|
||||
{/* Room */}
|
||||
|
||||
@@ -58,7 +58,7 @@ function NotificationsPanel({ onClose }: { onClose: () => void }) {
|
||||
: notifications
|
||||
|
||||
return (
|
||||
<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)' }}>
|
||||
<div className="fixed left-2 right-2 top-16 sm:absolute sm:left-auto sm:right-0 sm:top-full sm:mt-2 sm:w-96 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-2xl shadow-2xl z-50 flex flex-col" style={{ maxHeight: 'calc(100vh - 80px)' }}>
|
||||
{/* 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 gap-2">
|
||||
|
||||
@@ -376,18 +376,15 @@ export function DiscountsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Value badge */}
|
||||
<div className="text-right shrink-0">
|
||||
{/* Value + Actions (right column) */}
|
||||
<div className="shrink-0 flex flex-col items-end gap-2">
|
||||
<p className={cn(
|
||||
'text-xl font-bold',
|
||||
'text-lg font-bold',
|
||||
discount.value === 100 ? 'text-emerald-600 dark:text-emerald-400' : 'text-brand-600 dark:text-brand-400',
|
||||
)}>
|
||||
{discount.value === 100 ? 'Бесплатно' : `−${discount.value}${discount.valueType === 'percent' ? '%' : ' ₽'}`}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => toggleActive(discount.id)}
|
||||
className={cn('p-1.5 rounded-lg transition-colors', discount.isActive ? 'text-brand-600' : 'text-slate-400')}
|
||||
@@ -402,6 +399,7 @@ export function DiscountsPage() {
|
||||
className="p-1.5 rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors">
|
||||
<Trash2 size={15} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -202,6 +202,22 @@ export function PosPage() {
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
|
||||
{/* ── Desktop-only notice on mobile ── */}
|
||||
<div className="sm:hidden flex-1 flex flex-col items-center justify-center gap-4 p-8 text-center">
|
||||
<div className="w-16 h-16 rounded-2xl bg-slate-100 dark:bg-slate-700 flex items-center justify-center">
|
||||
<Receipt size={28} className="text-slate-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-lg font-semibold text-slate-700 dark:text-slate-300">Касса — только десктоп</p>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400 mt-1">
|
||||
Откройте приложение на компьютере или планшете для работы с кассой
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Full POS UI (desktop only) ── */}
|
||||
<div className="hidden sm:flex sm:flex-col sm:flex-1 sm:overflow-hidden">
|
||||
|
||||
{/* ── Top bar ── */}
|
||||
<div className="shrink-0 px-4 py-3 border-b border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800">
|
||||
<div className="flex items-center justify-between flex-wrap gap-2">
|
||||
@@ -875,6 +891,7 @@ export function PosPage() {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>{/* end desktop-only wrapper */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -735,7 +735,7 @@ export function ReviewsPage() {
|
||||
review.status === 'pending' && 'border-red-300 dark:border-red-700/60 bg-red-50/50 dark:bg-red-900/10',
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start gap-3 flex-wrap">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className={cn('w-10 h-10 rounded-full flex items-center justify-center gap-0.5 text-sm font-bold shrink-0', ratingColor(review.rating, threshold))}>
|
||||
{review.rating}<Star size={10} className="fill-current" />
|
||||
</div>
|
||||
@@ -808,7 +808,7 @@ export function ReviewsPage() {
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-2 shrink-0 flex-wrap">
|
||||
<div className="flex gap-2 flex-wrap mt-3">
|
||||
{review.status === 'pending' && !review.reply && replyId !== review.id && (
|
||||
<>
|
||||
<button
|
||||
|
||||
@@ -356,27 +356,27 @@ export function RoomCategoriesPage() {
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
{cat.photos.length > 0 && (
|
||||
<span className="text-xs text-slate-400">{cat.photos.length} фото</span>
|
||||
)}
|
||||
<div className="flex flex-col items-end gap-2 shrink-0">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<button
|
||||
onClick={() => openEdit(cat)}
|
||||
className="p-1.5 rounded-lg border border-slate-200 dark:border-slate-600 hover:border-brand-400 text-slate-500 hover:text-brand-600 transition-colors"
|
||||
>
|
||||
<Pencil size={14} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(cat.id)}
|
||||
className="p-1.5 rounded-lg border border-slate-200 dark:border-slate-600 hover:border-red-400 text-slate-500 hover:text-red-500 transition-colors"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => openEdit(cat)}
|
||||
className="p-2 rounded-lg border border-slate-200 dark:border-slate-600 hover:border-brand-400 text-slate-500 hover:text-brand-600 transition-colors"
|
||||
className="flex items-center gap-1 px-2.5 py-1.5 rounded-lg bg-slate-100 dark:bg-slate-700 text-slate-700 dark:text-slate-300 text-xs font-medium hover:bg-slate-200 dark:hover:bg-slate-600 transition-colors whitespace-nowrap"
|
||||
>
|
||||
<Pencil size={14} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(cat.id)}
|
||||
className="p-2 rounded-lg border border-slate-200 dark:border-slate-600 hover:border-red-400 text-slate-500 hover:text-red-500 transition-colors"
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => openEdit(cat)}
|
||||
className="flex items-center gap-1 px-3 py-1.5 rounded-lg bg-slate-100 dark:bg-slate-700 text-slate-700 dark:text-slate-300 text-xs font-medium hover:bg-slate-200 dark:hover:bg-slate-600 transition-colors"
|
||||
>
|
||||
Номера <ChevronRight size={12} />
|
||||
<span className="hidden sm:inline">Номера</span>
|
||||
<ChevronRight size={12} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -508,40 +508,40 @@ export function TariffsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Price modifier */}
|
||||
<div className="text-right shrink-0">
|
||||
<p className={cn(
|
||||
'text-sm font-semibold',
|
||||
tariff.modifierValue > 0 ? 'text-amber-600 dark:text-amber-400'
|
||||
: tariff.modifierValue < 0 ? 'text-emerald-600 dark:text-emerald-400'
|
||||
: 'text-slate-500',
|
||||
)}>
|
||||
{modLabel}
|
||||
</p>
|
||||
<p className="text-xs text-slate-400">к базовой цене</p>
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<button
|
||||
onClick={() => toggleActive(tariff.id)}
|
||||
className={cn('p-1.5 rounded-lg transition-colors', tariff.isActive ? 'text-brand-600 hover:bg-brand-50' : 'text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-700')}
|
||||
title={tariff.isActive ? 'Деактивировать' : 'Активировать'}
|
||||
>
|
||||
{tariff.isActive ? <ToggleRight size={20} /> : <ToggleLeft size={20} />}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setModal(tariff)}
|
||||
className="p-1.5 rounded-lg text-slate-400 hover:text-brand-600 hover:bg-brand-50 dark:hover:bg-brand-900/20 transition-colors"
|
||||
>
|
||||
<Edit2 size={15} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setDeleteTarget(tariff)}
|
||||
className="p-1.5 rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
|
||||
>
|
||||
<Trash2 size={15} />
|
||||
</button>
|
||||
{/* Price + Actions (right column) */}
|
||||
<div className="shrink-0 flex flex-col items-end gap-2">
|
||||
<div className="text-right">
|
||||
<p className={cn(
|
||||
'text-sm font-semibold',
|
||||
tariff.modifierValue > 0 ? 'text-amber-600 dark:text-amber-400'
|
||||
: tariff.modifierValue < 0 ? 'text-emerald-600 dark:text-emerald-400'
|
||||
: 'text-slate-500',
|
||||
)}>
|
||||
{modLabel}
|
||||
</p>
|
||||
<p className="text-xs text-slate-400">к базе</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => toggleActive(tariff.id)}
|
||||
className={cn('p-1.5 rounded-lg transition-colors', tariff.isActive ? 'text-brand-600 hover:bg-brand-50' : 'text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-700')}
|
||||
title={tariff.isActive ? 'Деактивировать' : 'Активировать'}
|
||||
>
|
||||
{tariff.isActive ? <ToggleRight size={20} /> : <ToggleLeft size={20} />}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setModal(tariff)}
|
||||
className="p-1.5 rounded-lg text-slate-400 hover:text-brand-600 hover:bg-brand-50 dark:hover:bg-brand-900/20 transition-colors"
|
||||
>
|
||||
<Edit2 size={15} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setDeleteTarget(tariff)}
|
||||
className="p-1.5 rounded-lg text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
|
||||
>
|
||||
<Trash2 size={15} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user