- 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>
nginx: add /ws location with Upgrade/Connection headers and 1h timeout
useHotelSocket: ping every 30s to prevent idle timeout, read fresh token
from localStorage on reconnect (handles 1h JWT expiry gracefully)
BookingCalendar: use elementFromPoint for reliable drag-to-move target
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Filter cancelled bookings from calendar grid (they no longer appear as red blocks)
- Remove cancelled from legend
- Drag-to-move: left-click drag a booking block to a different room row; click without dragging still opens the detail panel
- checked_out bookings are now semi-transparent and pointer-events-none so clicking through them creates a new booking
- Conflict detection in BookingDetailPanel now excludes checked_out bookings
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>
- New /travelline route: accepts all NetUP polls, logs them to in-memory
ring buffer (GET /travelline/_log to inspect), responds with active
reservations in a generic PMS format
- Migration 007: adds tl_token column to netup_settings
- PATCH netup/settings now saves tl_token; GET returns it
- TvWelcomePage: new TravelLine section with read-only API URL + copy
button and token input field
Setup in NetUP: Integration type = TravelLine,
API URL = https://api.hotelsync.ru/travelline, Token = <tl_token>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
req() auto-converts server_url→serverUrl, default_language→defaultLanguage,
netup_room_number→netupRoomNumber. Fixed both type declarations and component reads.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two root causes fixed:
1. api.ts saveToken() updated only localStorage, not React state — WS kept
reconnecting with the expired token. Now dispatches hotelsync:token-updated
event; AuthContext listens and updates session state, triggering WS reconnect
with the fresh token.
2. AuthContext mount effect could race with login(): if refresh failed while
login() was concurrently setting a new token, catch() called setSession(null)
and wiped the fresh session. Fixed with functional setSession updater that
checks current state before clearing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When WS reconnects after a disconnect, the browser may have missed unlock/booking:created
messages, leaving gray striped overlays stuck forever. Clearing all locks on reconnect
ensures stale state is removed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Keeps nginx config in repo so WS upgrade headers are not lost on server changes.
The /ws block must appear before location / in api.hotelsync.ru server block.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix api.ts getToken/saveToken to read from localStorage (matching AuthContext) — was reading sessionStorage causing all API requests to fail auth until refresh
- Fix session cleanup on 401 to use localStorage
- Add isConflict/availableRooms helpers to BookingModal; show amber warning when selected room is occupied with clickable free room suggestions
- Pass bookings prop to BookingModal via BookingCalendar
- Add paid_amount to backend POST /bookings INSERT
- Show alert() when booking creation fails so user sees the error
- Pass paidAmount in CalendarPage handleCreate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Reject pure-numeric passwords (must contain ≥1 letter) in RegisterForm, ResetPasswordPage, and backend register/reset-password handlers
- Replace bare strength bars with labeled 2×2 grid: ≥8 символов, Содержит букву, Заглавная буква, Цифра
- Auto-capitalize first letter of each word in "Название отеля" and "Контактное лицо" fields (autoCapitalize="words" + JS handler)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
If localStorage session has user but no token (corrupted/migrated session),
attempt a token refresh via httpOnly cookie. If refresh fails, clear session
and force re-login. Prevents WebSocket 'missing token' skip.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
sessionStorage is per-tab — new tabs had no session, so WebSocket
didn't connect and real-time lock/booking sync didn't work.
localStorage shares session across tabs in the same browser.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ThemeContext: add 'auto' mode that switches light 7:00-22:00, dark otherwise, re-checks every minute
- Topbar: cycle through 3 modes on click, show Monitor icon for auto mode
- Settings → Внешний вид: 3-button grid with split preview for auto
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>