# Estructura del Frontend ## Árbol de Directorios ``` frontend/ ├── public/ │ └── favicon.ico │ ├── src/ │ ├── main.tsx # Entry point │ ├── App.tsx # App root │ │ │ ├── pages/ │ │ ├── Dashboard.tsx # Main dashboard │ │ ├── ProjectView.tsx # Single project view │ │ ├── TaskDetail.tsx # Task details modal │ │ └── AgentsView.tsx # Agents monitoring │ │ │ ├── components/ │ │ ├── kanban/ │ │ │ ├── KanbanBoard.tsx │ │ │ ├── KanbanColumn.tsx │ │ │ ├── TaskCard.tsx │ │ │ └── TaskCardActions.tsx │ │ │ │ │ ├── terminal/ │ │ │ ├── WebTerminal.tsx │ │ │ └── TerminalTab.tsx │ │ │ │ │ ├── projects/ │ │ │ ├── ProjectCard.tsx │ │ │ ├── ProjectForm.tsx │ │ │ └── ProjectSettings.tsx │ │ │ │ │ ├── tasks/ │ │ │ ├── TaskForm.tsx │ │ │ ├── TaskQuestion.tsx │ │ │ └── TaskTimeline.tsx │ │ │ │ │ ├── agents/ │ │ │ ├── AgentCard.tsx │ │ │ ├── AgentStatus.tsx │ │ │ └── AgentLogs.tsx │ │ │ │ │ ├── deployments/ │ │ │ ├── DeploymentList.tsx │ │ │ ├── DeploymentCard.tsx │ │ │ └── DeployButton.tsx │ │ │ │ │ ├── ui/ │ │ │ ├── Button.tsx │ │ │ ├── Modal.tsx │ │ │ ├── Card.tsx │ │ │ ├── Badge.tsx │ │ │ ├── Input.tsx │ │ │ ├── Select.tsx │ │ │ └── Spinner.tsx │ │ │ │ │ └── layout/ │ │ ├── Sidebar.tsx │ │ ├── Header.tsx │ │ ├── Layout.tsx │ │ └── Navigation.tsx │ │ │ ├── hooks/ │ │ ├── useProjects.ts │ │ ├── useTasks.ts │ │ ├── useAgents.ts │ │ ├── useWebSocket.ts │ │ ├── useTaskActions.ts │ │ └── useDeployments.ts │ │ │ ├── store/ │ │ ├── authStore.ts │ │ ├── uiStore.ts │ │ └── terminalStore.ts │ │ │ ├── api/ │ │ ├── client.ts # Axios instance │ │ ├── projects.ts │ │ ├── tasks.ts │ │ ├── agents.ts │ │ ├── deployments.ts │ │ └── websocket.ts │ │ │ ├── types/ │ │ ├── project.ts │ │ ├── task.ts │ │ ├── agent.ts │ │ ├── deployment.ts │ │ └── common.ts │ │ │ ├── utils/ │ │ ├── format.ts │ │ ├── validation.ts │ │ └── constants.ts │ │ │ └── styles/ │ └── index.css # Tailwind imports │ ├── index.html ├── vite.config.ts ├── tailwind.config.js ├── tsconfig.json ├── package.json └── README.md ``` ## Setup Inicial ### package.json ```json { "name": "aiworker-frontend", "version": "1.0.0", "type": "module", "scripts": { "dev": "vite", "build": "tsc && vite build", "preview": "vite preview", "lint": "eslint src --ext ts,tsx", "format": "prettier --write src/**/*.{ts,tsx}" }, "dependencies": { "react": "19.2.0", "react-dom": "19.2.0", "react-router-dom": "^7.1.3", "@tanstack/react-query": "^6.3.0", "zustand": "^5.0.3", "socket.io-client": "^4.8.1", "axios": "^1.7.9", "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^9.1.0", "xterm": "^5.5.0", "xterm-addon-fit": "^0.10.0", "xterm-addon-web-links": "^0.11.0", "lucide-react": "^0.469.0", "react-hot-toast": "^2.4.1", "recharts": "^2.15.0", "date-fns": "^4.1.0", "clsx": "^2.1.1" }, "devDependencies": { "@types/react": "^19.0.6", "@types/react-dom": "^19.0.2", "@vitejs/plugin-react": "^4.3.4", "typescript": "^5.7.2", "vite": "^6.0.7", "tailwindcss": "^4.0.0", "autoprefixer": "^10.4.21", "postcss": "^8.4.49", "eslint": "^9.18.0", "prettier": "^3.4.2" } } ``` ### vite.config.ts ```typescript import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import path from 'path' export default defineConfig({ plugins: [react()], resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, server: { port: 5173, proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true, }, '/socket.io': { target: 'http://localhost:3000', ws: true, }, }, }, }) ``` ### tailwind.config.js ```javascript /** @type {import('tailwindcss').Config} */ export default { content: [ "./index.html", "./src/**/*.{js,ts,jsx,tsx}", ], theme: { extend: { colors: { primary: { 50: '#f0f9ff', 100: '#e0f2fe', 500: '#0ea5e9', 600: '#0284c7', 700: '#0369a1', }, success: { 50: '#f0fdf4', 500: '#22c55e', 600: '#16a34a', }, warning: { 50: '#fefce8', 500: '#eab308', 600: '#ca8a04', }, error: { 50: '#fef2f2', 500: '#ef4444', 600: '#dc2626', }, }, }, }, plugins: [], } ``` ### tsconfig.json ```json { "compilerOptions": { "target": "ES2020", "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, "moduleResolution": "bundler", "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, "baseUrl": ".", "paths": { "@/*": ["./src/*"] } }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] } ``` ## Entry Points ### main.tsx ```typescript import React from 'react' import ReactDOM from 'react-dom/client' import { BrowserRouter } from 'react-router-dom' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { Toaster } from 'react-hot-toast' import App from './App' import './styles/index.css' const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 1000 * 60 * 5, // 5 minutes refetchOnWindowFocus: false, }, }, }) ReactDOM.createRoot(document.getElementById('root')!).render( ) ``` ### App.tsx ```typescript import { Routes, Route } from 'react-router-dom' import Layout from './components/layout/Layout' import Dashboard from './pages/Dashboard' import ProjectView from './pages/ProjectView' import AgentsView from './pages/AgentsView' import { WebSocketProvider } from './api/websocket' function App() { return ( } /> } /> } /> ) } export default App ``` ### styles/index.css ```css @import 'tailwindcss/base'; @import 'tailwindcss/components'; @import 'tailwindcss/utilities'; @layer base { body { @apply bg-gray-50 text-gray-900; } } @layer components { .card { @apply bg-white rounded-lg shadow-sm border border-gray-200 p-4; } .btn { @apply px-4 py-2 rounded-lg font-medium transition-colors; } .btn-primary { @apply btn bg-primary-600 text-white hover:bg-primary-700; } .btn-secondary { @apply btn bg-gray-200 text-gray-700 hover:bg-gray-300; } .badge { @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium; } } ``` ## Comandos ```bash # Desarrollo bun run dev # Build bun run build # Preview build bun run preview # Lint bun run lint # Format bun run format ``` ## Variables de Entorno ```bash # .env VITE_API_URL=http://localhost:3000 VITE_WS_URL=ws://localhost:3000 ``` ## Estructura de Componentes Los componentes siguen esta estructura: ```typescript // Imports import { useState } from 'react' import { SomeIcon } from 'lucide-react' // Types interface ComponentProps { prop1: string prop2?: number } // Component export function Component({ prop1, prop2 = 0 }: ComponentProps) { // State const [state, setState] = useState('') // Handlers const handleAction = () => { // ... } // Render return (
{/* JSX */}
) } ```