veza/apps/web/src/components/developer/WebhooksView.tsx

98 lines
4.2 KiB
TypeScript
Raw Normal View History

import React, { useState } from 'react';
import { Card } from '../ui/card';
import { Button } from '../ui/button';
import { Input } from '../ui/input';
import { Radio, Plus, Trash2, Zap, AlertTriangle } from 'lucide-react';
import { useToast } from '../../context/ToastContext';
interface Webhook {
id: string;
url: string;
events: string[];
status: 'active' | 'failed';
lastTriggered: string;
}
const MOCK_WEBHOOKS: Webhook[] = [
{ id: 'w1', url: 'https://api.myapp.com/veza-hook', events: ['track.upload', 'user.update'], status: 'active', lastTriggered: '2 hours ago' },
{ id: 'w2', url: 'https://hooks.slack.com/services/T000...', events: ['sales.new'], status: 'failed', lastTriggered: '1 day ago' },
];
export const WebhooksView: React.FC = () => {
const { addToast } = useToast();
const [webhooks, setWebhooks] = useState<Webhook[]>(MOCK_WEBHOOKS);
const [newUrl, setNewUrl] = useState('');
const handleCreate = () => {
if (!newUrl) return;
const newHook: Webhook = {
id: `w-${Date.now()}`,
url: newUrl,
events: ['all'],
status: 'active',
lastTriggered: 'Never'
};
setWebhooks([...webhooks, newHook]);
setNewUrl('');
addToast("Webhook created", "success");
};
const handleTest = (_id: string) => {
addToast(`Sending test payload to webhook...`, "info");
};
const handleDelete = (id: string) => {
setWebhooks(webhooks.filter(w => w.id !== id));
addToast("Webhook deleted", "info");
};
return (
<div className="space-y-8 animate-fadeIn pb-20">
<div>
<h2 className="text-2xl font-bold text-white mb-2">WEBHOOKS</h2>
<p className="text-gray-400 font-mono text-sm">Subscribe to real-time events.</p>
</div>
<Card variant="default">
<h3 className="font-bold text-white mb-4 flex items-center gap-2">
<Plus className="w-4 h-4 text-kodo-cyan" /> Add Endpoint
</h3>
<div className="flex gap-4">
<Input placeholder="https://your-domain.com/webhook" value={newUrl} onChange={(e) => setNewUrl(e.target.value)} className="flex-1" />
<Button variant="primary" onClick={handleCreate} disabled={!newUrl}>Create</Button>
</div>
</Card>
<div className="space-y-4">
{webhooks.map(hook => (
<Card key={hook.id} variant="gaming" className="flex flex-col md:flex-row items-start md:items-center justify-between p-4 gap-4">
<div className="flex items-start gap-4">
<div className={`p-2 rounded-full ${hook.status === 'active' ? 'bg-kodo-lime/10 text-kodo-lime' : 'bg-kodo-red/10 text-kodo-red'}`}>
{hook.status === 'active' ? <Radio className="w-5 h-5" /> : <AlertTriangle className="w-5 h-5" />}
</div>
<div>
<div className="font-bold text-white font-mono text-sm break-all">{hook.url}</div>
<div className="text-xs text-gray-400 mt-1 flex flex-wrap gap-2">
<span className="text-gray-500">Events:</span>
{hook.events.map(e => <span key={e} className="bg-white/5 px-1.5 rounded text-gray-300">{e}</span>)}
<span className="text-gray-500 ml-2">Last Triggered: {hook.lastTriggered}</span>
</div>
</div>
</div>
<div className="flex gap-2 w-full md:w-auto">
<Button variant="ghost" size="sm" className="flex-1 md:flex-none border border-kodo-steel" onClick={() => handleTest(hook.id)}>
<Zap className="w-4 h-4 mr-2" /> Test
</Button>
<Button variant="ghost" size="sm" className="flex-1 md:flex-none text-kodo-red hover:bg-kodo-red/10" onClick={() => handleDelete(hook.id)}>
<Trash2 className="w-4 h-4" />
</Button>
</div>
</Card>
))}
</div>
</div>
);
};