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>
This commit is contained in:
2026-03-31 11:19:54 +03:00
parent 1970afdd13
commit f6d3c78dd0
9 changed files with 201 additions and 60 deletions

View File

@@ -171,6 +171,22 @@ const auth: FastifyPluginAsync = async (fastify) => {
VALUES ($1, $2, $3, 'hotel_admin', $4, $5, false, $6, NOW())`,
[contact, email.toLowerCase().trim(), passwordHash, hotel.id, phone ?? null, confirmToken],
)
// Template rental objects (inactive drafts — user activates as needed)
const templates = [
{ name: 'Теннисный корт', icon: '🎾', color: 'bg-green-500', text_color: 'text-green-700 dark:text-green-400', price_h: 1500, price_d: 8000, open: 8, close: 22, sort: 1 },
{ name: 'Баня / сауна', icon: '🛁', color: 'bg-orange-500', text_color: 'text-orange-700 dark:text-orange-400', price_h: 2500, price_d: 12000, open: 10, close: 23, sort: 2 },
{ name: 'Конференц-зал', icon: '🏛️', color: 'bg-blue-500', text_color: 'text-blue-700 dark:text-blue-400', price_h: 3000, price_d: 15000, open: 9, close: 20, sort: 3 },
]
for (const t of templates) {
await client.query(
`INSERT INTO rental_objects
(hotel_id, name, icon, color, text_color, price_per_hour, price_per_day,
open_hour, close_hour, sort_order, is_active)
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10, false)`,
[hotel.id, t.name, t.icon, t.color, t.text_color, t.price_h, t.price_d, t.open, t.close, t.sort],
)
}
await client.query('COMMIT')
} catch (err) {
await client.query('ROLLBACK')

View File

@@ -74,7 +74,7 @@ const rental: FastifyPluginAsync = async (fastify) => {
if (!canAccess(request.user.hotelSlug, request.user.role, slug)) return reply.code(403).send({ error: 'Forbidden' })
const b = request.body as Record<string, unknown>
const allowed = ['name','icon','color','text_color','price_per_hour','price_per_day',
'open_hour','close_hour','max_hours_per_slot','buffer_minutes','sort_order']
'open_hour','close_hour','max_hours_per_slot','buffer_minutes','sort_order','is_active']
const sets: string[] = []
const vals: unknown[] = []
for (const key of allowed) {