Commit Graph

354 Commits

Author SHA1 Message Date
31d135563f fix: cdn.hotelsync.ru nginx config + RoomModal key for state reset
- Add cdn.hotelsync.ru server block to nginx config (serves /var/www/uploads)
- Add cdn.hotelsync.ru to HTTP redirect server_name
- RoomModal: add key={editingRoom?.id ?? 'new'} to force state reinit on room change
- RoomModal: remove categories[0] fallback for categoryId (show 'Без категории' instead)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 13:14:20 +03:00
95809851fb fix: correct token read in upload + remove upload button, keep drop zone
- api.upload.photo used wrong localStorage key ('access_token' vs 'hotelsync-session')
- Removed standalone 'Загрузить фото' button — drop zone handles click+drag
- Added '+' thumbnail button to add more photos when gallery is non-empty
- Show spinner inline during upload instead of disabled button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 13:05:08 +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
dd75794a78 feat: organize uploads by folder + cdn.hotelsync.ru active
- 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>
2026-03-23 12:41:27 +03:00
f625d82cc5 fix: downgrade @fastify/multipart to ^8 and @fastify/static to ^7 (Fastify 4 compat)
v9 of both plugins requires Fastify 5.x — we're on 4.x

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 12:32:29 +03:00
3ed3156fe8 feat: move amenities directory to RoomCategoriesPage + cdn.hotelsync.ru for uploads
- 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>
2026-03-23 12:31:08 +03:00
3ef7f8265a feat: real file upload to /opt/hotelsync/uploads + serve via /uploads/ on API 2026-03-23 12:23:13 +03:00
5540d0cdfa fix: category form — async save with error display, drag-and-drop photos, 20MB body limit 2026-03-23 12:17:53 +03:00
1f761e65cf feat: room category — remove hardcoded types, show 'Без категории' with link when none exist 2026-03-23 12:08:47 +03:00
04258a4f50 feat: room type dropdown uses categories from API when available 2026-03-23 12:02:32 +03:00
c36d94e22a feat: show occupied slots in new rental step-1 modal 2026-03-23 11:54:52 +03:00
d54ea551b4 fix: RoomsPage load categories from API instead of mock 2026-03-23 11:45:40 +03:00
5c40b154d0 feat: categories + tariffs — migration, API routes, frontend integration 2026-03-23 11:42:29 +03:00
b494224af0 feat: BookingsPage rental tab — connect to API (replace mock data) 2026-03-23 11:19:04 +03:00
2475c76d45 feat: RentalBookingModal 2-column layout
Left col: existing bookings list, full-day toggle, time selectors
Right col: guest autocomplete, phone, link-to-room, notes, price+payment block
Modal widened to max-w-2xl; payment section unified with BookingModal style

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 01:38:25 +03:00
7c65bebc22 feat: rental tab 2-column layout + payment section
Left col: object picker, date, full-day toggle, time selectors
Right col: guest autocomplete, phone, notes, total + payment block
Payment: Наличные/Терминал toggle buttons, оплачено input, 'Всю сумму' shortcut, debt indicator; paidAmount saved to booking

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 01:31:16 +03:00
b1b6854663 feat: single ФИО field with autocomplete replaces 3 separate name inputs
- BookingModal (room tab): replace Фамилия/Имя/Отчество grid with single 'ФИО *' input; parseFullName() splits by spaces into last/first/middle; DB search via api.guests.list (debounced 300ms); dropdown shows current bookings + DB guests with phone/email autofill
- BookingDetailPanel: merge Фамилия (with autocomplete) + Имя + Отчество into single ФИО * input; existing acQuery/acResults infrastructure reused; parseFio() updates all 3 bgForm fields on every keystroke; openEditForm initializes ФИО from full name
- RentalBookingModal + BookingModal rental tab: include middleName when displaying DB guest results and when setting guestName on selection; update type to include middleName field

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 01:22:46 +03:00
e403689522 fix: toggle CSS + guest autocomplete in BookingModal rental tab
- Toggle: add type=button, overflow-hidden, p-0, explicit left-[2px] + translate-x-[18px] pattern for reliable knob positioning across all toggles
- Guest field in BookingModal rental tab: add debounced DB search (api.guests.list) + suggestions dropdown (same as RentalBookingModal)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 01:08:25 +03:00
f21c5d5054 fix: rental calendar cells cap at 3 bookings + show +N overflow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 23:08:25 +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
370fe8a9a9 feat: move rental module to main nav under Цены и тарифы
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 22:46:39 +03:00
20582581be fix: sync endHour state when effectiveStartHour shifts past it
When existing bookings block the initial startHour, effectiveStartHour
snaps to the first available slot. Added useLayoutEffect to update
endHour state immediately so start and end times are never equal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 22:37:49 +03:00
fe4b9aa6eb feat: connect RentalPage to API (objects + bookings persisted in DB)
- Add createObject/updateObject/deleteObject to api.rental
- Add RentalObjectPayload interface
- RentalPage now loads objects and bookings from API on mount
- Save/delete object operations call the API instead of only local state
- Save booking operations call the API (create or update)
- bufferMinutes from DB is now correctly passed to booking modal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 22:23:16 +03:00
3961585196 fix: rental booking buffer applied to end times + phone mask
- getMaxEndHour now subtracts breakHours from next booking's start,
  so you can't book right up to a slot that needs buffer time before it
- effectiveStartHour moved before availableEndHours so equal start/end
  times are no longer possible
- maskPhone (+7 (XXX) XXX-XX-XX) applied to phone field in RentalBookingModal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 22:16:12 +03:00
31858ca5cc feat: room/rental tabs in booking modal + smart time slot filtering
BookingModal:
- Add 'Номер' / 'Аренда объекта' tabs when rental objects exist
- Rental tab: object picker, date, full-day toggle, conflict-aware time dropdowns, guest/phone fields, total amount

RentalBookingModal:
- Filter start times: exclude hours inside booked slots + bufferMinutes gap
- Filter end times: cap at next booking's start hour
- Disable 'Весь день' if any timed bookings exist for the day
- Show 'Все слоты заняты' when no start hours are available
- Show buffer duration in time section label

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 22:07:40 +03:00
5cab0ce8da fix: checkout form defaults to planned checkOut date, not today
Previously earlyOutDate defaulted to today, which caused bookings checked
out on their first day (checkIn == today) to become 0-night invisible blocks
in the calendar. Now defaults to the planned checkOut date so the bar stays
visible; manager explicitly sets an earlier date for early departure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 19:39:24 +03:00
c0a086a7b8 fix: normalize checkIn/checkOut dates with .slice(0,10) before comparison
PostgreSQL DATE columns serialize as ISO strings with time component
(2026-03-21T00:00:00.000Z), causing string comparison with yyyy-MM-dd
to incorrectly show the early check-in banner for same-day bookings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 19:30:02 +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
9f1dd0c8c9 fix: block 'Заселить' button for future bookings (checkIn > today)
Future bookings can only be checked in via the early check-in flow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 19:15:51 +03:00
2c458dfc09 feat: early checkout with refund calculation for any checked_in booking
Replaces simple 'Выселить' button with a checkout form:
- Date picker (min=checkIn, max=original checkOut)
- If leaving early: shows refund nights, refund amount, and payment method selector
- Adds negative payment record to local payment history
- On confirm: sets status=checked_out and updates checkOut date

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 19:11:29 +03:00
b111cc0898 fix: early check-in checkout date capped at original planned checkout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 19:07:14 +03:00
6dbff276c8 feat: early check-in for guests who arrive before booking date
Show amber banner on confirmed bookings where check-in is in the future.
Opens an inline form to check in today: checks if current room is free,
otherwise shows available rooms with price difference. Allows overriding
checkout date. On confirm, sets status=checked_in and check-in=today.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 18:49:23 +03:00
b831106ae2 Rental modal: require phone for new guests, auto-create in guests DB
- Track guestIsKnown: true when selected from dropdown, false when typed manually
- If guest is new (typed manually): phone field is required (red border + hint)
- On save: if new guest + slug available, call api.guests.create before saving rental
- Button shows 'Сохранение...' while saving, disabled during async flow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 17:02:59 +03:00
033882790d Rental guest search: show phone number as subtitle in suggestions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 16:49:44 +03:00
dbccc59dfc Fix: rental booking save (date comparison), add DB guest search in rental modal
- BookingCalendar: normalize date comparison with .slice(0,10) to fix PostgreSQL DATE ISO format mismatch
- CalendarPage: show alert on rental booking save failure
- RentalBookingModal: add debounced DB guest search (api.guests.list) with 300ms delay
- RentalBookingModal: combine booking suggestions + DB results in unified dropdown
- Pass slug prop to RentalBookingModal for API access

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 16:45:37 +03:00
68c9caf46f RentalBookingModal: guest search autocomplete + link to room booking
- Guest name field shows dropdown with matching guests from all bookings
- Checked-in guests (🏠) shown first, then confirmed (📅)
- Selecting a guest auto-fills name and phone
- New 'Привязать к номеру' dropdown to link rental to a room booking
- Selecting a linked room auto-fills guest name if empty
- Pass bookings + rooms props from BookingCalendar to modal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 15:56:13 +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
3f4d0dac4c Calendar: compact mobile layout — narrow left column, hide View btn, move + to month row
- Left column: 80px on mobile (was 160px), hide room name/price/type badge
- Category headers: truncate text, reduce padding on mobile
- Toolbar: hide "Вид" button on mobile (sm:hidden)
- Toolbar: add compact "+" button next to month label on mobile only
- Original "+" button hidden on mobile (hidden sm:inline-flex)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 14:52:32 +03:00
7bd0e038ba Fix: block drag-to-move if target room has conflicting booking
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 01:05:40 +03:00
456a35e00d Revert: drag ghost back to small badge style
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 01:02:43 +03:00
80e3b21827 Fix: drag ghost visual, check-in date auto-fill, backend room_id patch
- BookingCalendar: replace tiny ghost badge with full-size booking block
  (same color, correct width based on nights × cell width, centered on cursor)
- BookingModal: auto-set check-out to check-in+1 day when check-in changes
- Backend PATCH /bookings/🆔 add room_id to allowed fields so drag-to-move saves correctly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 00:56:49 +03:00
97b2ca1f91 Fix drag drop: use ref for target room ID instead of elementFromPoint in mouseup (stale closure fix) 2026-03-21 00:44:30 +03:00
3e4a2a3cd2 Calendar: add drag ghost element + grabbing cursor; BookingDetail: add Relocate room for checked_in guests 2026-03-21 00:37:26 +03:00
75d7d6aa08 Fix: drag booking passes roomId to API; checked_out rooms available for new booking; remove time-based theme text 2026-03-21 00:22:03 +03:00
fd804926e4 BookingModal: move guest fields to right column; Theme: auto mode follows system prefers-color-scheme 2026-03-21 00:04:24 +03:00
d3581f27f2 BookingModal: move extra beds+tags to right col, payment to full-width bottom; Calendar: fix today booking left edge fill 2026-03-20 23:56:02 +03:00
0464259273 BookingModal: redesign — dates first, category cards, specific room toggle 2026-03-20 23:45:11 +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