From 16696147359ee34aed91250a7a84b10afb9fe425 Mon Sep 17 00:00:00 2001 From: MythEclipse Date: Wed, 3 Jun 2026 18:18:27 +0700 Subject: [PATCH] `feat(analytics): add warned status tracking to summary cards, trend chart, and violator table` --- .../components/ModerationActionsPanel.tsx | 147 ++++++++++++++++++ .../analytics/components/SummaryCards.tsx | 11 ++ .../analytics/components/TrendChart.tsx | 18 ++- .../analytics/components/ViolatorTable.tsx | 30 +++- 4 files changed, 199 insertions(+), 7 deletions(-) create mode 100644 services/frontend/src/features/analytics/components/ModerationActionsPanel.tsx diff --git a/services/frontend/src/features/analytics/components/ModerationActionsPanel.tsx b/services/frontend/src/features/analytics/components/ModerationActionsPanel.tsx new file mode 100644 index 0000000..51d5cbc --- /dev/null +++ b/services/frontend/src/features/analytics/components/ModerationActionsPanel.tsx @@ -0,0 +1,147 @@ +import type { ModerationActionRecord } from "../../../shared/api/client"; +import { cn } from "../../../shared/lib/utils"; +import { + Badge, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, + ScrollArea, +} from "../../../shared/ui"; + +interface ModerationActionsPanelProps { + actions: ModerationActionRecord[]; + loading: boolean; +} + +const ACTION_LABELS: Record = { + delete_message: { label: "Hapus Pesan", color: "bg-red-100 text-red-700 border-red-200" }, + warn_user: { label: "Peringatan", color: "bg-yellow-100 text-yellow-700 border-yellow-200" }, + mute_user: { label: "Mute", color: "bg-orange-100 text-orange-700 border-orange-200" }, + kick_user: { label: "Kick", color: "bg-pink-100 text-pink-700 border-pink-200" }, + ban_user: { label: "Ban", color: "bg-accent/20 text-accent border-accent/30" }, +}; + +const STATUS_LABELS: Record = { + pending: { label: "Pending", color: "bg-gray-100 text-gray-600" }, + completed: { label: "Selesai", color: "bg-green-100 text-green-700" }, + executed: { label: "Tereksekusi", color: "bg-green-100 text-green-700" }, + failed: { label: "Gagal", color: "bg-red-100 text-red-700" }, +}; + +export function ModerationActionsPanel({ + actions, + loading, +}: ModerationActionsPanelProps) { + if (loading && !actions?.length) { + return ; + } + + if (!actions?.length) { + return ( + + + Belum ada aksi moderasi. + + + ); + } + + return ( + + +
+
+ + 🛡️ + Aksi Moderasi + + + Riwayat tindakan moderasi yang telah diambil. + +
+ {actions.length} aksi +
+
+ + +
+ {actions.map((action) => { + const actionStyle = ACTION_LABELS[action.action_type] ?? { + label: action.action_type, + color: "bg-gray-100 text-gray-600", + }; + const statusStyle = STATUS_LABELS[action.status] ?? { + label: action.status, + color: "bg-gray-100 text-gray-600", + }; + + return ( +
+
+
+ + {actionStyle.label} + + + {action.username} + +
+ + {statusStyle.label} + +
+ {action.reason && ( +

+ {action.reason} +

+ )} + {action.error && ( +

+ Error: {action.error} +

+ )} +
+ {new Date(action.created_at).toLocaleString("id-ID", { + day: "numeric", + month: "short", + hour: "2-digit", + minute: "2-digit", + })} +
+
+ ); + })} +
+
+
+
+ ); +} + +function LoadingBox() { + return ( + + + + Memuat data... + + + ); +} diff --git a/services/frontend/src/features/analytics/components/SummaryCards.tsx b/services/frontend/src/features/analytics/components/SummaryCards.tsx index deecd2b..de74d65 100644 --- a/services/frontend/src/features/analytics/components/SummaryCards.tsx +++ b/services/frontend/src/features/analytics/components/SummaryCards.tsx @@ -31,12 +31,18 @@ export function SummaryCards({ "Total Pesan": "💬", "Rata-rata/jam": "📊", Clean: "✅", + Warned: "⚠️", Flagged: "🚩", Pending: "⏳", "User Aktif": "👤", Channel: "📡", }; + const warnedPct = + messages && messages.total > 0 + ? Math.round((messages.warned / messages.total) * 100) + : 0; + const cards = [ { label: "Total Pesan", @@ -53,6 +59,11 @@ export function SummaryCards({ value: cleanPct > 0 ? `${cleanPct}%` : "—", accent: "text-primary", }, + { + label: "Warned", + value: warnedPct > 0 ? `${warnedPct}%` : "—", + accent: "text-yellow-600", + }, { label: "Flagged", value: flaggedPct > 0 ? `${flaggedPct}%` : "—", diff --git a/services/frontend/src/features/analytics/components/TrendChart.tsx b/services/frontend/src/features/analytics/components/TrendChart.tsx index a517151..d1f9e7a 100644 --- a/services/frontend/src/features/analytics/components/TrendChart.tsx +++ b/services/frontend/src/features/analytics/components/TrendChart.tsx @@ -24,7 +24,7 @@ export function TrendChart({ trend, loading }: TrendChartProps) { const data = trend.map((bucket) => ({ date: bucket.date, clean: bucket.clean, - warned: 0, + warned: bucket.warned, flagged: bucket.flagged, error: bucket.error, total: bucket.count, @@ -37,7 +37,7 @@ export function TrendChart({ trend, loading }: TrendChartProps) { Tren Harian - Volume pesan per hari dengan status moderasi — area pink = flagged. + Volume pesan per hari dengan status moderasi. @@ -45,7 +45,9 @@ export function TrendChart({ trend, loading }: TrendChartProps) {
+ +
@@ -129,6 +131,18 @@ export function TrendChart({ trend, loading }: TrendChartProps) { fill="url(#trendFillPink)" stroke="#FF9EBB" /> + + {data.map((item, index) => { const x = diff --git a/services/frontend/src/features/analytics/components/ViolatorTable.tsx b/services/frontend/src/features/analytics/components/ViolatorTable.tsx index dd05734..f6fdbbe 100644 --- a/services/frontend/src/features/analytics/components/ViolatorTable.tsx +++ b/services/frontend/src/features/analytics/components/ViolatorTable.tsx @@ -49,21 +49,23 @@ export function ViolatorTable({ users, loading }: ViolatorTableProps) { Pelanggar Terbanyak - Skor: flagged × 3. + Skor: flagged × 3 + warned. Flag terbanyak terakhir ditampilkan.
{users.length} pelanggar - + - + + + @@ -113,9 +115,12 @@ export function ViolatorTable({ users, loading }: ViolatorTableProps) { - + + ); })}
# User FlaggedSkorWarnedSkorFlag
{user.flagged_count} + + {user.warned_count > 0 ? user.warned_count : "—"} +
-
+
+
+ {user.worst_flags?.length > 0 + ? user.worst_flags.slice(0, 3).map((flag) => ( + + {flag} + + )) + : } +
+