diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..4982420 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,37 @@ +{ + "name": "gmw-dashboard", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --host 0.0.0.0", + "build": "tsc --noEmit && vite build", + "preview": "vite preview --host 0.0.0.0", + "typecheck": "tsc --noEmit", + "lint": "biome check --diagnostic-level=error src/", + "format": "biome format --write src/" + }, + "dependencies": { + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-tabs": "^1.1.13", + "@tanstack/react-query": "^5.100.14", + "clsx": "^2.1.1", + "lucide-react": "^1.16.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "tailwind-merge": "^3.6.0" + }, + "devDependencies": { + "@biomejs/biome": "latest", + "@tailwindcss/postcss": "^4.3.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.2", + "autoprefixer": "^10.5.0", + "postcss": "^8.5.14", + "tailwindcss": "^4.3.0", + "typescript": "^5.9.3", + "vite": "^8.0.13" + } +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c0c97e1..7c0692b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,284 +1,125 @@ -import { Component, Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { DashboardLayout } from "./components/layout/DashboardLayout"; -import { LivePanel } from "./components/live/LivePanel"; -import { MessagesPanel } from "./components/messages/MessagesPanel"; -import { AuthOverlay } from "./components/layout/AuthOverlay"; -import { useDashboardSocket } from "./hooks/useDashboardSocket"; -import { mergeMessages, useMessages } from "./hooks/useMessages"; -import { useMediaControl } from "./hooks/useMediaControl"; -import { useUIState } from "./hooks/useUIState"; -import { useVoiceControl } from "./hooks/useVoiceControl"; -import { getAppConfig } from "./api/client"; -import type { MessageRecord } from "./types/messages"; -import type { DashboardTab } from "./types/ui"; -import type { ActiveSpeaker } from "./types/voice"; +import { useEffect, useMemo, useState } from "react"; +import { Component, Suspense, lazy } from "react"; +import { DashboardLayout } from "./widgets/DashboardLayout"; +import { MobileTabBar } from "./shared/ui/MobileTabBar"; +import { AuthOverlay } from "./features/auth"; +import { LivePanel } from "./features/live"; +import { MessagesPanel } from "./features/messages"; +import { useDashboardSocket } from "./shared/ws/socket"; +import { mergeMessages, useMessages } from "./features/messages/hooks/useMessages"; +import { useMediaControl } from "./features/live/hooks/useMediaControl"; +import { useUIState } from "./shared/hooks/useUIState"; +import { useVoiceControl } from "./features/live/hooks/useVoiceControl"; +import { useAudioPlayback } from "./shared/hooks/useAudioPlayback"; +import { useAudioTransmit } from "./shared/hooks/useAudioTransmit"; +import { getAppConfig, type MessageRecord, type ActiveSpeaker, type MediaState } from "./shared/api/client"; +import { Skeleton } from "./shared/ui"; -const AnalyticsPanel = lazy(() => import("./components/analytics").then((module) => ({ default: module.AnalyticsPanel }))); +const AnalyticsPanel = lazy(() => import("./features/analytics").then((module) => ({ default: module.AnalyticsPanel }))); class AnalyticsErrorBoundary extends Component<{ children: React.ReactNode }, { hasError: boolean }> { state = { hasError: false }; - - static getDerivedStateFromError() { - return { hasError: true }; - } - + static getDerivedStateFromError() { return { hasError: true }; } override render() { if (this.state.hasError) { - return ( -
Pilih guild untuk melihat analitik.
-Pilih guild untuk melihat analitik.
Join a Discord voice channel, listen, and transmit audio.
+ +{displayContent}
) : null} - {/* Sticker preview */} {stickers.length > 0 && (