From 2a741d89d051b09bcee012baab9f6842441782f9 Mon Sep 17 00:00:00 2001 From: HotelSync Date: Mon, 23 Mar 2026 16:23:10 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20rate=20overrides=20save=20+=20add=20?= =?UTF-8?q?=D0=91=D0=B5=D0=B7=20=D0=BA=D0=B0=D1=82=D0=B5=D0=B3=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=B8=20group?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix applyPrices: build overrides array before setPrices (updater is async, side-effects inside it are unreliable) - Add "Без категории" group for rooms not assigned to any category Co-Authored-By: Claude Sonnet 4.6 --- src/pages/AvailabilityPage.tsx | 59 +++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/src/pages/AvailabilityPage.tsx b/src/pages/AvailabilityPage.tsx index b81906b..1e28ae6 100644 --- a/src/pages/AvailabilityPage.tsx +++ b/src/pages/AvailabilityPage.tsx @@ -693,6 +693,7 @@ export function AvailabilityPage() { api.rateOverrides.list(slug).catch(() => []), ]).then(([cats, rooms, apiPeriods, apiOverrides]) => { // Build AvailabilityCat[] from real categories, derive basePrice from rooms + const colors = ['#6366f1','#10b981','#f59e0b','#ef4444','#8b5cf6','#06b6d4'] const availCats: AvailabilityCat[] = cats.map(cat => { const catRooms = rooms.filter(r => r.categoryId === cat.id) const basePrice = catRooms.length > 0 @@ -702,8 +703,7 @@ export function AvailabilityPage() { }) // If no categories, fall back to room types as groups - const colors = ['#6366f1','#10b981','#f59e0b','#ef4444','#8b5cf6','#06b6d4'] - const finalCats = availCats.length > 0 ? availCats : (() => { + let finalCats: AvailabilityCat[] = availCats.length > 0 ? availCats : (() => { const typeMap = new Map() for (const r of rooms) { if (!r.type) continue @@ -716,6 +716,19 @@ export function AvailabilityPage() { })) })() + // Add "Без категории" group for rooms not assigned to any category + const assignedCatIds = new Set(cats.map(c => c.id)) + const uncategorized = rooms.filter(r => !r.categoryId || !assignedCatIds.has(r.categoryId)) + if (uncategorized.length > 0) { + const basePrice = Math.min(...uncategorized.map(r => r.baseRate)) + finalCats = [...finalCats, { + id: '__no_category__', + name: 'Без категории', + color: '#94a3b8', + basePrice, + }] + } + setRoomCategories(finalCats) // Build initial grid then apply saved overrides on top @@ -798,34 +811,36 @@ export function AvailabilityPage() { ? roomCategories.filter(c => c.id === onlyCategoryId) : roomCategories + // Build overrides BEFORE setPrices (updater runs async, can't rely on side-effects inside it) const overrides: Array<{ category_id: string; date: string; price: number extra_person: number; min_nights: number channel_prices: Record; closed: boolean }> = [] + for (const cat of catsToUpdate) { + const basePrice = categoryPrices[cat.id] ?? cat.basePrice + for (const d of days) { + const channelPrices: Record = {} + for (const ch of RATE_CHANNELS) { + channelPrices[ch.id] = Math.round(basePrice * (channelMarkup[ch.id] ?? 1)) + } + overrides.push({ + category_id: cat.id, date: d, price: basePrice, + extra_person: extraPerson, min_nights: minNights, + channel_prices: channelPrices, closed, + }) + } + } + setPrices(prev => { const next = { ...prev } - for (const cat of catsToUpdate) { - next[cat.id] = { ...prev[cat.id] } - const basePrice = categoryPrices[cat.id] ?? cat.basePrice - for (const d of days) { - const channelPrices: Record = {} - for (const ch of RATE_CHANNELS) { - channelPrices[ch.id] = Math.round(basePrice * (channelMarkup[ch.id] ?? 1)) - } - next[cat.id][d] = { - price: basePrice, extraPerson, minNights, channelPrices, closed, - } - overrides.push({ - category_id: cat.id, - date: d, - price: basePrice, - extra_person: extraPerson, - min_nights: minNights, - channel_prices: channelPrices, - closed, - }) + for (const o of overrides) { + if (!next[o.category_id]) next[o.category_id] = {} + next[o.category_id] = { ...next[o.category_id] } + next[o.category_id][o.date] = { + price: o.price, extraPerson: o.extra_person, minNights: o.min_nights, + channelPrices: o.channel_prices, closed: o.closed, } } return next