Commit Graph

65 Commits

Author SHA1 Message Date
a4fc4a0972 feat: deposit — history page, card info, cancel button, webhook notice, online booking email
- Migration 060: card_last4, card_brand columns in booking_deposits
- Backend: webhook saves card brand/last4 from YooKassa notification
- Backend: GET /api/hotels/:slug/deposits — payment history with guest/room info
- Backend: DELETE /api/hotels/:slug/bookings/:bookingId/deposit — cancel hold_created or paid_cash deposit
- Backend: auto-create YooKassa hold + send deposit email for source='online' bookings
- Frontend: DepositHistoryPage with filters, card display, retention reasons
- Frontend: deposit cancel button for hold_created and paid_cash states
- Frontend: show card last4/brand in hold_confirmed status
- Frontend: release form presets (full return / hold all) + improved UX
- Frontend: webhook setup instructions in DepositSettingsPage
- Frontend: "История платежей" link in deposit settings header

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 11:47:51 +03:00
d98389e2bc feat: enforce checklist/minibar completion before marking task done
- ApiError now carries errors[] array from 422 responses
- updateStatus re-throws 422 so TaskCard can surface errors
- TaskCard: minibarCheckedLocal state + 'Подтвердить проверку минибара' button
- TaskCard: async handleComplete with completionErrors display
- Backend validation errors shown inline in completion panel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 23:09:03 +03:00
3c1a6ccebf feat: checklists, minibar and deposit modules
- DB migrations 057-059: checklist_templates/items/completions, minibar_items/consumptions, hotel_deposit_settings, booking_deposits
- Backend routes: checklists (templates CRUD + task completions), minibar (items + consumptions), deposit (settings, cash/yookassa hold, release/capture)
- YooKassa service for hold/capture/cancel payments
- Frontend: ChecklistSettingsPage, MinibarSettingsPage, DepositSettingsPage
- HousekeepingPage: task cards now show checklist + minibar panel with checkboxes and quantity buttons
- BookingModal: minibar charges summary + deposit management (cash/YooKassa/release) for existing bookings
- Sidebar + App.tsx: new routes /settings/checklists, /settings/minibar, /settings/deposit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 19:24:53 +03:00
1ad2a8057a feat: TTLock — correct DLL flow with buildNo/floorNo per lock
Backend: room-locks now store/return build_no, floor_no (required by
CE_WriteCard). issue-card passes buildNo/floorNo to agent instead of
cardSectors/username/password. testApi route simplified (no OAuth).

Frontend: remove username/password fields (not needed), add buildNo/floorNo
fields to lock mapping form, show building/floor in lock list.

Migration 056: adds build_no, floor_no columns to room_lock_mappings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 11:59:38 +03:00
8c215f2dcd feat: TTLock — add Учетная запись + Пароль fields for OAuth
Per Sciener docs: OAuth requires TTHotel account credentials (Учетная запись + Пароль)
separate from developer app client_id/client_secret.

- Migration 055: add ttlock_username, ttlock_password columns
- Backend: store and pass new fields to agent
- UI: add input fields matching TTHotel PMS integration page
- Agent: getAccessToken() uses ttlockUsername/ttlockPassword when provided

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 11:46:30 +03:00
84ead2af99 fix: TTLock — correct OAuth server (api.sciener.com) and API servers (euapi/cnapi)
- OAuth token: always https://api.sciener.com/oauth2/token (was euopen.ttlock.com = wrong)
- EU API: https://euapi.ttlock.com/v3/... (was euopen.ttlock.com)
- CN API: https://cnapi.ttlock.com/v3/... (was open.ttlock.com)
- Migration 054: update existing DB records with old server URLs
- UI: update radio button values to correct server names

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 11:21:37 +03:00
a986470f8e feat: TTLock API server selector (EU vs China) + pass apiServer to agent
- Add api_server column to hotel_ttlock_config (migration 053)
- GET/PATCH config includes apiServer field
- issue-card passes apiServer to agent command
- Agent: WriteCardCmd.apiServer optional, all API calls use it
- TTLockPage: radio buttons EU/China with hint about 404 error

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 23:48:37 +03:00
1ec3664886 feat: TTLockPage — import locks from TTHotel XLS file
- Parse .xls/.xlsx via SheetJS (xlsx package)
- Auto-match Door Name → room number; unmatched rows kept with empty room
- Preview table: green=matched, amber=unmatched, grey=already mapped
- Per-row room dropdown for manual correction before import
- room_id nullable in room_lock_mappings (migration 052)
- Conflict key changed from (hotel_id, room_id) to (hotel_id, lock_mac)
- LEFT JOIN in GET room-locks to show unmatched locks
- DELETE by UUID or MAC depending on whether room is assigned

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 23:08:24 +03:00
f837a84db5 feat: TTLock encoder per workstation (Option A)
- Migration 051: add ttlock_com_port to workstations, drop com_port/workstation_id from hotel_ttlock_config
- Backend workstations PATCH: accepts ttlock_com_port alongside name
- Backend ttlock: test/issue-card use per-workstation com_port; auto-select online encoder if workstationId not specified
- TTLockPage Step 2: per-workstation table with COM port input, Save, Test buttons per row
- BookingDetailPanel: auto-issue if 1 encoder online, picker modal if multiple
- api.ts: TTLockConfig/Update cleaned up, Workstation gets ttlockComPort, issueCard/testEncoder updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 23:40:29 +03:00
948744e077 feat: tariff selector in BookingModal
- Migration 050: add tariff_id FK to bookings table
- Backend: accept tariff_id in POST/PATCH bookings
- Frontend: load active tariffs in BookingModal, show tariff dropdown
- Price calculation applies tariff modifier (percent or fixed) before discount
- tariffId forwarded through CalendarPage → api.bookings.create/update

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 21:02:56 +03:00
249492cf3a feat: TTLock settings redesign — correct TTHotel DLL architecture
- Settings page rebuilt with 3-step flow:
  Step 1: client_id / client_secret (from TTHotel → Settings → Integrations)
  Step 2: workstation + COM port + card sectors
  Step 3: room → lock MAC address mapping (from lock.ttlock.com)
- Agent command ttlock:write_card now uses lockMac, comPort, cardSectors
  instead of cloud-only lockId approach
- New agent command ttlock:ping_encoder for encoder connectivity check
- Migration 049_ttlock_v2.sql: lock_mac TEXT replaces lock_id BIGINT,
  card_sectors + com_port added, ttlock_username/password removed
- API types updated: TTLockConfig, RoomLockMapping, CardIssuance

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 16:29:30 +03:00
be92d026e8 feat: TTLock integration + fix agent offline status
- Add TTLock electronic lock module: settings page, room-lock mapping,
  card issuance via agent command (ttlock:write_card)
- Fix agent offline: EquipmentPage now polls every 30s + reacts to
  workstation:online/offline WebSocket events instantly
- Backend broadcasts workstation online/offline events to hotel clients
  via existing WebSocket (agent-ws.ts)
- New DB migration 048_ttlock.sql: hotel_ttlock_config, room_lock_mappings,
  card_issuances tables
- New backend routes: GET/PATCH ttlock/config, POST ttlock/token,
  GET ttlock/locks, POST/DELETE ttlock/room-locks, POST issue-card
- "Выдать ключ (TTLock)" button in BookingDetailPanel for confirmed/checked_in
- TTLock page added to sidebar under Настройки

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 15:46:17 +03:00
f6d3c78dd0 feat: rental drafts, room modal cleanup, tariff auto-code
- New hotels get 3 rental object templates (tennis/sauna/conference) as drafts (is_active=false)
- RentalPage: draft templates shown separately with Activate/Delete buttons
- Migration 047: adds is_active column to rental_objects
- RoomModal: hide bed type/status/housekeeping in create mode; optional price (from category); restructured Places tab with extra beds constructor
- TariffsPage: auto-generate tariff code from name initials if code field is empty
- types: ExtraPlace.beds for extra place bed configuration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 11:19:54 +03:00
0088457f51 feat: add WiFi authorization module
- Backend: hotel_wifi_settings table (migration 046), CRUD routes
- Public API endpoint /api/wifi-auth/verify for captive portal
- Auth methods: room+lastname, room+birthdate, room+any, room_only
- Frontend: WiFiPage with settings, token management, setup guide
- Guide tabs: MikroTik, UniFi, Universal with copy-paste instructions
- Sidebar: WiFi item in Settings group

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 12:08:51 +03:00
f882cb2481 feat: KKT fiscal/non-fiscal mode, multi-KKT per workstation, DTO port per device
- Migration 045: fiscal_mode + dto_port columns on workstation_devices
- Backend: pass fiscal_mode/dto_port to agent in test_device command
- Agent atol.ts: remove global DTO singleton, accept dtoPort per function call
- Agent test_device: returns real KKT info (model, serial, FN, shift status)
- Equipment page: fiscal/non-fiscal toggle + DTO port field when adding KKT
- Equipment page: show fiscal mode badge on device card, DTO port if non-default
- Equipment page: KKT test result shows model, serial, FN, shift status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 14:06:44 +03:00
2caf717131 feat: NetUP via agent — proxy HTTP requests through Windows workstation
- Migration 044: add connection_type + agent_workstation_id to netup_settings
- Backend: netupReqViaCfg routes requests via agent WebSocket when connection_type=agent
- Agent: add netup_request command handler (proxies HTTP to local NetUP server)
- TvWelcomePage: add connection mode toggle (direct / via agent) + workstation selector
- Hint changes: URL placeholder shows local IP when agent mode selected

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 13:53:55 +03:00
0cce5f566f feat: agent version tracking, update button, device test, COM port list
- Migration 043: agent_version in workstations, netup device type
- agent-ws: sendCommandAndWait for async commands, save version on hello
- workstations route: list-ports, test-device, update, update-all endpoints
- agent-release route: GET /api/agents/latest-release reads latest.yml
- app.ts: serve /agent-updates/ static files
- api.ts: fix camelCase→snake_case for outgoing requests (fixes null:null)
- api.ts: new methods testDevice, sendUpdate, updateAll, listPorts
- EquipmentPage: version badge, update button, test (zap) button per device
- EquipmentPage: COM-port dropdown loaded from agent when online

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 13:05:23 +03:00
b2b2034f4c feat: COM-port support for workstation devices + agent UI fixes
- EquipmentPage: add COM-port connection type with port field (COM1, /dev/ttyUSB0)
- api.ts: add comPort field to WorkstationDevice type
- workstations route: pass com_port to INSERT/PATCH
- migration 042: extend connection check constraint to include 'com', add com_port column
- agent index.html: highlight first cell on load, reset input on unpair

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 12:41:31 +03:00
439be33c55 feat: workstations API + agent WebSocket — паркинг агентов и управление оборудованием
- migration 041: таблицы workstations, agent_pair_codes, workstation_devices
- routes/workstations: CRUD рабочих мест, генерация 6-значного кода, CRUD устройств
- POST /api/agent/pair: сопряжение агента по коду → JWT токен
- agent-ws.ts: WebSocket /ws/agent для постоянного соединения агентов,
  heartbeat 30s, онлайн/оффлайн статус в БД

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:27:24 +03:00
be58b16365 fix: migration 040 — run UPDATE before ADD CONSTRAINT (correct order)
ALTER TABLE ADD CONSTRAINT validates existing rows immediately, so the
UPDATE must run first to migrate 'standard' → 'bronze' before the new
constraint is applied.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 11:12:47 +03:00
214a11ec9d fix: migration 040 — drop old loyalty_tier check constraint before renaming standard→bronze
The guests table had CHECK (loyalty_tier IN ('standard','silver','gold','platinum'))
but the UI expects 'bronze' not 'standard'. Drop the old constraint, migrate
'standard' rows to 'bronze', then add the correct constraint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 11:06:44 +03:00
5128957d7c fix: loyalty tier crash, chat duplicate rooms, sidebar defaults
- LoyaltyPage: levelBadge() now falls back to bronze for unknown tiers
  (DB had loyalty_tier='standard' from old seed data)
- migration 040: deduplicate general chat rooms + add unique index,
  normalize loyalty_tier to bronze for non-standard values
- chat.ts: ON CONFLICT clause now targets the new partial unique index
- Sidebar: DEFAULT_GROUPS only opens 'basics' by default; bump
  localStorage key to sidebarGroups_v2 to clear stale stored state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 10:58:47 +03:00
113971f854 feat: wire up loyalty + chat — register routes, API types, connect LoyaltyPage, add ChatWidget to layout 2026-03-26 10:15:29 +03:00
5d5efecd9e feat: staff work schedule — weekly grid for all roles, shift assignment, day off marking
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 01:18:02 +03:00
17012ae3ca fix: migration 036 — delete old channels before altering constraint + remove TravelLine refs + hide hotel_admin from roles tab 2026-03-26 01:10:48 +03:00
566c6a5e41 feat: Russian market channels — Яндекс, Островок, Суточно, Авито, Bronevik, 101hotels + role-based sidebar 2026-03-26 00:56:53 +03:00
d5267335a0 feat: add hotel_admin (Системный администратор) and technician roles + protect hotel owner from deletion
- 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>
2026-03-26 00:39:48 +03:00
943b97c200 feat: connect UsersPage and SettingsPage SMTP to API + extend user roles/fields
- 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>
2026-03-26 00:29:13 +03:00
588ecb0f2a 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>
2026-03-26 00:05:14 +03:00
2b21f2c219 fix: add 'cancelled' to housekeeping_tasks status check constraint
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>
2026-03-24 20:20:26 +03:00
8b55bc58f1 feat: auto-assign strategy (round_robin/least_loaded/first_free); urgent tasks always use first_free 2026-03-24 12:29:32 +03:00
0feec5c88f feat: emergency room closure for urgent maintenance tasks
- Backend housekeeping POST: when priority=urgent + category=maintenance + room_id
  → auto sets room status=maintenance with maintenance_from/to dates
  → closes room for emergency_close_days (default 1 day)
  → broadcasts room_updated WS event
- housekeeping-settings: add emergency_close_days column (migration 030, default 1)
- TechnicalPage: rename "Срочно" → "Экстренно!", show "🔒 Номер закрыт" badge for urgent active tasks
- HousekeepingPage plans tab: new "Экстренное закрытие номера" setting with day picker (1/2/3/5/7/14 days)
- api.ts HkSettings: add emergency_close_days field

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 11:51:42 +03:00
c83a9ca714 feat: task problem/solution split + maintenance icons on rooms/calendar
- 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>
2026-03-24 11:45:24 +03:00
96961046ed fix: room number + photos in task cards; time tracking for tasks
- 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>
2026-03-24 11:24:49 +03:00
2ebef940b9 feat: cleaning tooltip, report photos, fix tech task creation, completion photos
- 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>
2026-03-24 11:07:28 +03:00
e51d776b3a feat: photos for tech tasks, 3 priorities, remove cleaning status from context menu
- 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>
2026-03-24 10:38:51 +03:00
3bb7262b59 feat: add category field to housekeeping tasks + TechnicalPage for maintenance tasks
- 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>
2026-03-23 23:28:42 +03:00
b9116c427a feat: notifications API, auto-jobs, calendar housekeeping fixes
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>
2026-03-23 23:02:30 +03:00
9af11df1b0 feat: housekeeping automation — auto-task on checkout + room status on done
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>
2026-03-23 22:27:18 +03:00
e5daf91ed4 feat: persist rate overrides to DB when applying prices in AvailabilityPage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 16:14:45 +03:00
c3a30be74e feat: calendar hover price tooltip + AvailabilityPage API integration
- 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>
2026-03-23 15:05:12 +03:00
63da3d21cc feat: maintenance/blocked date ranges with booking overlap warning
- 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>
2026-03-23 14:49:19 +03:00
c0945e7264 fix: add photos column to room_categories via migration 019
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 12:53:44 +03:00
5c40b154d0 feat: categories + tariffs — migration, API routes, frontend integration 2026-03-23 11:42:29 +03:00
db05898c08 feat: 15-minute rental slots + compact calendar cell display
- 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>
2026-03-21 23:02:24 +03:00
edd493e24e feat: early check-in / late checkout fees with room-level configuration
- 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>
2026-03-21 19:24:36 +03:00
b48e50e62b Rental: connect to API, improve cell UI; remove price from room labels
Backend:
- Migration 015_rental.sql: rental_objects + rental_bookings tables, seed demo data
- New routes: GET/POST/PATCH/DELETE rental-objects and rental-bookings per hotel
- Register rentalRoutes in app.ts

Frontend:
- api.ts: add rental.listObjects, listBookings, createBooking, updateBooking, deleteBooking
- CalendarPage: fetch rental objects + bookings from API instead of mock data
- BookingCalendar: rental cell UI redesigned — colored squares + working hours + time slots (matches design spec)
- BookingCalendar: remove base rate price from desktop room label column (irrelevant with dynamic pricing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 15:46:01 +03:00
babd6e950e Fix: add passport_issued_by/issue_date to guests table for full autocomplete
- 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>
2026-03-20 17:47:12 +03:00
887c362c18 Fix: widen passport columns to TEXT for encrypted values
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 17:28:52 +03:00
8282155237 Add AES-256-GCM encryption for sensitive personal data fields
- New backend/src/lib/crypto.ts: encrypt/decrypt (AES-256-GCM) + passportToken (HMAC-SHA256)
- Migration 012: birth_date/passport_issue_date → TEXT, add passport_search_token indexes
- guests.ts: encrypt passport_series/number/birth_date on write, decrypt on read,
  passport search via HMAC token (?passport= param) instead of plaintext LIKE
- booking-guests.ts: encrypt all sensitive fields, search token for passport lookup
- api.ts: guests.list() supports passport= param for exact passport lookup
- BookingDetailPanel: use passport param for passport-based autocomplete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 17:19:27 +03:00