Add role permissions, loyalty accrual rules, guest statuses, and room service guest page
- UsersPage: add "Роли и права" tab with per-module permission editor per role; create/delete custom roles - LoyaltyPage: add "Правила начисления" tab with togglable accrual rules editor; add "Ручное начисление" tab with guest search by name/phone/email, add/deduct points with reason and history; fix banner text (HotelSync is a PMS, not a hotel chain) - GuestsPage: add "Статусы гостей" tab (moved from Settings) - SettingsPage: remove Guests section; add compact calendar toggle in Appearance; toggle persists via localStorage - BookingCalendar: read compact mode default from localStorage - App.tsx: add public route /room-service/:slug → GuestRoomServicePage - GuestRoomServicePage: new public guest-facing page for ordering room service with menu, cart, payment, and order status tracking - RoomServicePage: QR/link sharing, payment mode toggles, new order notifications - RentalPage/rentalData: buffer time between bookings (bufferMinutes field) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,7 @@ interface ObjectFormData {
|
||||
openHour: number
|
||||
closeHour: number
|
||||
maxHoursPerSlot: number | ''
|
||||
bufferMinutes: number
|
||||
description: string
|
||||
}
|
||||
|
||||
@@ -48,6 +49,7 @@ const EMPTY_FORM: ObjectFormData = {
|
||||
name: '', icon: '🎾', color: 'bg-green-500',
|
||||
pricePerHour: 1000, pricePerDay: 5000,
|
||||
openHour: 9, closeHour: 21, maxHoursPerSlot: '',
|
||||
bufferMinutes: 0,
|
||||
description: '',
|
||||
}
|
||||
|
||||
@@ -65,6 +67,7 @@ function ObjectFormModal({
|
||||
pricePerHour: initial.pricePerHour, pricePerDay: initial.pricePerDay,
|
||||
openHour: initial.openHour, closeHour: initial.closeHour,
|
||||
maxHoursPerSlot: initial.maxHoursPerSlot ?? '',
|
||||
bufferMinutes: initial.bufferMinutes ?? 0,
|
||||
description: '',
|
||||
}
|
||||
: EMPTY_FORM,
|
||||
@@ -86,6 +89,7 @@ function ObjectFormModal({
|
||||
openHour: form.openHour,
|
||||
closeHour: form.closeHour,
|
||||
maxHoursPerSlot: form.maxHoursPerSlot !== '' ? Number(form.maxHoursPerSlot) : undefined,
|
||||
bufferMinutes: form.bufferMinutes > 0 ? form.bufferMinutes : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -171,6 +175,31 @@ function ObjectFormModal({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Buffer time */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">
|
||||
Технический перерыв между бронями
|
||||
</label>
|
||||
<select
|
||||
className="input"
|
||||
value={form.bufferMinutes}
|
||||
onChange={e => set('bufferMinutes', parseInt(e.target.value))}
|
||||
>
|
||||
{[
|
||||
{ value: 0, label: 'Без перерыва' },
|
||||
{ value: 15, label: '15 минут' },
|
||||
{ value: 30, label: '30 минут' },
|
||||
{ value: 45, label: '45 минут' },
|
||||
{ value: 60, label: '1 час' },
|
||||
{ value: 90, label: '1,5 часа' },
|
||||
{ value: 120, label: '2 часа' },
|
||||
].map(o => (
|
||||
<option key={o.value} value={o.value}>{o.label}</option>
|
||||
))}
|
||||
</select>
|
||||
<p className="text-xs text-slate-400 mt-1">Время для уборки СПА, подготовки корта и т.д.</p>
|
||||
</div>
|
||||
|
||||
{/* Hours */}
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user