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

@@ -166,6 +166,18 @@ function TariffModal({
const set = <K extends keyof typeof form>(k: K, v: typeof form[K]) =>
setForm(prev => ({ ...prev, [k]: v }))
// Авто-генерация кода из названия (если поле кода пустое)
const autoCode = (name: string) =>
name.trim().split(/\s+/).map(w => w[0]?.toUpperCase() ?? '').join('').slice(0, 6) || ''
const handleNameChange = (v: string) => {
setForm(prev => ({
...prev,
name: v,
code: prev.code || !tariff ? autoCode(v) : prev.code,
}))
}
const toggleInclusion = (id: string) =>
setForm(prev => ({
...prev,
@@ -204,9 +216,13 @@ function TariffModal({
<>
<button onClick={onClose} className="btn-secondary">Отмена</button>
<button
onClick={() => { if (form.name && form.code) onSave(form) }}
onClick={() => {
if (!form.name) return
const code = form.code || autoCode(form.name) || `T${Date.now().toString(36).toUpperCase().slice(-4)}`
onSave({ ...form, code })
}}
className="btn-primary"
disabled={!form.name || !form.code}
disabled={!form.name}
>
{tariff ? 'Сохранить' : 'Создать тариф'}
</button>
@@ -221,13 +237,13 @@ function TariffModal({
Название тарифа *
</label>
<input type="text" className="input" placeholder="Стандарт с завтраком"
value={form.name} onChange={e => set('name', e.target.value)} />
value={form.name} onChange={e => handleNameChange(e.target.value)} />
</div>
<div>
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">
Код *
Код <span className="text-slate-400 font-normal text-xs">(необязательно)</span>
</label>
<input type="text" className="input font-mono uppercase" placeholder="STD-BB"
<input type="text" className="input font-mono uppercase" placeholder="авто"
value={form.code}
onChange={e => set('code', e.target.value.toUpperCase())} />
</div>