Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
parkpow
GitHub Repository: parkpow/deep-license-plate-recognition
Path: blob/master/gate-controller/components/WebhookLogsTab.tsx
1072 views
1
'use client';
2
3
import { useState, useEffect, useRef } from 'react';
4
import { listen } from '@tauri-apps/api/event';
5
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
6
import { Button } from '@/components/ui/button';
7
import { CheckCircle2, XCircle, Trash2 } from 'lucide-react';
8
9
interface WebhookLog {
10
timestamp: string;
11
success: boolean;
12
details: string;
13
error_message: string | null;
14
}
15
16
export function WebhookLogsTab() {
17
const [logs, setLogs] = useState<WebhookLog[]>([]);
18
const scrollRef = useRef<HTMLDivElement>(null);
19
20
useEffect(() => {
21
const unlisten = listen<WebhookLog>('webhook-log', (event) => {
22
setLogs((prevLogs) => [event.payload, ...prevLogs].slice(0, 100)); // Keep last 100 logs
23
});
24
25
return () => {
26
unlisten.then(f => f());
27
};
28
}, []);
29
30
useEffect(() => {
31
// Scroll to top when new logs arrive
32
if (scrollRef.current) {
33
scrollRef.current.scrollTop = 0;
34
}
35
}, [logs]);
36
37
return (
38
<div className="p-4 h-full flex flex-col space-y-4">
39
<Card className='h-full flex flex-col'>
40
<CardHeader className='flex-shrink-0 flex flex-row items-center justify-between'>
41
<CardTitle>Real-time Webhook Logs</CardTitle>
42
<Button variant='destructive' size='sm' onClick={() => setLogs([])} disabled={logs.length === 0}>
43
<Trash2 className='w-4 h-4 mr-2' />
44
Clear Logs
45
</Button>
46
</CardHeader>
47
<CardContent ref={scrollRef} className='flex-grow min-h-0 overflow-y-auto space-y-3 pr-2'>
48
{logs.length === 0 ? (
49
<div className='text-center text-muted-foreground pt-12'>Listening for webhook events...</div>
50
) : (
51
logs.map((log, index) => (
52
<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'}`}>
53
{log.success ? <CheckCircle2 className='w-5 h-5 mt-1 flex-shrink-0' /> : <XCircle className='w-5 h-5 mt-1 flex-shrink-0' />}
54
<div className='flex-grow'>
55
<p className='font-mono text-xs opacity-80'>{new Date(log.timestamp).toLocaleString()}</p>
56
<p className='font-semibold'>{log.details}</p>
57
{log.error_message && <p className='text-xs opacity-90'>{log.error_message}</p>}
58
</div>
59
</div>
60
))
61
)}
62
</CardContent>
63
</Card>
64
</div>
65
);
66
}
67
68