style(analytics): format JSX and object literals consistently
This commit is contained in:
@@ -112,8 +112,8 @@ export function AIDistributionPanel({
|
||||
Distribusi Analisis AI
|
||||
</CardTitle>
|
||||
<CardDescription className="text-xs">
|
||||
Sebaran tingkat keparahan dan rekomendasi dari {stats.total_analyzed} pesan
|
||||
yang dianalisis.
|
||||
Sebaran tingkat keparahan dan rekomendasi dari {stats.total_analyzed}{" "}
|
||||
pesan yang dianalisis.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
@@ -133,10 +133,7 @@ export function AIDistributionPanel({
|
||||
return (
|
||||
<div key={key} className="flex items-center gap-2">
|
||||
<span
|
||||
className={cn(
|
||||
"w-16 text-[10px] font-medium",
|
||||
s.color,
|
||||
)}
|
||||
className={cn("w-16 text-[10px] font-medium", s.color)}
|
||||
>
|
||||
{s.label}
|
||||
</span>
|
||||
@@ -172,10 +169,7 @@ export function AIDistributionPanel({
|
||||
return (
|
||||
<div key={key} className="flex items-center gap-2">
|
||||
<span
|
||||
className={cn(
|
||||
"w-16 text-[10px] font-medium",
|
||||
a.color,
|
||||
)}
|
||||
className={cn("w-16 text-[10px] font-medium", a.color)}
|
||||
>
|
||||
{a.label}
|
||||
</span>
|
||||
|
||||
@@ -31,22 +31,36 @@ export function AttachmentStatsPanel({
|
||||
);
|
||||
}
|
||||
|
||||
const uploadPct = stats.total_attachments > 0
|
||||
? Math.round((stats.uploaded / stats.total_attachments) * 100)
|
||||
: 0;
|
||||
const uploadPct =
|
||||
stats.total_attachments > 0
|
||||
? Math.round((stats.uploaded / stats.total_attachments) * 100)
|
||||
: 0;
|
||||
|
||||
const failedPct = stats.total_attachments > 0
|
||||
? Math.round((stats.failed / stats.total_attachments) * 100)
|
||||
: 0;
|
||||
const failedPct =
|
||||
stats.total_attachments > 0
|
||||
? Math.round((stats.failed / stats.total_attachments) * 100)
|
||||
: 0;
|
||||
|
||||
const totalSizeMB = stats.total_size_bytes / (1024 * 1024);
|
||||
|
||||
const cards = [
|
||||
{ label: "Total Media", value: formatNum(stats.total_attachments), accent: "text-foreground" },
|
||||
{
|
||||
label: "Total Media",
|
||||
value: formatNum(stats.total_attachments),
|
||||
accent: "text-foreground",
|
||||
},
|
||||
{ label: "Upload Success", value: `${uploadPct}%`, accent: "text-primary" },
|
||||
{ label: "Gagal Upload", value: `${failedPct}%`, accent: "text-accent" },
|
||||
{ label: "Total Ukuran", value: `${totalSizeMB.toFixed(1)} MB`, accent: "text-muted-foreground" },
|
||||
{ label: "Pengupload", value: formatNum(stats.unique_uploaders), accent: "text-primary" },
|
||||
{
|
||||
label: "Total Ukuran",
|
||||
value: `${totalSizeMB.toFixed(1)} MB`,
|
||||
accent: "text-muted-foreground",
|
||||
},
|
||||
{
|
||||
label: "Pengupload",
|
||||
value: formatNum(stats.unique_uploaders),
|
||||
accent: "text-primary",
|
||||
},
|
||||
];
|
||||
|
||||
const statusCards = [
|
||||
@@ -81,7 +95,9 @@ export function AttachmentStatsPanel({
|
||||
{stats.top_mime_type ? (
|
||||
<>
|
||||
Upload status media — tipe dominan:{" "}
|
||||
<span className="font-medium text-primary">{stats.top_mime_type}</span>
|
||||
<span className="font-medium text-primary">
|
||||
{stats.top_mime_type}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
"Upload status media di semua channel."
|
||||
@@ -98,7 +114,12 @@ export function AttachmentStatsPanel({
|
||||
<div className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
|
||||
{c.label}
|
||||
</div>
|
||||
<div className={cn("mt-1 font-mono text-lg font-bold tabular-nums", c.accent)}>
|
||||
<div
|
||||
className={cn(
|
||||
"mt-1 font-mono text-lg font-bold tabular-nums",
|
||||
c.accent,
|
||||
)}
|
||||
>
|
||||
{c.value}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,11 +16,26 @@ interface ModerationActionsPanelProps {
|
||||
}
|
||||
|
||||
const ACTION_LABELS: Record<string, { label: string; color: string }> = {
|
||||
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" },
|
||||
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<string, { label: string; color: string }> = {
|
||||
|
||||
@@ -142,17 +142,21 @@ export function ViolatorTable({ users, loading }: ViolatorTableProps) {
|
||||
</td>
|
||||
<td className="py-1.5 pr-4">
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{user.worst_flags?.length > 0
|
||||
? user.worst_flags.slice(0, 3).map((flag) => (
|
||||
<Badge
|
||||
key={flag}
|
||||
variant="outline"
|
||||
className="text-[8px] px-1 py-0 border-accent/30 text-accent"
|
||||
>
|
||||
{flag}
|
||||
</Badge>
|
||||
))
|
||||
: <span className="text-[10px] text-muted-foreground">—</span>}
|
||||
{user.worst_flags?.length > 0 ? (
|
||||
user.worst_flags.slice(0, 3).map((flag) => (
|
||||
<Badge
|
||||
key={flag}
|
||||
variant="outline"
|
||||
className="text-[8px] px-1 py-0 border-accent/30 text-accent"
|
||||
>
|
||||
{flag}
|
||||
</Badge>
|
||||
))
|
||||
) : (
|
||||
<span className="text-[10px] text-muted-foreground">
|
||||
—
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -119,7 +119,10 @@ export function AnalyticsPanel({ guildId, guildName }: AnalyticsPanelProps) {
|
||||
<motion.div variants={cardItem}>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<ViolatorTable users={violators} loading={loading} />
|
||||
<ModerationActionsPanel actions={moderationActions} loading={loading} />
|
||||
<ModerationActionsPanel
|
||||
actions={moderationActions}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
@@ -522,7 +522,9 @@ export function fetchModerationActions(params: {
|
||||
...(params.hours && { hours: String(params.hours) }),
|
||||
...(params.limit && { limit: String(params.limit) }),
|
||||
});
|
||||
return request<ModerationActionRecord[]>(`/api/analytics/moderation-actions?${sp}`);
|
||||
return request<ModerationActionRecord[]>(
|
||||
`/api/analytics/moderation-actions?${sp}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function fetchAIStats(params: {
|
||||
|
||||
Reference in New Issue
Block a user