feat: widget settings persistence + remove redundant payment card

This commit is contained in:
2026-04-07 22:14:45 +03:00
parent a02cf342a2
commit 99274dfa04

View File

@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { import {
Code2, CreditCard, Globe, CalendarCheck2, Copy, CheckCheck, Code2, CreditCard, Globe, CalendarCheck2, Copy, CheckCheck, Save,
ChevronLeft, ChevronRight, Star, Users, Dumbbell, Waves, ChevronLeft, ChevronRight, Star, Users, Dumbbell, Waves,
Eye, Settings2, ArrowRight, Plus, Trash2, Check, X as XIcon, Eye, Settings2, ArrowRight, Plus, Trash2, Check, X as XIcon,
BedDouble, Baby, ToggleLeft, ToggleRight, ChevronDown, ChevronUp, BedDouble, Baby, ToggleLeft, ToggleRight, ChevronDown, ChevronUp,
@@ -1100,11 +1100,27 @@ export function BookingWidgetPage() {
Promise.all([ Promise.all([
api.widget.getConfig(slug).catch(() => null), api.widget.getConfig(slug).catch(() => null),
api.paymentGateways.list(slug).catch(() => [] as PaymentGateway[]), api.paymentGateways.list(slug).catch(() => [] as PaymentGateway[]),
]).then(([config, gws]) => { api.hotelSettings.get(slug).catch(() => ({}) as Record<string, unknown>),
]).then(([config, gws, hsRaw]) => {
const hs = hsRaw as Record<string, unknown>
if (config?.rooms) setRealRooms(config.rooms) if (config?.rooms) setRealRooms(config.rooms)
if (config?.categories) setRealCategories(config.categories) if (config?.categories) setRealCategories(config.categories)
const widgetGw = gws.find(g => g.isActive && g.modules?.includes('booking-widget')) const widgetGw = gws.find(g => g.isActive && g.modules?.includes('booking-widget'))
setGateway(widgetGw ?? null) setGateway(widgetGw ?? null)
// Restore saved widget settings
setSettings(prev => ({
...prev,
hotelName: String(hs.widget_hotel_name ?? config?.hotelName ?? prev.hotelName),
primaryColor: String(hs.widget_color ?? prev.primaryColor),
language: (String(hs.widget_lang ?? prev.language)) as 'ru' | 'en',
showRooms: hs.widget_show_rooms !== undefined ? Boolean(hs.widget_show_rooms) : prev.showRooms,
showRental: hs.widget_show_rental !== undefined ? Boolean(hs.widget_show_rental) : prev.showRental,
roomDisplayMode: (String(hs.widget_room_mode ?? prev.roomDisplayMode)) as 'rooms' | 'categories',
minNights: hs.widget_min_nights !== undefined ? Number(hs.widget_min_nights) : prev.minNights,
showPromo: hs.widget_show_promo !== undefined ? Boolean(hs.widget_show_promo) : prev.showPromo,
allowExtraBeds: hs.widget_extra_beds !== undefined ? Boolean(hs.widget_extra_beds) : prev.allowExtraBeds,
allowChildren: hs.widget_children !== undefined ? Boolean(hs.widget_children) : prev.allowChildren,
}))
}).finally(() => setGatewayLoading(false)) }).finally(() => setGatewayLoading(false))
}, [slug]) }, [slug])
@@ -1124,6 +1140,8 @@ export function BookingWidgetPage() {
additionalServices: DEFAULT_SERVICES, additionalServices: DEFAULT_SERVICES,
}) })
const [copied, setCopied] = useState(false) const [copied, setCopied] = useState(false)
const [saving, setSaving] = useState(false)
const [saved, setSaved] = useState(false)
const [pageTab, setPageTab] = useState<'constructor' | 'code' | 'stats'>('constructor') const [pageTab, setPageTab] = useState<'constructor' | 'code' | 'stats'>('constructor')
const [newFieldLabel, setNewFieldLabel] = useState('') const [newFieldLabel, setNewFieldLabel] = useState('')
const [showAddField, setShowAddField] = useState(false) const [showAddField, setShowAddField] = useState(false)
@@ -1131,6 +1149,28 @@ export function BookingWidgetPage() {
const set = <K extends keyof WidgetSettings>(k: K, v: WidgetSettings[K]) => const set = <K extends keyof WidgetSettings>(k: K, v: WidgetSettings[K]) =>
setSettings(prev => ({ ...prev, [k]: v })) setSettings(prev => ({ ...prev, [k]: v }))
const handleSaveSettings = async () => {
if (!slug) return
setSaving(true)
try {
await api.hotelSettings.update(slug, {
widget_hotel_name: settings.hotelName,
widget_color: settings.primaryColor,
widget_lang: settings.language,
widget_show_rooms: settings.showRooms,
widget_show_rental: settings.showRental,
widget_room_mode: settings.roomDisplayMode,
widget_min_nights: settings.minNights,
widget_show_promo: settings.showPromo,
widget_extra_beds: settings.allowExtraBeds,
widget_children: settings.allowChildren,
})
setSaved(true)
setTimeout(() => setSaved(false), 2000)
} catch { /* ignore */ }
finally { setSaving(false) }
}
const toggleService = (id: string) => const toggleService = (id: string) =>
setSettings(prev => ({ setSettings(prev => ({
...prev, ...prev,
@@ -1454,33 +1494,17 @@ export function BookingWidgetPage() {
)} )}
</div> </div>
{/* Payment */} {/* Save button */}
<div className="card p-5 space-y-3"> <div className="flex items-center gap-3">
<h3 className="font-semibold text-slate-900 dark:text-slate-100 flex items-center gap-2"> <button
<CreditCard size={16} className="text-emerald-600" /> onClick={handleSaveSettings}
Онлайн-оплата disabled={saving}
</h3> className="btn-primary flex items-center gap-2"
<div className="grid grid-cols-2 gap-2"> >
{PAYMENT_PROVIDERS.map(p => ( {saving ? <Loader2 size={15} className="animate-spin" /> : <Save size={15} />}
<button {saved ? 'Сохранено!' : 'Сохранить настройки'}
key={p.value} </button>
onClick={() => set('paymentProvider', p.value as any)} {saved && <span className="text-sm text-emerald-600 dark:text-emerald-400">Настройки виджета сохранены</span>}
className={cn(
'px-3 py-2 rounded-xl text-sm font-medium border transition-colors text-left',
settings.paymentProvider === p.value
? 'bg-brand-600 text-white border-brand-600'
: 'bg-white dark:bg-slate-700 border-slate-200 dark:border-slate-600 text-slate-700 dark:text-slate-300 hover:border-brand-400',
)}
>
{p.label}
</button>
))}
</div>
{settings.paymentProvider !== 'none' && (
<p className="text-xs text-slate-500 dark:text-slate-400">
Настройте ключи API в разделе Настройки Оплата
</p>
)}
</div> </div>
</div> </div>