Files
whatsapp-inbox-platform/app/team/performance/page.tsx
Wira Basalamah adde003fba
Some checks failed
CI - Production Readiness / Verify (push) Has been cancelled
chore: initial project import
2026-04-21 09:29:29 +07:00

91 lines
2.6 KiB
TypeScript

import { redirect } from "next/navigation";
import { getSession } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { ShellPage } from "@/components/page-templates";
import { TablePlaceholder } from "@/components/placeholders";
const AVERAGE_RESPONSE_DIVISOR = 1;
function formatDuration(ms: number | null) {
if (!ms || ms < 0) {
return "-";
}
const totalMinutes = Math.floor(ms / 60000);
const minutes = totalMinutes % 60;
const seconds = Math.floor((ms % 60000) / 1000);
if (totalMinutes < 60) {
return `${totalMinutes}m ${seconds}s`;
}
const hours = Math.floor(totalMinutes / 60);
return `${hours}h ${minutes}m`;
}
export default async function TeamPerformancePage() {
const session = await getSession();
if (!session) {
redirect("/login");
}
if (session.role !== "admin_client") {
redirect("/unauthorized");
}
const agents = await prisma.user.findMany({
where: {
tenantId: session.tenantId,
role: { code: "AGENT" }
},
include: {
assignedConversations: {
select: {
status: true,
firstMessageAt: true,
lastOutboundAt: true
}
}
},
orderBy: { fullName: "asc" }
});
const rows = agents.map((agent) => {
const handled = agent.assignedConversations.length;
const resolved = agent.assignedConversations.filter((item) => item.status === "RESOLVED").length;
const workload = agent.assignedConversations.filter((item) => item.status === "OPEN" || item.status === "PENDING").length;
const responseSamples = agent.assignedConversations
.filter((item) => item.firstMessageAt && item.lastOutboundAt)
.map((item) => {
if (!item.firstMessageAt || !item.lastOutboundAt) {
return null;
}
return item.lastOutboundAt.getTime() - item.firstMessageAt.getTime();
})
.filter((item): item is number => item !== null && item >= 0);
const avgResponse =
responseSamples.length > 0
? formatDuration(
responseSamples.reduce((sum, value) => sum + value, 0) /
Math.max(AVERAGE_RESPONSE_DIVISOR, responseSamples.length)
)
: "-";
return [agent.fullName, String(handled), avgResponse, String(resolved), `${workload} open`];
});
return (
<ShellPage shell="admin" title="Team Performance" description="Leaderboard performa agent dan workload snapshot.">
<TablePlaceholder
title="Performance table"
columns={["Agent", "Handled", "Avg response", "Resolved", "Workload"]}
rows={rows}
/>
</ShellPage>
);
}