feat: hourly pricing + category base prices in booking modal + availability grid
- Add hourly_price column to rate_overrides, base_price/allow_hourly/hourly_base_price to room_categories (migration 033) - Update categories and rate-overrides backend routes to handle new fields - AvailabilityPage: hourly/nightly mode toggle, period prices auto-applied to grid with 'П' indicator, confirmation dialog when overriding period prices - BookingModal: nightly/hourly toggle at top, pricing hierarchy (override → category base → room rate) - RoomCategoriesPage: base_price/allow_hourly/hourly_base_price fields in category form; inline rooms panel - CalendarPage + BookingCalendar: pass hourlyPriceOverrides and categoryBasePrices down to BookingModal Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,8 @@ export function CalendarPage() {
|
||||
const [rentalBookings, setRentalBookings] = useState<RentalBookingApi[]>([])
|
||||
const [locks, setLocks] = useState<Map<string, BookingLock>>(new Map())
|
||||
const [priceOverrides, setPriceOverrides] = useState<Record<string, Record<string, number>>>({})
|
||||
const [hourlyPriceOverrides, setHourlyPriceOverrides] = useState<Record<string, Record<string, number>>>({})
|
||||
const [categoryBasePrices, setCategoryBasePrices] = useState<Record<string, { nightlyBase: number; hourlyBase: number }>>({})
|
||||
// roomId → assigneeName for "убирается" tooltip
|
||||
const [cleaningAssignees, setCleaningAssignees] = useState<Record<string, string>>({})
|
||||
// Set of room IDs with active maintenance tasks
|
||||
@@ -36,21 +38,36 @@ export function CalendarPage() {
|
||||
api.rooms.list(slug),
|
||||
api.bookings.list(slug),
|
||||
api.rateOverrides.list(slug).catch(() => []),
|
||||
api.categories.list(slug).catch(() => []),
|
||||
]
|
||||
if (isRentalActive) {
|
||||
fetches.push(api.rental.listObjects(slug))
|
||||
fetches.push(api.rental.listBookings(slug))
|
||||
}
|
||||
Promise.all(fetches).then(([r, b, overrides, ro, rb]) => {
|
||||
Promise.all(fetches).then(([r, b, overridesRaw, catsRaw, ro, rb]) => {
|
||||
setRooms(r as Room[])
|
||||
setBookings(b as Booking[])
|
||||
// Build categoryId → date → price lookup
|
||||
const overrides = overridesRaw as import('../lib/api').RateOverrideApi[]
|
||||
const map: Record<string, Record<string, number>> = {}
|
||||
for (const o of overrides as import('../lib/api').RateOverrideApi[]) {
|
||||
const hourlyMap: Record<string, Record<string, number>> = {}
|
||||
for (const o of overrides) {
|
||||
if (!map[o.categoryId]) map[o.categoryId] = {}
|
||||
map[o.categoryId][o.date] = o.price
|
||||
if (o.hourlyPrice != null && o.hourlyPrice > 0) {
|
||||
if (!hourlyMap[o.categoryId]) hourlyMap[o.categoryId] = {}
|
||||
hourlyMap[o.categoryId][o.date] = o.hourlyPrice
|
||||
}
|
||||
}
|
||||
setPriceOverrides(map)
|
||||
setHourlyPriceOverrides(hourlyMap)
|
||||
// Build category base prices
|
||||
const cats = catsRaw as import('../lib/api').CategoryApi[]
|
||||
const catBases: Record<string, { nightlyBase: number; hourlyBase: number }> = {}
|
||||
for (const cat of cats) {
|
||||
catBases[cat.id] = { nightlyBase: cat.base_price ?? 0, hourlyBase: cat.hourly_base_price ?? 0 }
|
||||
}
|
||||
setCategoryBasePrices(catBases)
|
||||
if (isRentalActive) {
|
||||
setRentalObjects(ro as RentalObjectApi[])
|
||||
setRentalBookings(rb as RentalBookingApi[])
|
||||
@@ -296,6 +313,8 @@ export function CalendarPage() {
|
||||
onDraftCancel={handleDraftCancel}
|
||||
wsConnected={connected}
|
||||
priceOverrides={priceOverrides}
|
||||
hourlyPriceOverrides={hourlyPriceOverrides}
|
||||
categoryBasePrices={categoryBasePrices}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user