"use client"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslations } from "next-intl"; import { Card, ConfirmModal } from "@/shared/components"; import { AddWebhookWizard } from "./components/AddWebhookWizard"; import { HowItWorksSidebar } from "./components/HowItWorksSidebar"; import { WebhooksList } from "./components/WebhooksList"; import type { WebhookItem } from "./components/WebhookCard"; type FeedbackState = { type: "success" | "error"; message: string } | null; function getStatus(wh: WebhookItem): "active" | "inactive" | "errored" { if (!wh.enabled) return "inactive"; if (wh.failure_count > 0 || (wh.last_status !== null && wh.last_status >= 400)) return "errored"; return "active"; } export function WebhooksPageClient() { const t = useTranslations("webhooks"); const [webhooks, setWebhooks] = useState([]); const [loading, setLoading] = useState(true); const [testingId, setTestingId] = useState(null); const [feedback, setFeedback] = useState(null); const [wizardOpen, setWizardOpen] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const [deleting, setDeleting] = useState(false); const load = useCallback(async () => { setLoading(true); try { const res = await fetch("/api/webhooks"); const data = await res.json().catch(() => ({})); if (!res.ok) throw new Error(data.error || t("loadFailed")); setWebhooks(Array.isArray(data.webhooks) ? data.webhooks : []); } catch (err) { setFeedback({ type: "error", message: err instanceof Error ? err.message : t("loadFailed"), }); } finally { setLoading(false); } }, [t]); useEffect(() => { void load(); }, [load]); const stats = useMemo( () => webhooks.reduce( (acc, wh) => { acc.total += 1; acc[getStatus(wh)] += 1; return acc; }, { total: 0, active: 0, inactive: 0, errored: 0 } ), [webhooks] ); const handleTest = async (wh: WebhookItem) => { setTestingId(wh.id); setFeedback(null); try { const res = await fetch(`/api/webhooks/${wh.id}/test`, { method: "POST" }); const data = await res.json().catch(() => ({})); if (!res.ok || data.delivered === false) throw new Error(data.error || t("testFailed")); setFeedback({ type: "success", message: t("testSuccess") }); await load(); } catch (err) { setFeedback({ type: "error", message: err instanceof Error ? err.message : t("testFailed"), }); } finally { setTestingId(null); } }; const handleToggleEnabled = async (wh: WebhookItem) => { setFeedback(null); try { const res = await fetch(`/api/webhooks/${wh.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ enabled: !wh.enabled }), }); const data = await res.json().catch(() => ({})); if (!res.ok) throw new Error(data.error || t("saveFailed")); setWebhooks((prev) => prev.map((item) => (item.id === wh.id ? { ...item, enabled: !wh.enabled } : item)) ); } catch (err) { setFeedback({ type: "error", message: err instanceof Error ? err.message : t("saveFailed"), }); } }; const handleDelete = async () => { if (!deleteTarget) return; setDeleting(true); setFeedback(null); try { const res = await fetch(`/api/webhooks/${deleteTarget.id}`, { method: "DELETE" }); const data = await res.json().catch(() => ({})); if (!res.ok) throw new Error(data.error || t("deleteFailed")); setWebhooks((prev) => prev.filter((w) => w.id !== deleteTarget.id)); setDeleteTarget(null); setFeedback({ type: "success", message: t("deleteSuccess") }); } catch (err) { setFeedback({ type: "error", message: err instanceof Error ? err.message : t("deleteFailed"), }); } finally { setDeleting(false); } }; const tFn = (key: string, opts?: Record) => opts ? t(key as any, opts as any) : t(key as any); return (

{t("title")}

{t("description")}

{feedback && (
{feedback.message}
)}
{[ { label: t("total"), value: stats.total, icon: "webhook", tone: "text-primary" }, { label: t("active"), value: stats.active, icon: "check_circle", tone: "text-emerald-500", }, { label: t("inactive"), value: stats.inactive, icon: "pause_circle", tone: "text-text-muted", }, { label: t("errored"), value: stats.errored, icon: "error", tone: "text-red-500" }, ].map((stat) => (

{stat.label}

{stat.value}

{stat.icon}
))}

{t("configuredWebhooks")}

{t("configuredWebhooksDesc")}

void handleTest(wh)} onToggleEnabled={(wh) => void handleToggleEnabled(wh)} onEdit={() => setWizardOpen(true)} onDelete={setDeleteTarget} />
setWizardOpen(false)} onCreated={async () => { await load(); }} t={tFn} /> setDeleteTarget(null)} onConfirm={handleDelete} title={t("delete")} message={t("deleteConfirm")} confirmText={t("delete")} cancelText={t("wizard.cancel")} loading={deleting} />
); }