Log NetUP request/response body; fix reservation_id to be numeric

- reservation_id converted to integer (NetUP likely requires number not UUID string)
- Log requestBody sent to NetUP and responseBody received from NetUP
- Push event rows now expandable: click to see URL, request JSON, NetUP error response
- Same for checkout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 12:29:45 +03:00
parent d8f560ae12
commit c656d0c6ae
3 changed files with 75 additions and 31 deletions

View File

@@ -31,8 +31,10 @@ type PushEvent = {
roomNumber: string
netupRoom: string
url: string
requestBody?: unknown
status: 'ok' | 'error' | 'skipped'
httpStatus?: number
responseBody?: string
error?: string
}
@@ -65,7 +67,8 @@ export function TvWelcomePage() {
const [pushEvents, setPushEvents] = useState<PushEvent[]>([])
const [pullEntries, setPullEntries] = useState<PullEntry[]>([])
const [logLoading, setLogLoading] = useState(false)
const [selectedPull, setSelectedPull] = useState<PullEntry | null>(null)
const [selectedPull, setSelectedPull] = useState<PullEntry | null>(null)
const [selectedPush, setSelectedPush] = useState<PushEvent | null>(null)
// Test check-in
const [testingCheckin, setTestingCheckin] = useState(false)
const [testCheckinResult, setTestCheckinResult] = useState<{ ok: boolean; msg: string } | null>(null)
@@ -548,23 +551,53 @@ export function TvWelcomePage() {
) : (
<div className="divide-y divide-slate-100 dark:divide-slate-700/50">
{pushEvents.map((e, i) => (
<div key={i} className="flex items-center gap-3 py-2.5 px-1 text-sm">
<span className={cn(
'w-2 h-2 rounded-full shrink-0',
e.status === 'ok' ? 'bg-emerald-500' : e.status === 'skipped' ? 'bg-amber-400' : 'bg-red-500',
)} />
<span className="font-medium text-slate-700 dark:text-slate-200 w-20 shrink-0">
{e.action === 'check-in' ? 'Заселение' : 'Выезд'}
</span>
<span className="text-slate-500 dark:text-slate-400">
Номер <span className="font-medium text-slate-700 dark:text-slate-200">{e.roomNumber}</span>
{e.netupRoom && <> NetUP <span className="font-mono text-xs">{e.netupRoom}</span></>}
</span>
{e.error && <span className="text-red-500 dark:text-red-400 text-xs flex-1 truncate">{e.error}</span>}
{e.httpStatus && e.status === 'ok' && <span className="text-xs text-emerald-600 dark:text-emerald-400">HTTP {e.httpStatus}</span>}
<span className="ml-auto text-xs text-slate-400 shrink-0">
{new Date(e.ts).toLocaleTimeString('ru-RU')}
</span>
<div key={i}>
<button
onClick={() => setSelectedPush(selectedPush === e ? null : e)}
className={cn(
'w-full flex items-center gap-3 py-2.5 px-1 text-sm rounded-lg transition-colors',
selectedPush === e ? 'bg-slate-50 dark:bg-slate-700/40' : 'hover:bg-slate-50/60 dark:hover:bg-slate-700/20',
)}
>
<span className={cn(
'w-2 h-2 rounded-full shrink-0',
e.status === 'ok' ? 'bg-emerald-500' : e.status === 'skipped' ? 'bg-amber-400' : 'bg-red-500',
)} />
<span className="font-medium text-slate-700 dark:text-slate-200 w-20 shrink-0">
{e.action === 'check-in' ? 'Заселение' : 'Выезд'}
</span>
<span className="text-slate-500 dark:text-slate-400">
Номер <span className="font-medium text-slate-700 dark:text-slate-200">{e.roomNumber}</span>
{e.netupRoom && <> NetUP <span className="font-mono text-xs">{e.netupRoom}</span></>}
</span>
{e.error && <span className="text-red-500 dark:text-red-400 text-xs flex-1 truncate text-left">{e.error}</span>}
{e.httpStatus && e.status === 'ok' && <span className="text-xs text-emerald-600 dark:text-emerald-400">HTTP {e.httpStatus}</span>}
<span className="ml-auto text-xs text-slate-400 shrink-0">
{new Date(e.ts).toLocaleTimeString('ru-RU')}
</span>
</button>
{selectedPush === e && (
<div className="mx-1 mb-2 bg-slate-900 dark:bg-slate-950 rounded-xl p-4 space-y-3 text-xs font-mono">
<div>
<p className="text-slate-400 font-sans font-semibold mb-1">URL запроса</p>
<p className="text-violet-400 break-all">{e.url}</p>
</div>
{e.requestBody != null && (
<div>
<p className="text-slate-400 font-sans font-semibold mb-1">Тело запроса (отправили в NetUP)</p>
<pre className="text-emerald-300 whitespace-pre-wrap break-all">
{JSON.stringify(e.requestBody, null, 2)}
</pre>
</div>
)}
{e.responseBody && (
<div>
<p className="text-slate-400 font-sans font-semibold mb-1">Ответ NetUP</p>
<pre className="text-red-300 whitespace-pre-wrap break-all">{e.responseBody}</pre>
</div>
)}
</div>
)}
</div>
))}
</div>