refactor(fe): overhaul — split types, type-safe WS, remove dead deps/code

- Remove unused deps: gsap, @tanstack/react-query, three, r3f, drei, autoprefixer
- Split types from shared/api/client.ts into entities/{guild,voice,media,ui,recording,dashboard}
- Type-safe WebSocket handlers using WsEventMap — 0 'as' casts in App.tsx
- Replace gsap with framer-motion in AuthOverlay
- Remove dead code: useMascotSummary, gsapCardHover, live/components/index barrel
- Fix bare console calls → use createLogger from @bete/shared/logger
- Clean up unused imports and variables (aiVariant, etc.)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-06-16 22:44:28 +07:00
co-authored by Claude
parent 9af2d7d4dd
commit 752d144dc0
45 changed files with 535 additions and 1314 deletions
+10 -10
View File
@@ -1,7 +1,7 @@
import { motion } from "framer-motion";
import { Lock } from "lucide-react";
import { useEffect, useState } from "react";
import { login } from "../../shared/api/client";
import { useGsapTransition } from "../../shared/hooks/useGsapTransition";
import { useState } from "react";
import { login } from "../../shared/api/client.js";
import {
Button,
Card,
@@ -20,11 +20,6 @@ export function AuthOverlay({ onAuthenticated }: AuthOverlayProps) {
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const { pageRef, animateIn } = useGsapTransition("auth");
useEffect(() => {
animateIn();
}, [animateIn]);
const handleSubmit = async (e: { preventDefault: () => void }) => {
e.preventDefault();
@@ -42,7 +37,12 @@ export function AuthOverlay({ onAuthenticated }: AuthOverlayProps) {
};
return (
<div ref={pageRef} className="flex items-center justify-center p-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, ease: "easeOut" }}
className="flex items-center justify-center p-4"
>
<Card className="w-full max-w-md border-primary/30 shadow-lg shadow-primary/10">
<CardHeader className="text-center">
<div className="mx-auto mb-4 flex items-center justify-center">
@@ -77,6 +77,6 @@ export function AuthOverlay({ onAuthenticated }: AuthOverlayProps) {
</form>
</CardContent>
</Card>
</div>
</motion.div>
);
}
@@ -1,5 +1,5 @@
import { Hash } from "lucide-react";
import type { DashboardChannelDetail } from "../../../shared/api/client";
import type { DashboardChannelDetail } from "../../../entities/dashboard/types.js";
import { ProfileDetail } from "../../../shared/ui";
interface ChannelProfileDetailProps {
@@ -1,5 +1,5 @@
import { Hash } from "lucide-react";
import type { DashboardChannel } from "../../../shared/api/client";
import type { DashboardChannel } from "../../../entities/dashboard/types.js";
import type { SummaryItem } from "../../../shared/ui";
import { SummaryList } from "../../../shared/ui";
@@ -10,7 +10,7 @@ import {
Users,
} from "lucide-react";
import { cardItem, cardStagger } from "../../../shared/hooks/useFramerStagger";
import { useUIState } from "../../../shared/hooks/useUIState";
import { useUIState } from "../../../shared/hooks/useUIState.js";
import { cn } from "../../../shared/lib/utils";
import {
Card,
@@ -168,7 +168,7 @@ export function DashboardStatsContent() {
</p>
) : (
<div className="space-y-2">
{stats.top_channels.map((ch, i) => (
{stats.top_channels.map((ch) => (
<div
key={ch.channel_id}
className="flex items-center justify-between rounded-lg bg-muted/50 px-3 py-2 text-sm"
@@ -1,5 +1,5 @@
import { User } from "lucide-react";
import type { DashboardUserDetail } from "../../../shared/api/client";
import type { DashboardUserDetail } from "../../../entities/dashboard/types.js";
import { ProfileDetail } from "../../../shared/ui";
interface UserProfileDetailProps {
@@ -1,5 +1,5 @@
import { User } from "lucide-react";
import type { DashboardUser } from "../../../shared/api/client";
import type { DashboardUser } from "../../../entities/dashboard/types.js";
import type { SummaryItem } from "../../../shared/ui";
import { SummaryList } from "../../../shared/ui";
@@ -1,20 +1,18 @@
import { useCallback, useEffect, useState } from "react";
import type { DashboardStats } from "../../../entities/dashboard/types.js";
import {
type DashboardChannel,
type DashboardChannelDetail,
type DashboardStats,
type DashboardUser,
type DashboardUserDetail,
getDashboardChannelDetail,
getDashboardStats,
getDashboardUserDetail,
listDashboardChannels,
listDashboardUsers,
} from "../../../shared/api/client";
} from "../../../shared/api/client.js";
import { useItemDetail } from "../../../shared/hooks/useItemDetail";
import { usePaginatedList } from "../../../shared/hooks/usePaginatedList";
const logger = console;
import { createLogger } from "../../../shared/lib/logger.js";
const logger = createLogger("use-dashboard");
/**
* Fetch dashboard aggregate stats.
@@ -33,7 +31,7 @@ export function useDashboardStats() {
} catch (e) {
const msg = e instanceof Error ? e.message : "Failed to load stats";
setError(msg);
logger.error("[useDashboardStats]", msg);
logger.error("[useDashboardStats]", { error: msg });
} finally {
setLoading(false);
}
@@ -1,4 +1,4 @@
import type { ActiveSpeaker } from "../../../shared/api/client";
import type { ActiveSpeaker } from "../../../entities/voice/types.js";
import { EmptyStateMascot } from "../../../widgets/mascot/MascotImage";
interface ActiveSpeakersProps {
@@ -1,5 +1,5 @@
import { MonitorUp, Music2 } from "lucide-react";
import type { MediaItem } from "../../../shared/api/client";
import type { MediaItem } from "../../../entities/media/types.js";
import { Badge } from "../../../shared/ui";
interface NowPlayingProps {
@@ -2,7 +2,7 @@
import { Download, Mic, Trash2 } from "lucide-react";
import { useCallback, useEffect, useState } from "react";
import type { VoiceRecording } from "../../../shared/api/client";
import type { VoiceRecording } from "../../../entities/recording/types.js";
import { deleteRecording, listRecordings } from "../../../shared/api/client";
import { formatBytes, formatDate } from "../../../shared/lib/utils";
import { Badge, Button, Skeleton } from "../../../shared/ui";
@@ -1,5 +1,6 @@
import { Headphones, Radio } from "lucide-react";
import type { Channel, Guild, VoiceStatus } from "../../../shared/api/client";
import type { Channel, Guild } from "../../../entities/guild/types.js";
import type { VoiceStatus } from "../../../entities/voice/types.js";
import { Button, Select } from "../../../shared/ui";
import { MicLevelMeter } from "./MicLevelMeter";
@@ -1,11 +0,0 @@
// ─── Live feature barrel export ─────────────────────────────────────────────
export { ActiveSpeakers } from "./ActiveSpeakers";
export { AudioVisualizer } from "./AudioVisualizer";
export { MicLevelMeter } from "./MicLevelMeter";
export { MusicSubPanel } from "./MusicSubPanel";
export { NowPlaying } from "./NowPlaying";
export { RecordingsSubPanel } from "./RecordingsSubPanel";
export { ScreenSubPanel } from "./ScreenSubPanel";
export { VoiceConnectionCard } from "./VoiceConnectionCard";
export { WaveformPlayer } from "./WaveformPlayer";
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from "react";
import type { MediaState } from "../../../shared/api/client";
import type { MediaState } from "../../../entities/media/types.js";
import {
getMediaStatus,
queueMedia,
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useState } from "react";
import type { Channel, Guild, VoiceStatus } from "../../../shared/api/client";
import type { Channel, Guild } from "../../../entities/guild/types.js";
import type { VoiceStatus } from "../../../entities/voice/types.js";
import {
connectVoice,
disconnectVoice,
@@ -2,13 +2,9 @@
import { motion } from "framer-motion";
import { Mic, MonitorUp, Music2 } from "lucide-react";
import type {
ActiveSpeaker,
Channel,
Guild,
MediaState,
VoiceStatus,
} from "../../shared/api/client";
import type { Channel, Guild } from "../../entities/guild/types.js";
import type { MediaState } from "../../entities/media/types.js";
import type { ActiveSpeaker, VoiceStatus } from "../../entities/voice/types.js";
import { cardItem, cardStagger } from "../../shared/hooks/useFramerStagger";
import {
Card,
@@ -1,5 +1,5 @@
import { parseMetadata } from "../../../entities/message/types";
import type { MessageRecord } from "../../../shared/api/client";
import type { MessageRecord } from "../../../entities/message/types.js";
import { parseMetadata } from "../../../shared/lib/utils.js";
import { EmptyStateMascot } from "../../../widgets/mascot/MascotImage";
interface ImageItem {
@@ -73,7 +73,7 @@ export function ImageGrid({ messages }: { messages: MessageRecord[] }) {
return (
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4">
{images.map((image, index) => {
{images.map((image) => {
// Stable key using message.id + url
const stableKey = `${image.message.id}-${image.kind}-${image.url}`;
return (
@@ -9,8 +9,8 @@ import {
Trash2,
} from "lucide-react";
import { Fragment, useMemo, useState } from "react";
import { parseMetadata } from "../../../entities/message/types";
import type { MessageRecord } from "../../../shared/api/client";
import type { MessageRecord } from "../../../entities/message/types.js";
import { parseMetadata } from "../../../shared/lib/utils.js";
import { Badge, Button, Skeleton, StatusBadge } from "../../../shared/ui";
const CUSTOM_EMOJI_REGEX = /<(a)?:([a-zA-Z0-9_]+):(\d+)>/g;
@@ -72,12 +72,6 @@ function parseStringList(value?: string | null): string[] {
}
}
function aiVariant(status: string) {
if (status === "clean") return "success";
if (status === "flagged" || status === "error") return "destructive";
return "secondary";
}
function severityColor(severity: string) {
switch (severity) {
case "critical":
@@ -1,6 +1,6 @@
import { motion } from "framer-motion";
import { useEffect, useMemo, useRef } from "react";
import type { MessageRecord } from "../../../shared/api/client";
import type { MessageRecord } from "../../../entities/message/types.js";
import { cardItem, cardStagger } from "../../../shared/hooks/useFramerStagger";
import { ScrollArea } from "../../../shared/ui";
import { EmptyStateMascot } from "../../../widgets/mascot/MascotImage";
@@ -1,5 +1,5 @@
import { useCallback, useRef, useState } from "react";
import type { MessageRecord } from "../../../shared/api/client";
import type { MessageRecord } from "../../../entities/message/types.js";
import {
listMessages,
reanalyzeErrorBatch,
@@ -1,7 +1,8 @@
import { motion } from "framer-motion";
import { Filter, RotateCw, Search, X } from "lucide-react";
import { useMemo, useState } from "react";
import { type MessageRecord, request } from "../../shared/api/client";
import type { MessageRecord } from "../../shared/api/client";
import { request } from "../../shared/api/client";
import { cardItem, cardStagger } from "../../shared/hooks/useFramerStagger";
import {
Badge,