Path: blob/master/gate-controller/components/WebhookLogsTab.tsx
1072 views
'use client';12import { useState, useEffect, useRef } from 'react';3import { listen } from '@tauri-apps/api/event';4import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';5import { Button } from '@/components/ui/button';6import { CheckCircle2, XCircle, Trash2 } from 'lucide-react';78interface WebhookLog {9timestamp: string;10success: boolean;11details: string;12error_message: string | null;13}1415export function WebhookLogsTab() {16const [logs, setLogs] = useState<WebhookLog[]>([]);17const scrollRef = useRef<HTMLDivElement>(null);1819useEffect(() => {20const unlisten = listen<WebhookLog>('webhook-log', (event) => {21setLogs((prevLogs) => [event.payload, ...prevLogs].slice(0, 100)); // Keep last 100 logs22});2324return () => {25unlisten.then(f => f());26};27}, []);2829useEffect(() => {30// Scroll to top when new logs arrive31if (scrollRef.current) {32scrollRef.current.scrollTop = 0;33}34}, [logs]);3536return (37<div className="p-4 h-full flex flex-col space-y-4">38<Card className='h-full flex flex-col'>39<CardHeader className='flex-shrink-0 flex flex-row items-center justify-between'>40<CardTitle>Real-time Webhook Logs</CardTitle>41<Button variant='destructive' size='sm' onClick={() => setLogs([])} disabled={logs.length === 0}>42<Trash2 className='w-4 h-4 mr-2' />43Clear Logs44</Button>45</CardHeader>46<CardContent ref={scrollRef} className='flex-grow min-h-0 overflow-y-auto space-y-3 pr-2'>47{logs.length === 0 ? (48<div className='text-center text-muted-foreground pt-12'>Listening for webhook events...</div>49) : (50logs.map((log, index) => (51<div key={index} className={`flex items-start gap-3 p-2 rounded-md ${log.success ? 'bg-green-100 dark:bg-green-900/30' : 'bg-red-100 dark:bg-red-900/30'}`}>52{log.success ? <CheckCircle2 className='w-5 h-5 mt-1 flex-shrink-0' /> : <XCircle className='w-5 h-5 mt-1 flex-shrink-0' />}53<div className='flex-grow'>54<p className='font-mono text-xs opacity-80'>{new Date(log.timestamp).toLocaleString()}</p>55<p className='font-semibold'>{log.details}</p>56{log.error_message && <p className='text-xs opacity-90'>{log.error_message}</p>}57</div>58</div>59))60)}61</CardContent>62</Card>63</div>64);65}666768