- displayRooms now maps r.photos (URL strings) to photoUrls[]
- Collapsed thumbnail: shows <img> if real photo URL exists, falls back to emoji
- Expanded gallery: full-width <img> for real photos, gradient for mock
- Thumbnail strip: renders real photo thumbnails when available
- Photo nav buttons use totalPhotos (real or mock count)
- beds/area/rating hidden when value is 0 (real rooms have no beds/area/rating data)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Additional services in widget are now derived from rental_objects (not a separate list)
- Settings page shows rental objects with enable/disable toggle; link to rental module
- Saves only {id, enabled}[] to widget_services — rental objects manage name/price/icon
- Standalone widget also derives additionalServices from config.rentalObjects
- Widget config API includes services[] in widgetSettings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Cards now show wide photo (h-44) at top instead of small thumbnail
- Gallery navigation: left/right arrows on hover, dot indicators, counter
- Gradient overlay with available badge (top-left) and photo count
- Select button at bottom of each card
- Graceful fallback to 🛏️ placeholder when no photos
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Services can now be added, edited inline (icon/name/price/unit), and deleted
- List persisted to DB as widget_services key in hotel_settings (auto-save)
- Services loaded from DB on settings page init; fall back to DEFAULT_SERVICES
- Toggle enable/disable retained; edit/delete buttons appear on row hover
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Category cards now show photos[0] as <img> when available, fallback to 🛏️ emoji
- Settings page preview loads rentalObjects from API and passes to WidgetPreview
- MockCategory type and MOCK_CATEGORIES get photos: [] to match updated type
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add rental_objects to /api/widget/:slug/config response
- Add POST /api/widget/:slug/rental-bookings endpoint with availability check
- Widget rental tab now shows real rental objects (not additionalServices)
- Date picker, hourly or full-day toggle, start/end hour selectors
- Booking submit creates actual rental_booking record in DB
- Success screen shows rental object, date and time slot
- WidgetRentalObject + WidgetRentalBookingPayload types added to api.ts
- BookingWidgetStandalonePage passes rentalObjects to WidgetPreview
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add YooKassa webhook handler (POST /api/yookassa/webhook) that updates
booking to confirmed+paid on payment.succeeded, cancelled on payment.canceled
- Add public status endpoint GET /api/online-bookings/:id for return URL polling
- Add 'reserved' booking status (violet) shown in calendar while awaiting payment
- Add 'website' to BookingSource type
- Payment timeout job (every 2 min) auto-cancels expired unpaid bookings
- Widget setting: "Время ожидания оплаты" (5–60 min, default 15)
- BookingConfirmPage at /booking-confirm/:id — polls status, countdown timer,
shows success/pending/cancelled state
- Widget bookings now use status='reserved' instead of 'inquiry' when YooKassa
is configured; set to 'confirmed' by webhook on payment.succeeded
- Migration 070: add 'reserved' to bookings status constraint + payment_expires_at
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- localDate() uses local timezone instead of UTC (prevents showing yesterday
after midnight in UTC+3)
- Guest lookup: fallback to bookings table by guest_email/guest_phone when
no guest profile found in CRM (guests table)
- Fix REGEXP_REPLACE pattern from '\D' (PCRE) to '[^0-9]' (PostgreSQL POSIX)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- On API error: show error message instead of false success screen
- Success message: "Оплата прошла успешно" only when paidViaGateway=true (user actually clicked pay in YooKassa); otherwise "Бронирование принято. Менеджер свяжется с вами" or "Оплата на месте"
- Payment screen: simplified to YooKassa-only redirect (removed bank transfer variant)
- Removed bankDetails from WidgetSettings entirely (not needed per product decision)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Always show payment step when paymentEnabled=true even if YooKassa
confirmationUrl is null (payment creation failed silently)
- Add third payment screen variant: "manager will contact you" when no
YooKassa URL and no bank details configured
- Add Russian phone mask +7 (___) ___-__-__ on phone type fields
- Default check-in/check-out dates to today/tomorrow instead of hardcoded past dates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add is_blacklisted column to guests (migration 068)
- POST /widget/bookings: block blacklisted guests (403, generic message)
- GET /widget/:slug/guests/lookup: find existing guest by email/phone for auto-fill
- Widget form: debounce guest lookup on email/phone input, show "Заполнить" suggestion
- Widget payment screen: replace fake card form with proper YooKassa redirect screen or bank transfer details page
- Add bank transfer details field to widget settings (saved as widget_bank_details)
- GuestsPage: blacklist toggle button in edit modal + 🚫 ЧС badge in table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Widget white page fix: fallback photo when real rooms have no photos array
- Long-stay deposits: if booking > threshold days and toggle on → full charge instead of hold (bypasses YooKassa 7-day limit)
- DepositSettingsPage: toggle + day threshold input for long-stay full payment
- Refund endpoint: POST /deposit/refund supports full and partial refunds via YooKassa refunds API
- DepositWidget: refund UI for yookassa_charge deposits (full/partial, shows remaining amount)
- partially_refunded status support with badge and "Вернуть ещё" button
- Auto-confirm: webhook now auto-confirms booking status on payment.succeeded for widget bookings
- PaymentSettingsPage: auto-confirm toggle per gateway (shown when booking-widget module active)
- Migration 067: long_stay columns on hotel_deposit_settings, auto_confirm_on_payment on gateways, refunded_amount on deposits
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each room card in the widget can now be expanded via chevron button to reveal:
- Photo gallery with navigation arrows, dot indicators, and thumbnail strip (gradient placeholders per room)
- Room description, area (m²), bed count
- Amenities chips with icons (Wi-Fi, TV, AC, etc.)
- 3D tour button for rooms that support it (Делюкс, Пентхаус); placeholder notice for others
- "Выбрать этот номер" button inside detail panel
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HousekeepingPage: 3-level severity for maintenance reports (Не срочно / Средняя / Экстренно!); Экстренно automatically blocks the room and triggers an urgent notification; severity displayed as colored badge on task card
- NotificationsContext: extract shared notifications state from Topbar into context so any page can push notifications; add 'maintenance' type with wrench icon
- Topbar: migrate to NotificationsContext; add maintenance notification type with red Wrench icon
- BookingWidgetPage: add payment step after guest form when payment provider is connected (card number/expiry/CVV/name form); Без оплаты mode skips payment and shows "pay on site" message; hourly services (unit='ч') show date + time-from/time-to picker in form step
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DynamicPricingPage: add 'Нагрузка' tab with monthly occupancy bar chart (2025/2026 data), year selector, summary stats, and editable occupancy-based pricing rules (threshold ranges → price modifiers)
- HousekeepingPage: add maintenance note button (wrench icon) on task cards — housekeeper can report broken items with a description that gets sent to technical service; notes displayed in orange badge
- BookingWidgetPage: fix 'Забронировать' button with full form flow (browse → form → success), add configurable form fields (required/optional/custom), add additional services toggle in settings and at checkout, add extra beds + children count in guest selector with auto-pricing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add AvailabilityPage with price grid (categories × dates), drag-select,
channel prices, EditPanel, PeriodModal, and rate periods tab
- Add ModulesContext with localStorage persistence and dynamic sidebar items
- Add ModulesPage with WiFi Auth, Payments, TV Welcome, Smart Locks,
OLAP Reports, Website Builder, Booking Widget modules
- Add ReportsPage (OLAP analytics with KPI cards, charts, tables)
- Add WebsitePage and BookingWidgetPage placeholders
- Remove hotel slug from URL routing; hotel context from JWT
- Add Доступность nav item in Sidebar under Управление
- Fix LoginPage redirects and HotelSync branding
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>