BookingModal: move guest fields to right column; Theme: auto mode follows system prefers-color-scheme
This commit is contained in:
@@ -450,7 +450,12 @@ export function BookingModal({ open, draft, rooms, bookings = [], onClose, onSav
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 3. ФИО гостя */}
|
</div>
|
||||||
|
|
||||||
|
{/* ── RIGHT COLUMN ── */}
|
||||||
|
<div className="flex-1 space-y-4 min-w-0">
|
||||||
|
|
||||||
|
{/* Гость */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-semibold text-slate-700 dark:text-slate-300 mb-2">
|
<label className="block text-sm font-semibold text-slate-700 dark:text-slate-300 mb-2">
|
||||||
Гость
|
Гость
|
||||||
@@ -499,45 +504,38 @@ export function BookingModal({ open, draft, rooms, bookings = [], onClose, onSav
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="grid grid-cols-3 gap-2 mt-2">
|
||||||
|
<div>
|
||||||
{/* 4. Взрослые / Дети / Статус */}
|
<label className="block text-xs text-slate-500 dark:text-slate-400 mb-1">Взрослых</label>
|
||||||
<div className="grid grid-cols-3 gap-2">
|
<input
|
||||||
<div>
|
type="number" min={1} max={room?.maxGuests ?? 6} className="input"
|
||||||
<label className="block text-xs text-slate-500 dark:text-slate-400 mb-1">Взрослых</label>
|
value={form.adults}
|
||||||
<input
|
onChange={e => set('adults', parseInt(e.target.value) || 1)}
|
||||||
type="number" min={1} max={room?.maxGuests ?? 6} className="input"
|
/>
|
||||||
value={form.adults}
|
</div>
|
||||||
onChange={e => set('adults', parseInt(e.target.value) || 1)}
|
<div>
|
||||||
/>
|
<label className="block text-xs text-slate-500 dark:text-slate-400 mb-1">Детей</label>
|
||||||
</div>
|
<input
|
||||||
<div>
|
type="number" min={0} max={4} className="input"
|
||||||
<label className="block text-xs text-slate-500 dark:text-slate-400 mb-1">Детей</label>
|
value={form.children}
|
||||||
<input
|
onChange={e => set('children', parseInt(e.target.value) || 0)}
|
||||||
type="number" min={0} max={4} className="input"
|
/>
|
||||||
value={form.children}
|
</div>
|
||||||
onChange={e => set('children', parseInt(e.target.value) || 0)}
|
<div>
|
||||||
/>
|
<label className="block text-xs text-slate-500 dark:text-slate-400 mb-1">Статус</label>
|
||||||
</div>
|
<select
|
||||||
<div>
|
className="input"
|
||||||
<label className="block text-xs text-slate-500 dark:text-slate-400 mb-1">Статус</label>
|
value={form.status}
|
||||||
<select
|
onChange={e => set('status', e.target.value as BookingStatus)}
|
||||||
className="input"
|
>
|
||||||
value={form.status}
|
{(Object.entries(BOOKING_STATUS_LABELS) as [BookingStatus, string][]).map(([k, v]) => (
|
||||||
onChange={e => set('status', e.target.value as BookingStatus)}
|
<option key={k} value={k}>{v}</option>
|
||||||
>
|
))}
|
||||||
{(Object.entries(BOOKING_STATUS_LABELS) as [BookingStatus, string][]).map(([k, v]) => (
|
</select>
|
||||||
<option key={k} value={k}>{v}</option>
|
</div>
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* ── RIGHT COLUMN ── */}
|
|
||||||
<div className="w-56 shrink-0 space-y-4">
|
|
||||||
|
|
||||||
{/* Доп. места */}
|
{/* Доп. места */}
|
||||||
<div className="flex items-center justify-between px-3 py-2.5 rounded-xl bg-slate-50 dark:bg-slate-700/40 border border-slate-200 dark:border-slate-600">
|
<div className="flex items-center justify-between px-3 py-2.5 rounded-xl bg-slate-50 dark:bg-slate-700/40 border border-slate-200 dark:border-slate-600">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ interface ThemeContextValue {
|
|||||||
|
|
||||||
const ThemeContext = createContext<ThemeContextValue | null>(null)
|
const ThemeContext = createContext<ThemeContextValue | null>(null)
|
||||||
|
|
||||||
function getAutoTheme(): Theme {
|
function getSystemTheme(): Theme {
|
||||||
const h = new Date().getHours()
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||||
return h >= 7 && h < 22 ? 'light' : 'dark'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||||
@@ -26,14 +25,16 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|||||||
const stored = localStorage.getItem('hotelsync-theme') as ThemeMode | null
|
const stored = localStorage.getItem('hotelsync-theme') as ThemeMode | null
|
||||||
if (!stored || stored === 'light') return 'light'
|
if (!stored || stored === 'light') return 'light'
|
||||||
if (stored === 'dark') return 'dark'
|
if (stored === 'dark') return 'dark'
|
||||||
return getAutoTheme()
|
return getSystemTheme()
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mode === 'auto') {
|
if (mode === 'auto') {
|
||||||
setTheme(getAutoTheme())
|
setTheme(getSystemTheme())
|
||||||
const interval = setInterval(() => setTheme(getAutoTheme()), 60_000)
|
const mq = window.matchMedia('(prefers-color-scheme: dark)')
|
||||||
return () => clearInterval(interval)
|
const handler = (e: MediaQueryListEvent) => setTheme(e.matches ? 'dark' : 'light')
|
||||||
|
mq.addEventListener('change', handler)
|
||||||
|
return () => mq.removeEventListener('change', handler)
|
||||||
} else {
|
} else {
|
||||||
setTheme(mode)
|
setTheme(mode)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user