Files
aiworker/docs/03-frontend/estructura.md
Hector Ros db71705842 Complete documentation for future sessions
- CLAUDE.md for AI agents to understand the codebase
- GITEA-GUIDE.md centralizes all Gitea operations (API, Registry, Auth)
- DEVELOPMENT-WORKFLOW.md explains complete dev process
- ROADMAP.md, NEXT-SESSION.md for planning
- QUICK-REFERENCE.md, TROUBLESHOOTING.md for daily use
- 40+ detailed docs in /docs folder
- Backend as submodule from Gitea

Everything documented for autonomous operation.

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
2026-01-20 00:37:19 +01:00

9.1 KiB

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

{
  "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

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

/** @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

{
  "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

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(
  <React.StrictMode>
    <BrowserRouter>
      <QueryClientProvider client={queryClient}>
        <App />
        <Toaster position="top-right" />
      </QueryClientProvider>
    </BrowserRouter>
  </React.StrictMode>
)

App.tsx

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 (
    <WebSocketProvider>
      <Layout>
        <Routes>
          <Route path="/" element={<Dashboard />} />
          <Route path="/projects/:projectId" element={<ProjectView />} />
          <Route path="/agents" element={<AgentsView />} />
        </Routes>
      </Layout>
    </WebSocketProvider>
  )
}

export default App

styles/index.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

# Desarrollo
bun run dev

# Build
bun run build

# Preview build
bun run preview

# Lint
bun run lint

# Format
bun run format

Variables de Entorno

# .env
VITE_API_URL=http://localhost:3000
VITE_WS_URL=ws://localhost:3000

Estructura de Componentes

Los componentes siguen esta estructura:

// 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<string>('')

  // Handlers
  const handleAction = () => {
    // ...
  }

  // Render
  return (
    <div className="component">
      {/* JSX */}
    </div>
  )
}