- Migration 035: extend role constraint to include hotel_admin and technician; migrate existing manager users to hotel_admin
- auth.ts: registration now assigns hotel_admin (not manager) to hotel owner
- seed.ts: demo user manager@grand-palace.ru seeded as hotel_admin
- types.ts: expand JwtPayload role union with all roles
- users.ts: hotel_admin included in access checks; role creation/edit/delete rules enforced; hotel_admin users are undeletable and uneditable (non-super_admin); role cannot be set to hotel_admin via PATCH
- rooms.ts / channels.ts: hotel_admin added to write-access checks
- UsersPage.tsx: hotel_admin and technician added to StaffRole, ROLE_META, DEFAULT_POSITIONS, mapRole, backendRoleMap, INITIAL_ROLE_PERMISSIONS; delete button hidden for hotel_admin; role selector locked for hotel_admin users; hotel_admin excluded from new-user role selector
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Migration 034: extend users table with phone, position, active columns; add new roles (receptionist, accountant, security)
- Backend users.ts: accept phone/position on create, accept phone/position/active/role on update; managers can now assign all non-admin roles
- types/index.ts + api.ts: add phone, position, active to User type and users create/update payload types
- UsersPage: fix mapRole to handle all 5 roles, fix toStaffUser to use real phone/position/active from API, fix handleSave to pass all fields
- SettingsPage: load SMTP settings from hotelSettings API on mount, save SMTP on handleSave when section === 'notify'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
DB constraint only allowed pending/in_progress/done/skipped, causing
500 error when trying to cancel a maintenance task.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Set crossOriginResourcePolicy: cross-origin in helmet so browsers
can load uploaded images from api.hotelsync.ru when app runs on
app.hotelsync.ru (was blocked by same-origin CORP header)
- Hide booking status legend squares below lg breakpoint (hidden lg:flex)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- TechnicalPage: redesign task cards with separate "Задача" and "Решение" sections
- "Задача": problem description + problem photos (editable before done)
- "Решение": technician resolution comment + resolution photos (green section)
- Resolution comment in CompletionModal now saves to resolution_notes field
- Resolution photos save to resolution_photos (no longer mixed with problem photos)
- RoomModal history tab: maintenance entries show separate problem/resolution sections
- Backend: add resolution_notes, resolution_photos to PATCH allowed fields
- Migration 029: resolution_notes TEXT + resolution_photos TEXT[] columns
- RoomsPage: load active maintenance tasks, show 🔧 неисправность badge on room cards
- CalendarPage: load active maintenance tasks, show 🔧 неиспр. label in calendar row
- BookingCalendar: activeMaintenance prop, WS handler updates maintenance set on task changes
- CalendarPage WS: handle housekeeping_task_created to update activeMaintenance set
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- RoomModal: new "История" tab showing all housekeeping and maintenance tasks for the room
- Groups tasks by date (newest first), expandable cards
- Shows: assignee, start time, end time, duration (started_at → completed_at)
- For maintenance: description, problem/solution photos with lightbox viewer
- Timing grid: start / completion timestamps
- Backend GET /housekeeping: add room_id filter param
- TechnicalPage CompletionModal: add resolution comment field saved to task notes
- api.ts: housekeeping.list() accepts roomId param
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend POST housekeeping: include photos in INSERT, return task with room JOIN
- Backend PATCH housekeeping: set started_at on in_progress, return task with room JOIN
- WS broadcasts now include room_number/assignee_name from JOIN
- Frontend: normalizeTask() helper maps snake_case WS fields to camelCase
- TechnicalPage: fix room number display (roomNumber || room_number)
- TechnicalPage: show time spent (started_at → completed_at) on task cards
- HousekeepingPage: history tab now includes maintenance reports (no category filter)
- HousekeepingPage: HistoryCard shows photos, notes, time spent, maintenance styling
- Migration 028: started_at TIMESTAMPTZ column on housekeeping_tasks
- types: add startedAt? to HousekeepingTask
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Calendar: hover tooltip on '🧹 убирается' shows who is cleaning (assignee name)
fetched from active HK tasks on mount, updated via WS housekeeping_updated events
- HousekeepingPage: 'Сообщить о неисправности' now creates a real maintenance task
in the DB (category=maintenance) + sends WS so TechnicalPage receives it instantly;
photo upload added to the report form
- RoomContextMenu: photo upload added to tech task creation form (camera button,
preview thumbnails); photos passed through to API on task creation
- TechnicalPage: selecting 'Выполнено' now opens CompletionModal with photo upload;
technician can attach completion photos before confirming; if require_completion_photo
setting is on — at least one photo is required
- Settings (HousekeepingPage plans tab): new toggle 'Обязательное фото при завершении
тех. задачи' saved to housekeeping_settings.require_completion_photo (migration 027)
- Backend: housekeeping-settings updated with require_completion_photo field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix root cause: CalendarPage created tasks with type='regular' which violated DB CHECK constraint (allowed: cleaning/turnover/inspection/maintenance/amenities) → INSERT silently failed. Fixed to type='cleaning'.
- Fix: HousekeepingPage now loads ALL active tasks (status=active = pending+in_progress) without date filter — tasks appear regardless of when they were created
- Backend: support ?status=active filter (translates to IN ('pending','in_progress'))
- Add "История" tab in HousekeepingPage: shows completed tasks for a selected date with prev/next navigation, assignee and completion time displayed
- WS handler: when task becomes done, move it from active list instead of keeping in place
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove 'Убирается' (cleaning) from manually selectable HK statuses in calendar context menu — it now only shows automatically when a task is in_progress
- Reduce priorities from 4 to 3: remove 'high', keep urgent/medium/low across context menu, tech tasks modal, and HousekeepingTask type
- Add photo upload to technical tasks: upload via camera button per task, display thumbnails with hover-to-remove, stored as TEXT[] in DB (migration 026)
- Fix HousekeepingPage WS handler to filter by category — no longer adds maintenance tasks to housekeeping board
- Add 'tasks' folder support to upload route
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Context menu (RoomContextMenu):
- Dirty/Cleaning now show priority sub-form (срочно/высокий/средний/низкий)
- New "Тех. задача" section with description text + priority grid
Calendar (CalendarPage):
- handleRoomUpdate: creates task for both 'dirty' AND 'cleaning' status
- New handleMaintenanceTaskCreate for category=maintenance tasks
- Passes priority from context menu to task creation
Backend (housekeeping.ts):
- task status → in_progress: room housekeeping_status = 'cleaning',
broadcasts housekeeping_done with roomStatus='cleaning' so all
connected Calendar clients update in real-time
DB (migration 025):
- category column on housekeeping_tasks (default 'housekeeping')
- HousekeepingPage filters category=housekeeping
- TechnicalPage filters category=maintenance
New TechnicalPage (/technical):
- List/filter/delete maintenance tasks
- Status dropdown per task
- Create modal with room/user/priority/date
Sidebar: "Тех. задачи" nav item added
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add migration 025 to add `category` column (housekeeping/maintenance) to housekeeping_tasks
- Backend: filter by ?category= in GET, store category in POST, allow patching category
- API client: HkPayload + housekeeping.list() now support category param
- HousekeepingPage: filters by category=housekeeping so maintenance tasks don't appear
- RoomContextMenu: priority sub-form for dirty/cleaning status, tech task creation inline form
- BookingCalendar: accepts onMaintenanceTaskCreate prop, passes priority to onRoomUpdate
- CalendarPage: handleRoomUpdate creates task for dirty+cleaning (with priority), new handleMaintenanceTaskCreate for category=maintenance tasks
- TechnicalPage: new page for viewing/creating/updating maintenance tasks
- App.tsx: /technical route added
- Sidebar: Тех. задачи nav item added under Управление
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Interval reduced from 10m to 1h (min period is 2h — hourly is enough)
- Auto-checkout threshold: check_out date + hotel.check_out_time + N hours
(e.g. checkout Mar 23 + 12:00 + 2h = auto-checkout at 14:00)
- Auto-cancel threshold: check_in date + hotel.check_in_time + N hours
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- notifications table (024 migration) — stores per-hotel notifications
- GET/PATCH/POST/DELETE /api/hotels/:slug/notifications endpoints
- createNotification() helper used by jobs + future hooks
- jobs.ts — background tasks every 10min: auto-cancel no-shows and
auto-checkout overdue stays based on hotel_settings, broadcasts WS
events and creates notifications for each automated action
Frontend:
- CalendarPage: handle housekeeping_done WS → update room status in
calendar in real-time (bug fix)
- CalendarPage: when manually setting room to dirty via context menu,
auto-create housekeeping task and broadcast via WS (bug fix)
- NotificationsContext: replaced mock data with real API integration,
polls every 60s, syncs markRead/delete to backend
- SettingsPage booking section: auto-cancel no-show toggle + hours
selector; auto-checkout toggle + hours selector
- api.ts: notifications API methods
- useHotelSocket: notification:new WS message type
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- Migration 023: housekeeping_settings table (checkout_auto, checkout_priority, inspection_after_clean)
- New route: GET/PATCH /api/hotels/:slug/housekeeping-settings
- bookings.ts: on checked_out → auto-create turnover task + mark room dirty + broadcast WS
- housekeeping.ts: on task done → update room status (inspect|clean per setting) + broadcast WS
- ws.ts: export broadcast() for use in other routes
Frontend:
- HousekeepingPage: load/save settings to API, live Loader on save button
- useHotelSocket: add housekeeping WS message types
- HousekeepingPage: subscribe to WS — new tasks appear instantly without refresh
- BookingModal: show total section when room + nights selected (not just when total > 0)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Calendar cells: hold mouse 600ms to show price tooltip for that date/room (baseRate + hourly rate if enabled)
- AvailabilityPage: load real categories from API, derive basePrice from room baseRates, fall back to room types if no categories
- Rate periods: persisted to DB via new /api/hotels/:slug/rate-periods endpoint
- New backend migration 021_rate_periods.sql + rate-periods route
- Added api.ratePeriods.{list,create,update,delete} to frontend API client
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DB migration 020: add maintenance_from, maintenance_to to rooms
- Room type + RoomPayload: maintenanceFrom, maintenanceTo fields
- Context menu: clicking "На ремонт"/"Закрыт" expands inline date picker
(С / По + "Без срока" checkbox), confirms with "Применить" button
- Calendar: stripe overlay covers only the maintenance date range
(full row if no dates set, partial if date range specified)
- Booking creation: if drag-selected dates overlap with maintenance period,
shows warning dialog with "Отмена" / "Всё равно забронировать"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DELETE /api/upload?url= endpoint removes file from disk (with path traversal protection)
- api.upload.deletePhoto() helper in frontend
- removePhoto() now calls deletePhoto for cdn.hotelsync.ru URLs (best-effort)
- handleDragOver only highlights drop zone for external file drags (not thumbnail DnD)
- <img> in preview gets draggable={false} + pointer-events-none to not interfere with drops
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Upload route now accepts ?folder=categories|rooms|hotels|guests
- Files saved to /uploads/{folder}/uuid.ext, served at cdn.hotelsync.ru/{folder}/uuid.ext
- api.upload.photo() accepts optional folder param (default: 'rooms')
- RoomCategoriesPage passes folder='categories' on photo upload
- SSL cert issued and nginx config active for cdn.hotelsync.ru
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove "Справочник удобств" block from RoomsPage, add to RoomCategoriesPage
- Fix RoomsPage imports (add Pencil back, remove unused amenity icons)
- Update hint in CategoryForm to point to current page
- Upload route now returns cdn.hotelsync.ru URLs instead of api.hotelsync.ru/uploads/
- Nginx config for cdn.hotelsync.ru (serves /opt/hotelsync/uploads directly)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rental bookings now use minutes since midnight (start_hour/end_hour
store e.g. 495 for 8:15, 570 for 9:30) — DB migration 017
- Buffer applied in exact minutes (no hour ceiling): 15min buffer after
9:00 booking → next slot available at 9:15
- Calendar cell: removed working-hours line (8:00–22:00), now shows
colored dot + compact time per booking (8–10, 8:15–9:15)
- Time selects show HH:MM format for 15-min intervals
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DB migration 016: add early_checkin_fee, late_checkout_fee to rooms table
- Backend rooms: support new fee columns in POST/PATCH
- Backend hotel-settings GET: also returns check_in_time, check_out_time
- Room type + RoomPayload: add earlyCheckinFee, lateCheckoutFee fields
- RoomModal: add fee amount inputs in pricing section
- SettingsPage: add toggles for early_checkin_enabled / late_checkout_enabled
- BookingDetailPanel: on check-in shows fee warning + adds payment record if
current time is before checkInTime and fee is set on the room; same logic
for check-out after checkOutTime
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Migration 014: add passport_issued_by, passport_issue_date to guests table
- guests.ts: include new fields in SELECT, INSERT, PATCH + encrypt/decrypt
- booking-guests.ts: sync issued_by/issue_date back to guests on update
- GuestApiType: add passportIssuedBy, passportIssueDate fields
- selectAcGuest: fill passport_issued_by and passport_issue_date from guest profile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add lastName/firstName/middleName/phone fields to booking form
- Remove 'Документы гостя' tab entirely from BookingModal
- Auto-parse existing guestName into FIO parts when editing
- Add guestPhone to Booking type and propagate through CalendarPage/BookingsPage API calls
- Add middle_name to guests table (migration 011)
- Update guests backend route to include middle_name in all queries
- Add middleName to GuestApiType in api.ts
- Fix selectAcGuest in BookingDetailPanel to also fill middle_name
- Auto-open guest form pre-filled with booking FIO when no guests exist
- Add passport series+number lookup in BookingDetailPanel guest form
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
drag-to-move: use window-level mousemove/mouseup listeners for reliable
drag detection — React synthetic events on container were unreliable
WS keepalive: server sends ping JSON every 25s so nginx proxy_read_timeout
never fires; client ignores pings (not broadcast to peers anymore)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove TravelLine/netup-pms pull route (travelline.ts unused)
- Remove pull integration section from TvWelcomePage (token field, API URL, etc.)
- Migration 008: netup_push_log table (hotel_id, action, status, request/response body)
- recordPush() now writes to DB in addition to in-memory buffer
- getLog reads from DB so logs survive server restarts
- clearLog deletes from DB
- Limit enforced: 50 rows per hotel (oldest auto-deleted)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- reservation_id converted to integer (NetUP likely requires number not UUID string)
- Log requestBody sent to NetUP and responseBody received from NetUP
- Push event rows now expandable: click to see URL, request JSON, NetUP error response
- Same for checkout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- notifyNetupCheckin/Checkout now log every attempt (ok/error/skipped)
with room number, NetUP room, URL, HTTP status, error message
- Push log exposed via GET /netup/log alongside pull request log
- New POST /netup/test-checkin: manually trigger a test check-in from UI
- Diagnostics tab split into two sections:
- Outgoing (push): table of check-in/check-out events with status dots
- Incoming (pull): NetUP poll requests (if TravelLine integration works)
- Test button picks first mapped room and fires a real check-in call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>