refactor: clean up code formatting and improve consistency across multiple components
Deploy to VPS / deploy (push) Failing after 1m59s

This commit is contained in:
asepharyana
2026-07-26 15:39:41 +07:00
parent c2502a0e5f
commit 300512b2cb
12 changed files with 47 additions and 108 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.5.5/schema.json", "$schema": "https://biomejs.dev/schemas/2.2.0/schema.json",
"vcs": { "vcs": {
"enabled": true, "enabled": true,
"clientKind": "git", "clientKind": "git",
@@ -30,7 +30,6 @@
"linter": { "linter": {
"enabled": true, "enabled": true,
"rules": { "rules": {
"preset": "recommended",
"style": { "style": {
"noNonNullAssertion": "warn", "noNonNullAssertion": "warn",
"useNodejsImportProtocol": "warn" "useNodejsImportProtocol": "warn"
@@ -41,6 +40,7 @@
"noArrayIndexKey": "warn" "noArrayIndexKey": "warn"
}, },
"a11y": { "a11y": {
"useSemanticElements": "off",
"useButtonType": "off", "useButtonType": "off",
"noAutofocus": "off" "noAutofocus": "off"
}, },
@@ -111,10 +111,8 @@ export default function AnalysisPage() {
variant="outline" variant="outline"
className={cn( className={cn(
"text-[10px] px-1.5 py-0 h-4", "text-[10px] px-1.5 py-0 h-4",
msg.ai_status === "clean" && msg.ai_status === "clean" && "text-green-500",
"text-green-500", msg.ai_status === "flagged" && "text-red-500",
msg.ai_status === "flagged" &&
"text-red-500",
)} )}
> >
{msg.ai_status} {msg.ai_status}
@@ -127,17 +125,17 @@ export default function AnalysisPage() {
{msg.ai_moderation_flags && {msg.ai_moderation_flags &&
msg.ai_moderation_flags !== "[]" && ( msg.ai_moderation_flags !== "[]" && (
<div className="flex flex-wrap gap-1"> <div className="flex flex-wrap gap-1">
{safeParseJsonArray( {safeParseJsonArray(msg.ai_moderation_flags).map(
msg.ai_moderation_flags, (flag) => (
).map((flag) => ( <Badge
<Badge key={flag}
key={flag} variant="destructive"
variant="destructive" className="text-[10px] px-1.5 py-0 h-4"
className="text-[10px] px-1.5 py-0 h-4" >
> {flag}
{flag} </Badge>
</Badge> ),
))} )}
</div> </div>
)} )}
@@ -15,7 +15,7 @@ import {
} from "lucide-react"; } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { GuildSelector } from "@/components/shared/guild-selector";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
@@ -23,7 +23,6 @@ import { Input } from "@/components/ui/input";
import { Progress } from "@/components/ui/progress"; import { Progress } from "@/components/ui/progress";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { GuildSelector } from "@/components/shared/guild-selector";
import { dashboardApi } from "@/lib/api"; import { dashboardApi } from "@/lib/api";
import { formatNumber } from "@/lib/format"; import { formatNumber } from "@/lib/format";
import type { import type {
@@ -189,11 +188,7 @@ function StatsView() {
value={stats.total_messages} value={stats.total_messages}
icon={Hash} icon={Hash}
/> />
<StatCard <StatCard label="Today" value={stats.today_messages} icon={Clock} />
label="Today"
value={stats.today_messages}
icon={Clock}
/>
<StatCard label="Users" value={stats.total_users} icon={Users} /> <StatCard label="Users" value={stats.total_users} icon={Users} />
<StatCard <StatCard
label="Active 24h" label="Active 24h"
@@ -239,19 +234,15 @@ function StatsView() {
</p> </p>
) : ( ) : (
<div className="space-y-2"> <div className="space-y-2">
{stats.top_channels.map((ch, i) => { {stats.top_channels.map((ch, _i) => {
const maxCount = stats.top_channels[0].message_count; const maxCount = stats.top_channels[0].message_count;
const pct = const pct =
maxCount > 0 maxCount > 0 ? (ch.message_count / maxCount) * 100 : 0;
? (ch.message_count / maxCount) * 100
: 0;
return ( return (
<div key={ch.channel_id} className="space-y-1"> <div key={ch.channel_id} className="space-y-1">
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between text-sm">
<span className="truncate font-medium"> <span className="truncate font-medium">
# #{ch.channel_name ?? ch.channel_id.slice(0, 8)}
{ch.channel_name ??
ch.channel_id.slice(0, 8)}
</span> </span>
<span className="text-muted-foreground tabular-nums"> <span className="text-muted-foreground tabular-nums">
{formatNumber(ch.message_count)} {formatNumber(ch.message_count)}
@@ -279,9 +270,7 @@ function StatsView() {
<div className="text-2xl font-bold tabular-nums"> <div className="text-2xl font-bold tabular-nums">
{stats.moderation_overview.pending} {stats.moderation_overview.pending}
</div> </div>
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">Pending</div>
Pending
</div>
</div> </div>
<div className="rounded-lg bg-yellow-500/10 p-3 text-center space-y-1.5"> <div className="rounded-lg bg-yellow-500/10 p-3 text-center space-y-1.5">
<div className="text-2xl font-bold tabular-nums text-yellow-500"> <div className="text-2xl font-bold tabular-nums text-yellow-500">
@@ -5,8 +5,8 @@ import { Suspense } from "react";
import { Header } from "@/components/layout/header"; import { Header } from "@/components/layout/header";
import { MobileTabBar } from "@/components/layout/mobile-tab-bar"; import { MobileTabBar } from "@/components/layout/mobile-tab-bar";
import { Sidebar } from "@/components/layout/sidebar"; import { Sidebar } from "@/components/layout/sidebar";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import { MascotChatbot } from "@/components/mascot/mascot-chatbot"; import { MascotChatbot } from "@/components/mascot/mascot-chatbot";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import { WsProvider } from "@/lib/ws/context"; import { WsProvider } from "@/lib/ws/context";
function LoadingFallback() { function LoadingFallback() {
@@ -1,13 +1,6 @@
"use client"; "use client";
import { import { Disc3, Music, Play, SkipForward, Square, Volume2 } from "lucide-react";
Disc3,
Music,
Play,
SkipForward,
Square,
Volume2,
} from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
@@ -12,7 +12,7 @@ import {
} from "lucide-react"; } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { GuildSelector } from "@/components/shared/guild-selector";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -35,7 +35,6 @@ import {
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { GuildSelector } from "@/components/shared/guild-selector";
import { messagesApi, voiceApi } from "@/lib/api"; import { messagesApi, voiceApi } from "@/lib/api";
import { formatBytes, safeParseJsonArray } from "@/lib/format"; import { formatBytes, safeParseJsonArray } from "@/lib/format";
import type { AttachmentRecord, Channel, MessageRecord } from "@/lib/types"; import type { AttachmentRecord, Channel, MessageRecord } from "@/lib/types";
@@ -244,9 +243,7 @@ export default function MessagesPage() {
<GuildSelector value={guildId} onChange={setGuildId} /> <GuildSelector value={guildId} onChange={setGuildId} />
<div className="flex flex-col items-center justify-center py-20 text-center"> <div className="flex flex-col items-center justify-center py-20 text-center">
<AlertCircle className="size-10 text-destructive mb-3" /> <AlertCircle className="size-10 text-destructive mb-3" />
<p className="text-sm text-muted-foreground mb-4 max-w-sm"> <p className="text-sm text-muted-foreground mb-4 max-w-sm">{error}</p>
{error}
</p>
<Button variant="outline" onClick={fetchMessages}> <Button variant="outline" onClick={fetchMessages}>
<RefreshCw className="size-4 mr-2" /> <RefreshCw className="size-4 mr-2" />
Retry Retry
@@ -490,9 +487,7 @@ export default function MessagesPage() {
{detailMessage.username} {detailMessage.username}
</span> </span>
<span className="text-xs text-muted-foreground"> <span className="text-xs text-muted-foreground">
{new Date( {new Date(detailMessage.created_at).toLocaleString()}
detailMessage.created_at,
).toLocaleString()}
</span> </span>
{detailMessage.type === "deleted" && ( {detailMessage.type === "deleted" && (
<Badge variant="destructive" className="text-[10px]"> <Badge variant="destructive" className="text-[10px]">
@@ -787,18 +782,14 @@ function MessageCard({
</p> </p>
)} )}
{msg.ai_confidence !== undefined && {msg.ai_confidence !== undefined && msg.ai_confidence !== null && (
msg.ai_confidence !== null && ( <div className="flex items-center gap-2 max-w-40">
<div className="flex items-center gap-2 max-w-40"> <Progress value={msg.ai_confidence * 100} className="h-1.5" />
<Progress <span className="text-[11px] text-muted-foreground tabular-nums shrink-0">
value={msg.ai_confidence * 100} {(msg.ai_confidence * 100).toFixed(0)}%
className="h-1.5" </span>
/> </div>
<span className="text-[11px] text-muted-foreground tabular-nums shrink-0"> )}
{(msg.ai_confidence * 100).toFixed(0)}%
</span>
</div>
)}
<div className="flex gap-1.5 pt-0.5"> <div className="flex gap-1.5 pt-0.5">
<Button <Button
@@ -91,9 +91,7 @@ export default function RecordingsPage() {
{rec.username} {rec.username}
</p> </p>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
{rec.channel_name ?? {rec.channel_name ?? rec.channel_id ?? "Unknown channel"}
rec.channel_id ??
"Unknown channel"}
{" — "} {" — "}
{new Date(rec.created_at).toLocaleString()} {new Date(rec.created_at).toLocaleString()}
</p> </p>
@@ -108,9 +106,7 @@ export default function RecordingsPage() {
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
onClick={() => onClick={() => window.open(rec.download_url, "_blank")}
window.open(rec.download_url!, "_blank")
}
> >
<Download className="size-4" /> <Download className="size-4" />
</Button> </Button>
@@ -1,12 +1,6 @@
"use client"; "use client";
import { import { Moon, Server, Shield, Sun, Wifi } from "lucide-react";
Moon,
Server,
Shield,
Sun,
Wifi,
} from "lucide-react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
@@ -85,9 +79,7 @@ export default function SettingsPage() {
variant={statusConfig.variant} variant={statusConfig.variant}
className="gap-1.5 px-2.5 py-1" className="gap-1.5 px-2.5 py-1"
> >
<span <span className={cn("size-1.5 rounded-full", statusConfig.dot)} />
className={cn("size-1.5 rounded-full", statusConfig.dot)}
/>
{statusConfig.label} {statusConfig.label}
</Badge> </Badge>
</div> </div>
@@ -159,9 +151,7 @@ export default function SettingsPage() {
<Separator /> <Separator />
<ConfigRow <ConfigRow
label="Auto-Delete Flagged" label="Auto-Delete Flagged"
value={ value={config.autoDeleteFlaggedEnabled ? "Enabled" : "Disabled"}
config.autoDeleteFlaggedEnabled ? "Enabled" : "Disabled"
}
/> />
</div> </div>
) : ( ) : (
@@ -197,13 +187,7 @@ export default function SettingsPage() {
); );
} }
function ConfigRow({ function ConfigRow({ label, value }: { label: string; value: string }) {
label,
value,
}: {
label: string;
value: string;
}) {
return ( return (
<div className="flex items-center justify-between py-1"> <div className="flex items-center justify-between py-1">
<span className="text-muted-foreground">{label}</span> <span className="text-muted-foreground">{label}</span>
@@ -31,9 +31,7 @@ export default function VoicePage() {
const [voiceStatus, setVoiceStatus] = useState<VoiceStatus | null>(null); const [voiceStatus, setVoiceStatus] = useState<VoiceStatus | null>(null);
const [speakers, setSpeakers] = useState<ActiveSpeaker[]>([]); const [speakers, setSpeakers] = useState<ActiveSpeaker[]>([]);
const [guilds, setGuilds] = useState< const [guilds, setGuilds] = useState<Array<{ id: string; name: string }>>([]);
Array<{ id: string; name: string }>
>([]);
const [voiceChannels, setVoiceChannels] = useState< const [voiceChannels, setVoiceChannels] = useState<
Array<{ id: string; name: string }> Array<{ id: string; name: string }>
>([]); >([]);
@@ -74,9 +72,7 @@ export default function VoicePage() {
const unsubSpeaker = ws.on("voice_active_user", (user) => { const unsubSpeaker = ws.on("voice_active_user", (user) => {
const speaker = user as ActiveSpeaker; const speaker = user as ActiveSpeaker;
setSpeakers((prev) => { setSpeakers((prev) => {
const existing = prev.findIndex( const existing = prev.findIndex((s) => s.userId === speaker.userId);
(s) => s.userId === speaker.userId,
);
if (existing >= 0) { if (existing >= 0) {
const next = [...prev]; const next = [...prev];
next[existing] = speaker; next[existing] = speaker;
@@ -224,9 +220,7 @@ export default function VoicePage() {
) : ( ) : (
<Button <Button
onClick={handleConnect} onClick={handleConnect}
disabled={ disabled={voiceLoading || !selectedGuild || !selectedChannel}
voiceLoading || !selectedGuild || !selectedChannel
}
> >
{voiceLoading ? ( {voiceLoading ? (
<Loader2 className="size-4 animate-spin mr-1.5" /> <Loader2 className="size-4 animate-spin mr-1.5" />
@@ -286,9 +280,7 @@ export default function VoicePage() {
setMicActive(checked); setMicActive(checked);
try { try {
await voiceApi.sendCommand( await voiceApi.sendCommand(
checked checked ? "voice:transmit:start" : "voice:transmit:stop",
? "voice:transmit:start"
: "voice:transmit:stop",
); );
} catch { } catch {
setMicActive(!checked); setMicActive(!checked);
@@ -45,9 +45,7 @@ export function GuildSelector({
.getGuilds() .getGuilds()
.then(setGuilds) .then(setGuilds)
.catch((err) => .catch((err) =>
setError( setError(err instanceof Error ? err.message : "Failed to load guilds"),
err instanceof Error ? err.message : "Failed to load guilds",
),
) )
.finally(() => setLoading(false)); .finally(() => setLoading(false));
}, []); }, []);
+1 -3
View File
@@ -17,9 +17,7 @@ export function formatBytes(bytes: number): string {
/** /**
* Safely parse a JSON string into an array. * Safely parse a JSON string into an array.
*/ */
export function safeParseJsonArray( export function safeParseJsonArray(value: string | null | undefined): string[] {
value: string | null | undefined,
): string[] {
if (!value) return []; if (!value) return [];
try { try {
const parsed = JSON.parse(value); const parsed = JSON.parse(value);
+1 -1
View File
@@ -2,11 +2,11 @@ import {
BarChart3, BarChart3,
Headphones, Headphones,
LayoutDashboard, LayoutDashboard,
type LucideIcon,
MessageSquare, MessageSquare,
Mic, Mic,
Music, Music,
Search, Search,
type LucideIcon,
} from "lucide-react"; } from "lucide-react";
export interface NavItem { export interface NavItem {