Major UX improvements across multiple pages

Шахматка:
- Date/period picker dropdown on navigation button (choose start date + days window)
- Cancelled bookings fade out with animation after 1 second

Бронирования:
- Clickable column headers with sort asc/desc (Гость, Заезд, Выезд, Статус, Источник, Сумма)

Страница входа:
- Removed role-based account selector — just email + password
- System auto-detects role/hotel from credentials

Настройки:
- New "Бронирование" section with room assignment strategy (spread/together/sequential/manual)
- Notifications: SMTP email config + SMS provider config (SMSC, SMS.ru, МТС, etc.)

Модули:
- Added Housekeeping and Channel Manager as proper modules
- Channel Manager lists: Booking.com, Airbnb, Яндекс Путешествия, Островок, Суточно.ру, OneTwoTrip
- Housekeeping visible to all roles (including housekeeper) via module status
- Sidebar now uses module status to show/hide Уборка and Каналы

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 15:20:05 +03:00
parent 4e615359eb
commit c233590c4b
29 changed files with 2171 additions and 149 deletions

48
backend/src/migrate.ts Normal file
View File

@@ -0,0 +1,48 @@
import fs from 'fs'
import path from 'path'
import { db } from './db'
export async function runMigrations() {
const migrationsDir = path.join(process.cwd(), 'migrations')
// Create migrations tracking table
await db.query(`
CREATE TABLE IF NOT EXISTS _migrations (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL UNIQUE,
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
`)
const files = fs.readdirSync(migrationsDir)
.filter(f => f.endsWith('.sql'))
.sort()
for (const file of files) {
const { rows } = await db.query(
'SELECT id FROM _migrations WHERE name = $1',
[file],
)
if (rows.length > 0) continue // already applied
const sql = fs.readFileSync(path.join(migrationsDir, file), 'utf8')
console.log(`[migrate] Applying ${file}...`)
const client = await db.connect()
try {
await client.query('BEGIN')
await client.query(sql)
await client.query('INSERT INTO _migrations (name) VALUES ($1)', [file])
await client.query('COMMIT')
console.log(`[migrate] ✅ ${file} applied`)
} catch (err) {
await client.query('ROLLBACK')
console.error(`[migrate] ❌ ${file} failed:`, err)
throw err
} finally {
client.release()
}
}
console.log('[migrate] All migrations up to date')
}