# 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 */}
)
}
```