fix: widget payment screen always shown, phone mask, today's date default
- Always show payment step when paymentEnabled=true even if YooKassa confirmationUrl is null (payment creation failed silently) - Add third payment screen variant: "manager will contact you" when no YooKassa URL and no bank details configured - Add Russian phone mask +7 (___) ___-__-__ on phone type fields - Default check-in/check-out dates to today/tomorrow instead of hardcoded past dates Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -196,8 +196,10 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme
|
|||||||
paymentEnabled?: boolean
|
paymentEnabled?: boolean
|
||||||
}) {
|
}) {
|
||||||
const [previewTab, setPreviewTab] = useState<'rooms' | 'rental'>('rooms')
|
const [previewTab, setPreviewTab] = useState<'rooms' | 'rental'>('rooms')
|
||||||
const [checkIn, setCheckIn] = useState('2026-03-20')
|
const todayStr = new Date().toISOString().slice(0, 10)
|
||||||
const [checkOut, setCheckOut] = useState('2026-03-22')
|
const tomorrowStr = new Date(Date.now() + 86400000).toISOString().slice(0, 10)
|
||||||
|
const [checkIn, setCheckIn] = useState(todayStr)
|
||||||
|
const [checkOut, setCheckOut] = useState(tomorrowStr)
|
||||||
const [guests, setGuests] = useState(2)
|
const [guests, setGuests] = useState(2)
|
||||||
const [extraBeds, setExtraBeds] = useState(0)
|
const [extraBeds, setExtraBeds] = useState(0)
|
||||||
const [children, setChildren] = useState(0)
|
const [children, setChildren] = useState(0)
|
||||||
@@ -289,7 +291,8 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme
|
|||||||
if (result.confirmationUrl) {
|
if (result.confirmationUrl) {
|
||||||
setConfirmUrl(result.confirmationUrl)
|
setConfirmUrl(result.confirmationUrl)
|
||||||
setStep('payment')
|
setStep('payment')
|
||||||
} else if (settings.bankDetails) {
|
} else if (settings.bankDetails || needsPayment) {
|
||||||
|
// YooKassa failed or bank transfer configured — always show payment screen
|
||||||
setStep('payment')
|
setStep('payment')
|
||||||
} else {
|
} else {
|
||||||
setStep('success')
|
setStep('success')
|
||||||
@@ -379,6 +382,7 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme
|
|||||||
// ── Payment screen ──
|
// ── Payment screen ──
|
||||||
if (step === 'payment') {
|
if (step === 'payment') {
|
||||||
const isYookassa = !!confirmUrl
|
const isYookassa = !!confirmUrl
|
||||||
|
const hasBankDetails = !isYookassa && !!settings.bankDetails
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-2xl shadow-2xl overflow-hidden border border-slate-200" style={{ fontFamily: 'Inter, sans-serif' }}>
|
<div className="bg-white rounded-2xl shadow-2xl overflow-hidden border border-slate-200" style={{ fontFamily: 'Inter, sans-serif' }}>
|
||||||
@@ -410,14 +414,14 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme
|
|||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-slate-400">🔒 Защищённое соединение · ЮKassa</p>
|
<p className="text-xs text-slate-400">🔒 Защищённое соединение · ЮKassa</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : hasBankDetails ? (
|
||||||
/* Bank transfer details */
|
/* Bank transfer details */
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<p className="text-sm font-medium text-slate-700">
|
<p className="text-sm font-medium text-slate-700">
|
||||||
{settings.language === 'ru' ? 'Реквизиты для оплаты' : 'Payment details'}
|
{settings.language === 'ru' ? 'Реквизиты для оплаты' : 'Payment details'}
|
||||||
</p>
|
</p>
|
||||||
<div className="bg-slate-50 rounded-xl p-4 text-sm text-slate-700 whitespace-pre-wrap leading-relaxed">
|
<div className="bg-slate-50 rounded-xl p-4 text-sm text-slate-700 whitespace-pre-wrap leading-relaxed">
|
||||||
{settings.bankDetails || 'Реквизиты не указаны'}
|
{settings.bankDetails}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-slate-400">
|
<p className="text-xs text-slate-400">
|
||||||
{settings.language === 'ru'
|
{settings.language === 'ru'
|
||||||
@@ -425,6 +429,16 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme
|
|||||||
: 'After transfer, the hotel will confirm your booking. Include your name and dates in the payment reference.'}
|
: 'After transfer, the hotel will confirm your booking. Include your name and dates in the payment reference.'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
/* No payment configured — pending confirmation */
|
||||||
|
<div className="text-center space-y-3 py-2">
|
||||||
|
<div className="w-14 h-14 rounded-full flex items-center justify-center mx-auto bg-amber-50">
|
||||||
|
<AlertCircle size={28} className="text-amber-500" />
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-slate-700 font-medium">
|
||||||
|
{settings.language === 'ru' ? 'Бронирование принято. Менеджер отеля свяжется с вами для подтверждения оплаты.' : 'Booking received. Hotel manager will contact you to confirm payment.'}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -437,7 +451,7 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme
|
|||||||
>
|
>
|
||||||
{settings.language === 'ru' ? `Перейти к оплате · ${grandTotal.toLocaleString('ru-RU')} ₽` : `Proceed to pay · ${grandTotal.toLocaleString('ru-RU')} ₽`}
|
{settings.language === 'ru' ? `Перейти к оплате · ${grandTotal.toLocaleString('ru-RU')} ₽` : `Proceed to pay · ${grandTotal.toLocaleString('ru-RU')} ₽`}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : hasBankDetails ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => setStep('success')}
|
onClick={() => setStep('success')}
|
||||||
className="w-full py-3 rounded-xl text-white font-semibold text-sm transition-opacity hover:opacity-90"
|
className="w-full py-3 rounded-xl text-white font-semibold text-sm transition-opacity hover:opacity-90"
|
||||||
@@ -445,6 +459,14 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme
|
|||||||
>
|
>
|
||||||
{settings.language === 'ru' ? 'Я перевёл оплату' : 'I have paid'}
|
{settings.language === 'ru' ? 'Я перевёл оплату' : 'I have paid'}
|
||||||
</button>
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => setStep('success')}
|
||||||
|
className="w-full py-3 rounded-xl text-white font-semibold text-sm transition-opacity hover:opacity-90"
|
||||||
|
style={{ background: settings.primaryColor }}
|
||||||
|
>
|
||||||
|
{settings.language === 'ru' ? 'Понятно' : 'OK'}
|
||||||
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -603,13 +625,29 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme
|
|||||||
</select>
|
</select>
|
||||||
) : (
|
) : (
|
||||||
<input
|
<input
|
||||||
type={field.type}
|
type={field.type === 'phone' ? 'tel' : field.type}
|
||||||
|
inputMode={field.type === 'phone' ? 'tel' : undefined}
|
||||||
|
placeholder={field.type === 'phone' ? '+7 (___) ___-__-__' : undefined}
|
||||||
className="w-full text-sm border border-slate-200 rounded-lg px-3 py-2 focus:outline-none"
|
className="w-full text-sm border border-slate-200 rounded-lg px-3 py-2 focus:outline-none"
|
||||||
value={formValues[field.id] ?? ''}
|
value={formValues[field.id] ?? ''}
|
||||||
onChange={e => {
|
onChange={e => {
|
||||||
setFormValues(prev => ({ ...prev, [field.id]: e.target.value }))
|
let val = e.target.value
|
||||||
if (field.id === 'email') doGuestLookup(e.target.value, formValues['phone'])
|
if (field.type === 'phone') {
|
||||||
if (field.id === 'phone') doGuestLookup(formValues['email'], e.target.value)
|
const digits = val.replace(/\D/g, '').slice(0, 11)
|
||||||
|
if (digits.length === 0) { val = '' }
|
||||||
|
else {
|
||||||
|
const d = digits.startsWith('8') ? '7' + digits.slice(1) : digits
|
||||||
|
const n = d.startsWith('7') ? d : '7' + d
|
||||||
|
val = '+7'
|
||||||
|
if (n.length > 1) val += ' (' + n.slice(1, 4)
|
||||||
|
if (n.length > 4) val += ') ' + n.slice(4, 7)
|
||||||
|
if (n.length > 7) val += '-' + n.slice(7, 9)
|
||||||
|
if (n.length > 9) val += '-' + n.slice(9, 11)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFormValues(prev => ({ ...prev, [field.id]: val }))
|
||||||
|
if (field.id === 'email') doGuestLookup(val, formValues['phone'])
|
||||||
|
if (field.id === 'phone') doGuestLookup(formValues['email'], val)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user