85 lines
3.4 KiB
TypeScript
85 lines
3.4 KiB
TypeScript
import { ShellPage } from "@/components/page-templates";
|
|
import { SectionCard } from "@/components/ui";
|
|
import { headers } from "next/headers";
|
|
import { getSession } from "@/lib/auth";
|
|
import { prisma } from "@/lib/prisma";
|
|
import { redirect } from "next/navigation";
|
|
|
|
export default async function IntegrationsSettingsPage() {
|
|
const session = await getSession();
|
|
if (!session) {
|
|
redirect("/login");
|
|
}
|
|
|
|
if (session.role === "agent") {
|
|
redirect("/unauthorized");
|
|
}
|
|
|
|
const tenantFilter = session.role === "super_admin" ? {} : { tenantId: session.tenantId };
|
|
|
|
const [channels, recentWebhook] = await Promise.all([
|
|
prisma.channel.findMany({
|
|
where: tenantFilter,
|
|
orderBy: { createdAt: "desc" }
|
|
}),
|
|
prisma.webhookEvent.findMany({
|
|
where: tenantFilter,
|
|
orderBy: { createdAt: "desc" },
|
|
take: 4
|
|
})
|
|
]);
|
|
|
|
const host = (await headers()).get("host");
|
|
const webhookBase = host ? `${process.env.NODE_ENV === "production" ? "https" : "http"}://${host}` : "";
|
|
|
|
const connectedCount = channels.filter((channel) => channel.status === "CONNECTED").length;
|
|
const failedCount = channels.filter((channel) => channel.status === "ERROR").length;
|
|
|
|
return (
|
|
<ShellPage
|
|
shell="admin"
|
|
title="Webhook / Integration Settings"
|
|
description="Status provider, webhook URL, dan reconnection action."
|
|
>
|
|
<div className="grid gap-6 xl:grid-cols-2">
|
|
<SectionCard title="Provider config">
|
|
<p className="text-sm text-on-surface-variant">
|
|
Webhook URL: {webhookBase ? `${webhookBase}/api/webhooks/whatsapp` : "/api/webhooks/whatsapp"}
|
|
</p>
|
|
<p className="text-sm text-on-surface-variant">
|
|
Connected: {connectedCount} • Error: {failedCount} • Total channels: {channels.length}
|
|
</p>
|
|
<ul className="mt-3 space-y-2">
|
|
{channels.map((channel) => (
|
|
<li key={channel.id} className="rounded-xl border border-line bg-surface-container p-3">
|
|
<p className="font-medium text-ink">{channel.channelName}</p>
|
|
<p className="text-sm text-on-surface-variant">Status: {channel.status}</p>
|
|
<p className="text-sm text-outline">Provider: {channel.provider}</p>
|
|
<p className="text-xs text-outline">
|
|
WABA ID: {channel.wabaId ?? "N/A"} • Phone ID: {channel.phoneNumberId ?? "N/A"}
|
|
</p>
|
|
</li>
|
|
))}
|
|
{channels.length === 0 ? <p className="text-sm text-on-surface-variant">Tidak ada channel terhubung.</p> : null}
|
|
</ul>
|
|
</SectionCard>
|
|
<SectionCard title="Health state">
|
|
<ul className="space-y-2">
|
|
{recentWebhook.map((event) => (
|
|
<li key={event.id} className="rounded-xl border border-line bg-surface-container p-3">
|
|
<p className="text-sm text-on-surface-variant">
|
|
{event.eventType} • {event.processStatus}
|
|
</p>
|
|
<p className="text-xs text-outline">
|
|
{event.createdAt.toLocaleString("id-ID", { dateStyle: "medium", timeStyle: "short" })}
|
|
</p>
|
|
</li>
|
|
))}
|
|
{recentWebhook.length === 0 ? <p className="text-sm text-on-surface-variant">Belum ada event webhook terbaru.</p> : null}
|
|
</ul>
|
|
</SectionCard>
|
|
</div>
|
|
</ShellPage>
|
|
);
|
|
}
|