From 46e95ce7b3de89ae0a270595dab79105cfdd4a52 Mon Sep 17 00:00:00 2001 From: syntaxbullet Date: Sun, 8 Feb 2026 16:41:40 +0100 Subject: [PATCH] refactor(web): remove frontend dashboard files Delete all React components, pages, hooks, contexts, styles, and build scripts. The web module now serves as an API-only server. --- web/build.ts | 245 ------- web/components.json | 21 - web/src/App.tsx | 52 -- web/src/components/activity-chart.tsx | 164 ----- web/src/components/commands-drawer.tsx | 235 ------ web/src/components/effect-editor.tsx | 397 ---------- web/src/components/feature-card.tsx | 48 -- web/src/components/image-cropper.tsx | 114 --- web/src/components/image-uploader.tsx | 259 ------- web/src/components/info-card.tsx | 30 - web/src/components/item-form-sheet.tsx | 70 -- web/src/components/item-form.tsx | 380 ---------- web/src/components/items-filter.tsx | 139 ---- web/src/components/items-table.tsx | 342 --------- web/src/components/layout/app-sidebar.tsx | 220 ------ web/src/components/layout/main-layout.tsx | 56 -- web/src/components/leaderboard-card.tsx | 170 ----- web/src/components/loot-table-builder.tsx | 281 ------- web/src/components/lootdrop-card.tsx | 128 ---- web/src/components/navigation/mobile-nav.tsx | 41 -- web/src/components/quest-form.tsx | 297 -------- web/src/components/quest-table.tsx | 288 -------- web/src/components/rarity-badge.tsx | 62 -- web/src/components/recent-activity.tsx | 98 --- web/src/components/section-header.tsx | 39 - web/src/components/stat-card.tsx | 77 -- web/src/components/testimonial-card.tsx | 41 -- web/src/components/ui/accordion.tsx | 66 -- web/src/components/ui/badge.tsx | 37 - web/src/components/ui/button.tsx | 64 -- web/src/components/ui/card.tsx | 92 --- web/src/components/ui/collapsible.tsx | 34 - web/src/components/ui/dialog.tsx | 122 ---- web/src/components/ui/dropdown-menu.tsx | 255 ------- web/src/components/ui/form.tsx | 165 ----- web/src/components/ui/input.tsx | 21 - web/src/components/ui/label.tsx | 22 - web/src/components/ui/progress.tsx | 24 - web/src/components/ui/scroll-area.tsx | 58 -- web/src/components/ui/select.tsx | 188 ----- web/src/components/ui/separator.tsx | 26 - web/src/components/ui/sheet.tsx | 139 ---- web/src/components/ui/sidebar.tsx | 725 ------------------- web/src/components/ui/skeleton.tsx | 13 - web/src/components/ui/slider.tsx | 26 - web/src/components/ui/sonner.tsx | 38 - web/src/components/ui/switch.tsx | 29 - web/src/components/ui/tabs.tsx | 64 -- web/src/components/ui/textarea.tsx | 18 - web/src/components/ui/tooltip.tsx | 59 -- web/src/contexts/navigation-context.tsx | 147 ---- web/src/frontend.tsx | 26 - web/src/hooks/use-items.ts | 306 -------- web/src/hooks/use-mobile.ts | 19 - web/src/hooks/use-settings.ts | 187 ----- web/src/hooks/use-socket.ts | 61 -- web/src/index.css | 1 - web/src/index.html | 18 - web/src/index.ts | 18 - web/src/lib/canvasUtils.ts | 89 --- web/src/lib/utils.ts | 6 - web/src/pages/AdminQuests.tsx | 152 ---- web/src/pages/DesignSystem.tsx | 382 ---------- web/src/pages/Home.tsx | 219 ------ web/src/pages/admin/Items.tsx | 106 --- web/src/pages/admin/Overview.tsx | 164 ----- web/src/pages/settings/Economy.tsx | 290 -------- web/src/pages/settings/General.tsx | 149 ---- web/src/pages/settings/Roles.tsx | 141 ---- web/src/pages/settings/SettingsLayout.tsx | 65 -- web/src/pages/settings/Systems.tsx | 338 --------- web/styles/globals.css | 314 -------- 72 files changed, 9777 deletions(-) delete mode 100644 web/build.ts delete mode 100644 web/components.json delete mode 100644 web/src/App.tsx delete mode 100644 web/src/components/activity-chart.tsx delete mode 100644 web/src/components/commands-drawer.tsx delete mode 100644 web/src/components/effect-editor.tsx delete mode 100644 web/src/components/feature-card.tsx delete mode 100644 web/src/components/image-cropper.tsx delete mode 100644 web/src/components/image-uploader.tsx delete mode 100644 web/src/components/info-card.tsx delete mode 100644 web/src/components/item-form-sheet.tsx delete mode 100644 web/src/components/item-form.tsx delete mode 100644 web/src/components/items-filter.tsx delete mode 100644 web/src/components/items-table.tsx delete mode 100644 web/src/components/layout/app-sidebar.tsx delete mode 100644 web/src/components/layout/main-layout.tsx delete mode 100644 web/src/components/leaderboard-card.tsx delete mode 100644 web/src/components/loot-table-builder.tsx delete mode 100644 web/src/components/lootdrop-card.tsx delete mode 100644 web/src/components/navigation/mobile-nav.tsx delete mode 100644 web/src/components/quest-form.tsx delete mode 100644 web/src/components/quest-table.tsx delete mode 100644 web/src/components/rarity-badge.tsx delete mode 100644 web/src/components/recent-activity.tsx delete mode 100644 web/src/components/section-header.tsx delete mode 100644 web/src/components/stat-card.tsx delete mode 100644 web/src/components/testimonial-card.tsx delete mode 100644 web/src/components/ui/accordion.tsx delete mode 100644 web/src/components/ui/badge.tsx delete mode 100644 web/src/components/ui/button.tsx delete mode 100644 web/src/components/ui/card.tsx delete mode 100644 web/src/components/ui/collapsible.tsx delete mode 100644 web/src/components/ui/dialog.tsx delete mode 100644 web/src/components/ui/dropdown-menu.tsx delete mode 100644 web/src/components/ui/form.tsx delete mode 100644 web/src/components/ui/input.tsx delete mode 100644 web/src/components/ui/label.tsx delete mode 100644 web/src/components/ui/progress.tsx delete mode 100644 web/src/components/ui/scroll-area.tsx delete mode 100644 web/src/components/ui/select.tsx delete mode 100644 web/src/components/ui/separator.tsx delete mode 100644 web/src/components/ui/sheet.tsx delete mode 100644 web/src/components/ui/sidebar.tsx delete mode 100644 web/src/components/ui/skeleton.tsx delete mode 100644 web/src/components/ui/slider.tsx delete mode 100644 web/src/components/ui/sonner.tsx delete mode 100644 web/src/components/ui/switch.tsx delete mode 100644 web/src/components/ui/tabs.tsx delete mode 100644 web/src/components/ui/textarea.tsx delete mode 100644 web/src/components/ui/tooltip.tsx delete mode 100644 web/src/contexts/navigation-context.tsx delete mode 100644 web/src/frontend.tsx delete mode 100644 web/src/hooks/use-items.ts delete mode 100644 web/src/hooks/use-mobile.ts delete mode 100644 web/src/hooks/use-settings.ts delete mode 100644 web/src/hooks/use-socket.ts delete mode 100644 web/src/index.css delete mode 100644 web/src/index.html delete mode 100644 web/src/index.ts delete mode 100644 web/src/lib/canvasUtils.ts delete mode 100644 web/src/lib/utils.ts delete mode 100644 web/src/pages/AdminQuests.tsx delete mode 100644 web/src/pages/DesignSystem.tsx delete mode 100644 web/src/pages/Home.tsx delete mode 100644 web/src/pages/admin/Items.tsx delete mode 100644 web/src/pages/admin/Overview.tsx delete mode 100644 web/src/pages/settings/Economy.tsx delete mode 100644 web/src/pages/settings/General.tsx delete mode 100644 web/src/pages/settings/Roles.tsx delete mode 100644 web/src/pages/settings/SettingsLayout.tsx delete mode 100644 web/src/pages/settings/Systems.tsx delete mode 100644 web/styles/globals.css diff --git a/web/build.ts b/web/build.ts deleted file mode 100644 index 76aaf15..0000000 --- a/web/build.ts +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/env bun -import plugin from "bun-plugin-tailwind"; -import { existsSync } from "fs"; -import { rm } from "fs/promises"; -import path from "path"; - -if (process.argv.includes("--help") || process.argv.includes("-h")) { - console.log(` -šŸ—ļø Bun Build Script - -Usage: bun run build.ts [options] - -Common Options: - --outdir Output directory (default: "dist") - --minify Enable minification (or --minify.whitespace, --minify.syntax, etc) - --sourcemap Sourcemap type: none|linked|inline|external - --target Build target: browser|bun|node - --format Output format: esm|cjs|iife - --splitting Enable code splitting - --packages Package handling: bundle|external - --public-path Public path for assets - --env Environment handling: inline|disable|prefix* - --conditions Package.json export conditions (comma separated) - --external External packages (comma separated) - --banner Add banner text to output - --footer Add footer text to output - --define Define global constants (e.g. --define.VERSION=1.0.0) - --help, -h Show this help message - -Example: - bun run build.ts --outdir=dist --minify --sourcemap=linked --external=react,react-dom -`); - process.exit(0); -} - -const toCamelCase = (str: string): string => str.replace(/-([a-z])/g, g => g[1]?.toUpperCase() || ""); - -const parseValue = (value: string): any => { - if (value === "true") return true; - if (value === "false") return false; - - if (/^\d+$/.test(value)) return parseInt(value, 10); - if (/^\d*\.\d+$/.test(value)) return parseFloat(value); - - if (value.includes(",")) return value.split(",").map(v => v.trim()); - - return value; -}; - -function parseArgs(): Partial { - const config: Partial = {}; - const args = process.argv.slice(2); - - for (let i = 0; i < args.length; i++) { - const arg = args[i]; - if (arg === undefined) continue; - if (!arg.startsWith("--")) continue; - - if (arg.startsWith("--no-")) { - const key = toCamelCase(arg.slice(5)); - // @ts-ignore - config[key] = false; - continue; - } - - if (!arg.includes("=") && (i === args.length - 1 || args[i + 1]?.startsWith("--"))) { - const key = toCamelCase(arg.slice(2)); - // @ts-ignore - config[key] = true; - continue; - } - - let key: string; - let value: string; - - if (arg.includes("=")) { - [key, value] = arg.slice(2).split("=", 2) as [string, string]; - } else { - key = arg.slice(2); - value = args[++i] ?? ""; - } - - key = toCamelCase(key); - - if (key.includes(".")) { - const [parentKey, childKey] = key.split("."); - // @ts-ignore - config[parentKey] = config[parentKey] || {}; - // @ts-ignore - config[parentKey][childKey] = parseValue(value); - } else { - // @ts-ignore - config[key] = parseValue(value); - } - } - - return config; -} - -const formatFileSize = (bytes: number): string => { - const units = ["B", "KB", "MB", "GB"]; - let size = bytes; - let unitIndex = 0; - - while (size >= 1024 && unitIndex < units.length - 1) { - size /= 1024; - unitIndex++; - } - - return `${size.toFixed(2)} ${units[unitIndex]}`; -}; - -console.log("\nšŸš€ Starting build process...\n"); - -const cliConfig = parseArgs(); -const outdir = cliConfig.outdir || path.join(process.cwd(), "dist"); - -if (existsSync(outdir)) { - console.log(`šŸ—‘ļø Cleaning previous build at ${outdir}`); - await rm(outdir, { recursive: true, force: true }); -} - -const start = performance.now(); - -const entrypoints = [...new Bun.Glob("**.html").scanSync("src")] - .map(a => path.resolve("src", a)) - .filter(dir => !dir.includes("node_modules")); -console.log(`šŸ“„ Found ${entrypoints.length} HTML ${entrypoints.length === 1 ? "file" : "files"} to process\n`); - -const build = async () => { - const result = await Bun.build({ - entrypoints, - outdir, - plugins: [plugin], - minify: true, - target: "browser", - sourcemap: "linked", - publicPath: "/", // Use absolute paths for SPA routing compatibility - define: { - "process.env.NODE_ENV": JSON.stringify((cliConfig as any).watch ? "development" : "production"), - }, - ...cliConfig, - }); - - const outputTable = result.outputs.map(output => ({ - File: path.relative(process.cwd(), output.path), - Type: output.kind, - Size: formatFileSize(output.size), - })); - - console.table(outputTable); - return result; -}; - -const result = await build(); - -const end = performance.now(); -const buildTime = (end - start).toFixed(2); -console.log(`\nāœ… Build completed in ${buildTime}ms\n`); - -if ((cliConfig as any).watch) { - console.log("šŸ‘€ Watching for changes...\n"); - - // Polling-based file watcher for Docker compatibility - // Docker volumes don't propagate filesystem events (inotify) reliably - const srcDir = path.join(process.cwd(), "src"); - const POLL_INTERVAL_MS = 1000; - let lastMtimes = new Map(); - let isRebuilding = false; - - // Collect all file mtimes in src directory - const collectMtimes = async (): Promise> => { - const mtimes = new Map(); - const glob = new Bun.Glob("**/*.{ts,tsx,js,jsx,css,html}"); - - for await (const file of glob.scan({ cwd: srcDir, absolute: true })) { - try { - const stat = await Bun.file(file).stat(); - if (stat) { - mtimes.set(file, stat.mtime.getTime()); - } - } catch { - // File may have been deleted, skip - } - } - return mtimes; - }; - - // Initial collection - lastMtimes = await collectMtimes(); - - // Polling loop - const poll = async () => { - if (isRebuilding) return; - - const currentMtimes = await collectMtimes(); - const changedFiles: string[] = []; - - // Check for new or modified files - for (const [file, mtime] of currentMtimes) { - const lastMtime = lastMtimes.get(file); - if (lastMtime === undefined || lastMtime < mtime) { - changedFiles.push(path.relative(srcDir, file)); - } - } - - // Check for deleted files - for (const file of lastMtimes.keys()) { - if (!currentMtimes.has(file)) { - changedFiles.push(path.relative(srcDir, file) + " (deleted)"); - } - } - - if (changedFiles.length > 0) { - isRebuilding = true; - console.log(`\nšŸ”„ Changes detected:`); - changedFiles.forEach(f => console.log(` • ${f}`)); - console.log(""); - - try { - const rebuildStart = performance.now(); - await build(); - const rebuildEnd = performance.now(); - console.log(`\nāœ… Rebuild completed in ${(rebuildEnd - rebuildStart).toFixed(2)}ms\n`); - } catch (err) { - console.error("āŒ Rebuild failed:", err); - } - - lastMtimes = currentMtimes; - isRebuilding = false; - } - }; - - const interval = setInterval(poll, POLL_INTERVAL_MS); - - // Handle manual exit - process.on("SIGINT", () => { - clearInterval(interval); - console.log("\nšŸ‘‹ Stopping build watcher..."); - process.exit(0); - }); - - // Keep process alive - process.stdin.resume(); -} diff --git a/web/components.json b/web/components.json deleted file mode 100644 index a084701..0000000 --- a/web/components.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", - "rsc": false, - "tsx": true, - "tailwind": { - "config": "", - "css": "styles/globals.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "iconLibrary": "lucide" -} diff --git a/web/src/App.tsx b/web/src/App.tsx deleted file mode 100644 index cd98cc2..0000000 --- a/web/src/App.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; -import "./index.css"; - -import { DesignSystem } from "./pages/DesignSystem"; -import { AdminQuests } from "./pages/AdminQuests"; -import { AdminOverview } from "./pages/admin/Overview"; -import { AdminItems } from "./pages/admin/Items"; - -import { Home } from "./pages/Home"; -import { Toaster } from "sonner"; -import { NavigationProvider } from "./contexts/navigation-context"; -import { MainLayout } from "./components/layout/main-layout"; - -import { SettingsLayout } from "./pages/settings/SettingsLayout"; -import { GeneralSettings } from "./pages/settings/General"; -import { EconomySettings } from "./pages/settings/Economy"; -import { SystemsSettings } from "./pages/settings/Systems"; -import { RolesSettings } from "./pages/settings/Roles"; - -export function App() { - return ( - - - - - - - } /> - } /> - } /> - } /> - } /> - - - }> - } /> - } /> - } /> - } /> - } /> - - - } /> - - - - - ); -} - -export default App; - diff --git a/web/src/components/activity-chart.tsx b/web/src/components/activity-chart.tsx deleted file mode 100644 index 9f87ad6..0000000 --- a/web/src/components/activity-chart.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts"; -import { Card, CardHeader, CardTitle, CardContent } from "./ui/card"; -import { Activity } from "lucide-react"; -import { cn } from "../lib/utils"; -import type { ActivityData } from "@shared/modules/dashboard/dashboard.types"; - -interface ActivityChartProps { - className?: string; - data?: ActivityData[]; -} - -export function ActivityChart({ className, data: providedData }: ActivityChartProps) { - const [data, setData] = useState([]); // using any[] for the displayTime extension - const [isLoading, setIsLoading] = useState(!providedData); - const [error, setError] = useState(null); - - useEffect(() => { - if (providedData) { - // Process provided data - const formatted = providedData.map((item) => ({ - ...item, - displayTime: new Date(item.hour).getHours().toString().padStart(2, '0') + ':00', - })); - setData(formatted); - return; - } - - let mounted = true; - - async function fetchActivity() { - try { - const response = await fetch("/api/stats/activity"); - if (!response.ok) throw new Error("Failed to fetch activity data"); - const result = await response.json(); - - if (mounted) { - // Normalize data: ensure we have 24 hours format - // The API returns { hour: ISOString, commands: number, transactions: number } - // We want to format hour to readable time - const formatted = result.map((item: ActivityData) => ({ - ...item, - displayTime: new Date(item.hour).getHours().toString().padStart(2, '0') + ':00', - })); - - // Sort by time just in case, though API should handle it - setData(formatted); - - // Only set loading to false on the first load to avoid flickering - setIsLoading(false); - } - } catch (err) { - if (mounted) { - console.error(err); - setError("Failed to load activity data"); - setIsLoading(false); - } - } - } - - fetchActivity(); - - // Refresh every 60 seconds - const interval = setInterval(fetchActivity, 60000); - - return () => { - mounted = false; - clearInterval(interval); - }; - }, [providedData]); - - if (error) { - return ( - - - {error} - - - ); - } - - return ( - - -
- - 24h Activity -
-
- -
- {isLoading ? ( -
-
-
- ) : ( - - - - - - - - - - - - - - - `${value}`} - /> - - - - - - )} -
-
-
- ); -} diff --git a/web/src/components/commands-drawer.tsx b/web/src/components/commands-drawer.tsx deleted file mode 100644 index 9b4f3f0..0000000 --- a/web/src/components/commands-drawer.tsx +++ /dev/null @@ -1,235 +0,0 @@ -import React, { useEffect, useState, useMemo } from "react"; -import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from "./ui/sheet"; -import { ScrollArea } from "./ui/scroll-area"; -import { Switch } from "./ui/switch"; -import { Badge } from "./ui/badge"; -import { Loader2, Terminal, Sparkles, Coins, Shield, Backpack, TrendingUp, MessageSquare, User } from "lucide-react"; -import { cn } from "../lib/utils"; -import { toast } from "sonner"; - -interface Command { - name: string; - category: string; -} - -interface CommandsDrawerProps { - open: boolean; - onOpenChange: (open: boolean) => void; -} - -// Category metadata for visual styling -const CATEGORY_CONFIG: Record = { - admin: { label: "Admin", color: "bg-red-500/20 text-red-400 border-red-500/30", icon: Shield }, - economy: { label: "Economy", color: "bg-amber-500/20 text-amber-400 border-amber-500/30", icon: Coins }, - leveling: { label: "Leveling", color: "bg-emerald-500/20 text-emerald-400 border-emerald-500/30", icon: TrendingUp }, - inventory: { label: "Inventory", color: "bg-blue-500/20 text-blue-400 border-blue-500/30", icon: Backpack }, - quest: { label: "Quests", color: "bg-purple-500/20 text-purple-400 border-purple-500/30", icon: Sparkles }, - feedback: { label: "Feedback", color: "bg-cyan-500/20 text-cyan-400 border-cyan-500/30", icon: MessageSquare }, - user: { label: "User", color: "bg-pink-500/20 text-pink-400 border-pink-500/30", icon: User }, - uncategorized: { label: "Other", color: "bg-zinc-500/20 text-zinc-400 border-zinc-500/30", icon: Terminal }, -}; - -export function CommandsDrawer({ open, onOpenChange }: CommandsDrawerProps) { - const [commands, setCommands] = useState([]); - const [enabledState, setEnabledState] = useState>({}); - const [loading, setLoading] = useState(false); - const [saving, setSaving] = useState(null); - - // Fetch commands and their enabled state - useEffect(() => { - if (open) { - setLoading(true); - Promise.all([ - fetch("/api/settings/meta").then(res => res.json()), - fetch("/api/settings").then(res => res.json()), - ]).then(([meta, config]) => { - setCommands(meta.commands || []); - // Build enabled state from config.commands (undefined = enabled by default) - const state: Record = {}; - for (const cmd of meta.commands || []) { - state[cmd.name] = config.commands?.[cmd.name] !== false; - } - setEnabledState(state); - }).catch(err => { - toast.error("Failed to load commands", { - description: "Unable to fetch command list. Please try again." - }); - console.error(err); - }).finally(() => { - setLoading(false); - }); - } - }, [open]); - - // Group commands by category - const groupedCommands = useMemo(() => { - const groups: Record = {}; - for (const cmd of commands) { - const cat = cmd.category || "uncategorized"; - if (!groups[cat]) groups[cat] = []; - groups[cat].push(cmd); - } - // Sort categories: admin first, then alphabetically - const sortedCategories = Object.keys(groups).sort((a, b) => { - if (a === "admin") return -1; - if (b === "admin") return 1; - return a.localeCompare(b); - }); - return sortedCategories.map(cat => ({ category: cat, commands: groups[cat]! })); - }, [commands]); - - // Toggle command enabled state - const toggleCommand = async (commandName: string, enabled: boolean) => { - setSaving(commandName); - try { - const response = await fetch("/api/settings", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - commands: { - [commandName]: enabled, - }, - }), - }); - - if (!response.ok) throw new Error("Failed to save"); - - setEnabledState(prev => ({ ...prev, [commandName]: enabled })); - toast.success(`/${commandName} ${enabled ? "enabled" : "disabled"}`, { - description: `Command has been ${enabled ? "enabled" : "disabled"} successfully.`, - duration: 2000, - id: "command-toggle", - }); - } catch (error) { - toast.error("Failed to toggle command", { - description: "Unable to update command status. Please try again." - }); - console.error(error); - } finally { - setSaving(null); - } - }; - - return ( - - - - - - Command Manager - - - Enable or disable commands. Changes take effect immediately. - - - - {loading ? ( -
- -
- ) : ( -
- -
- {groupedCommands.map(({ category, commands: cmds }) => { - const config = (CATEGORY_CONFIG[category] ?? CATEGORY_CONFIG.uncategorized)!; - const IconComponent = config.icon; - - return ( -
- {/* Category Header */} -
- -

- {config.label} -

- - {cmds.length} - -
- - {/* Commands Grid */} -
- {cmds.map(cmd => { - const isEnabled = enabledState[cmd.name] !== false; - const isSaving = saving === cmd.name; - - return ( -
- {/* Category color accent bar */} -
- -
-
- {/* Icon with glow effect */} -
- -
- -
- - /{cmd.name} - - - {category} - -
-
- - toggleCommand(cmd.name, checked)} - disabled={isSaving} - className={cn( - "transition-opacity duration-300", - !isEnabled && "opacity-60" - )} - /> -
-
- ); - })} -
-
- ); - })} - - {groupedCommands.length === 0 && ( -
- No commands found. -
- )} -
- -
- )} - - - ); -} diff --git a/web/src/components/effect-editor.tsx b/web/src/components/effect-editor.tsx deleted file mode 100644 index b14d816..0000000 --- a/web/src/components/effect-editor.tsx +++ /dev/null @@ -1,397 +0,0 @@ -/** - * EffectEditor Component - * Dynamic form for adding/editing item effects with all 7 effect types. - */ - -import { useState, useCallback } from "react"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Textarea } from "@/components/ui/textarea"; -import { Label } from "@/components/ui/label"; -import { Card, CardContent } from "@/components/ui/card"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { - Accordion, - AccordionContent, - AccordionItem, - AccordionTrigger, -} from "@/components/ui/accordion"; -import { LootTableBuilder } from "@/components/loot-table-builder"; -import { - Plus, - Trash2, - Sparkles, - Coins, - MessageSquare, - Zap, - Clock, - Palette, - Package, -} from "lucide-react"; -import { cn } from "@/lib/utils"; - -// Effect types matching the backend -const EFFECT_TYPES = [ - { value: "ADD_XP", label: "Add XP", icon: Sparkles, color: "text-blue-400" }, - { value: "ADD_BALANCE", label: "Add Balance", icon: Coins, color: "text-amber-400" }, - { value: "REPLY_MESSAGE", label: "Reply Message", icon: MessageSquare, color: "text-green-400" }, - { value: "XP_BOOST", label: "XP Boost", icon: Zap, color: "text-purple-400" }, - { value: "TEMP_ROLE", label: "Temporary Role", icon: Clock, color: "text-orange-400" }, - { value: "COLOR_ROLE", label: "Color Role", icon: Palette, color: "text-pink-400" }, - { value: "LOOTBOX", label: "Lootbox", icon: Package, color: "text-yellow-400" }, -]; - -interface Effect { - type: string; - [key: string]: any; -} - -interface EffectEditorProps { - effects: Effect[]; - onChange: (effects: Effect[]) => void; -} - -const getDefaultEffect = (type: string): Effect => { - switch (type) { - case "ADD_XP": - return { type, amount: 100 }; - case "ADD_BALANCE": - return { type, amount: 100 }; - case "REPLY_MESSAGE": - return { type, message: "" }; - case "XP_BOOST": - return { type, multiplier: 2, durationMinutes: 60 }; - case "TEMP_ROLE": - return { type, roleId: "", durationMinutes: 60 }; - case "COLOR_ROLE": - return { type, roleId: "" }; - case "LOOTBOX": - return { type, pool: [] }; - default: - return { type }; - } -}; - -const getEffectSummary = (effect: Effect): string => { - switch (effect.type) { - case "ADD_XP": - return `+${effect.amount} XP`; - case "ADD_BALANCE": - return `+${effect.amount} coins`; - case "REPLY_MESSAGE": - return effect.message ? `"${effect.message.slice(0, 30)}..."` : "No message"; - case "XP_BOOST": - return `${effect.multiplier}x for ${effect.durationMinutes || effect.durationHours * 60 || effect.durationSeconds / 60}m`; - case "TEMP_ROLE": - return `Role for ${effect.durationMinutes || effect.durationHours * 60 || effect.durationSeconds / 60}m`; - case "COLOR_ROLE": - return effect.roleId ? `Role: ${effect.roleId}` : "No role set"; - case "LOOTBOX": - return `${effect.pool?.length || 0} drops`; - default: - return effect.type; - } -}; - -export function EffectEditor({ effects, onChange }: EffectEditorProps) { - const [expandedItems, setExpandedItems] = useState([]); - - const addEffect = useCallback(() => { - const newEffect = getDefaultEffect("ADD_XP"); - const newEffects = [...effects, newEffect]; - onChange(newEffects); - setExpandedItems(prev => [...prev, `effect-${newEffects.length - 1}`]); - }, [effects, onChange]); - - const removeEffect = useCallback((index: number) => { - const newEffects = effects.filter((_, i) => i !== index); - onChange(newEffects); - }, [effects, onChange]); - - const updateEffect = useCallback((index: number, updates: Partial) => { - const newEffects = effects.map((effect, i) => - i === index ? { ...effect, ...updates } : effect - ); - onChange(newEffects); - }, [effects, onChange]); - - const changeEffectType = useCallback((index: number, newType: string) => { - const newEffects = effects.map((effect, i) => - i === index ? getDefaultEffect(newType) : effect - ); - onChange(newEffects); - }, [effects, onChange]); - - if (effects.length === 0) { - return ( -
-

- No effects added yet -

- -
- ); - } - - return ( -
- - {effects.map((effect, index) => { - const effectType = EFFECT_TYPES.find(t => t.value === effect.type); - const Icon = effectType?.icon || Sparkles; - - return ( - - -
- - - {effectType?.label || effect.type} - - - {getEffectSummary(effect)} - -
-
- -
- {/* Effect Type Selector */} -
- - -
- - {/* Dynamic Fields Based on Effect Type */} - {effect.type === "ADD_XP" && ( -
- - updateEffect(index, { amount: parseInt(e.target.value) || 0 })} - className="bg-background/50" - /> -
- )} - - {effect.type === "ADD_BALANCE" && ( -
- -
- updateEffect(index, { amount: parseInt(e.target.value) || 0 })} - className="bg-background/50 pr-10" - /> -
-
- )} - - {effect.type === "REPLY_MESSAGE" && ( -
- -