# Phase 2b — Dashboard Sante Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add a dashboard showing container health status (running/stopped) and version for all environments, plus detailed metrics (CPU, memory, uptime) on the application detail page. **Architecture:** A DockerService executes Docker CLI commands via Process to get container status and stats. Two new API Platform endpoints expose dashboard overview and per-environment health. Frontend adds a new dashboard page and enriches the detail page. **Tech Stack:** PHP 8.4, Symfony 8, API Platform 4, Symfony Process, Docker CLI, Nuxt 4, Vue 3 --- ## File Structure ### Backend — Create | File | Responsibility | |------|---------------| | `src/Service/DockerService.php` | Execute Docker CLI commands via Process | | `src/ApiResource/Dashboard.php` | API Platform DTO for dashboard response | | `src/ApiResource/EnvironmentHealth.php` | API Platform DTO for env health response | | `src/State/DashboardProvider.php` | Provider that aggregates all apps + container status | | `src/State/EnvironmentHealthProvider.php` | Provider for single env detailed health | ### Frontend — Create | File | Responsibility | |------|---------------| | `frontend/services/dashboard.ts` | API calls for dashboard and env health | | `frontend/services/dto/dashboard.ts` | TypeScript types | | `frontend/pages/dashboard.vue` | Dashboard page | ### Frontend — Modify | File | Change | |------|--------| | `frontend/pages/applications/[slug].vue` | Add health metrics block per env | | `frontend/layouts/default.vue` | Add Dashboard sidebar link | | `frontend/middleware/auth.global.ts` | Redirect `/` to `/dashboard` instead of `/applications` | | `frontend/i18n/locales/fr.json` | Dashboard translation keys | --- ## Task 1: DockerService **Files:** - Create: `src/Service/DockerService.php` - [ ] **Step 1: Create the service** ```php setTimeout(10); $process->run(); if (!$process->isSuccessful()) { return [ 'status' => 'not_found', 'image' => '', 'version' => '', 'startedAt' => '', ]; } $parts = explode('||', trim($process->getOutput())); if (\count($parts) < 3) { return [ 'status' => 'not_found', 'image' => '', 'version' => '', 'startedAt' => '', ]; } $image = $parts[1]; $version = 'latest'; if (str_contains($image, ':')) { $version = substr($image, strrpos($image, ':') + 1); } return [ 'status' => $parts[0], 'image' => $image, 'version' => $version, 'startedAt' => $parts[2], ]; } /** * @return array{cpuPercent: float, memoryUsage: string, memoryLimit: string, memoryPercent: float} */ public function getContainerStats(string $containerName): array { $process = new Process([ 'docker', 'stats', '--no-stream', '--format', '{{.CPUPerc}}||{{.MemUsage}}||{{.MemPerc}}', $containerName, ]); $process->setTimeout(10); $process->run(); if (!$process->isSuccessful()) { return [ 'cpuPercent' => 0.0, 'memoryUsage' => '', 'memoryLimit' => '', 'memoryPercent' => 0.0, ]; } $parts = explode('||', trim($process->getOutput())); if (\count($parts) < 3) { return [ 'cpuPercent' => 0.0, 'memoryUsage' => '', 'memoryLimit' => '', 'memoryPercent' => 0.0, ]; } $memParts = explode(' / ', $parts[1]); return [ 'cpuPercent' => (float) rtrim($parts[0], '%'), 'memoryUsage' => $memParts[0] ?? '', 'memoryLimit' => $memParts[1] ?? '', 'memoryPercent' => (float) rtrim($parts[2], '%'), ]; } } ``` - [ ] **Step 2: Commit** ```bash git add src/Service/DockerService.php git commit -m "feat : add DockerService for container status and stats" ``` --- ## Task 2: API Platform DTOs **Files:** - Create: `src/ApiResource/Dashboard.php` - Create: `src/ApiResource/EnvironmentHealth.php` - [ ] **Step 1: Create Dashboard DTO** ```php }> */ public array $applications = []; } ``` - [ ] **Step 2: Create EnvironmentHealth DTO** ```php applicationRepository->findAll(); $dto = new Dashboard(); foreach ($applications as $app) { $envs = []; foreach ($app->getEnvironments() as $env) { $containerStatus = $this->dockerService->getContainerStatus($env->getContainerName()); $envs[] = [ 'id' => $env->getId(), 'name' => $env->getName(), 'status' => $containerStatus['status'], 'version' => $containerStatus['version'], ]; } $dto->applications[] = [ 'name' => $app->getName(), 'slug' => $app->getSlug(), 'giteaUrl' => $app->getGiteaUrl(), 'environments' => $envs, ]; } return $dto; } } ``` - [ ] **Step 2: Commit** ```bash git add src/State/DashboardProvider.php git commit -m "feat : add DashboardProvider for container status overview" ``` --- ## Task 4: EnvironmentHealthProvider **Files:** - Create: `src/State/EnvironmentHealthProvider.php` - [ ] **Step 1: Create the provider** ```php environmentRepository->find($id) : null; if (null === $environment) { throw new NotFoundHttpException(sprintf('Environment "%s" not found.', $id)); } $containerName = $environment->getContainerName(); $status = $this->dockerService->getContainerStatus($containerName); $stats = $this->dockerService->getContainerStats($containerName); $dto = new EnvironmentHealth(); $dto->status = $status['status']; $dto->version = $status['version']; $dto->startedAt = $status['startedAt']; $dto->cpuPercent = $stats['cpuPercent']; $dto->memoryUsage = $stats['memoryUsage']; $dto->memoryLimit = $stats['memoryLimit']; $dto->memoryPercent = $stats['memoryPercent']; return $dto; } } ``` - [ ] **Step 2: Commit** ```bash git add src/State/EnvironmentHealthProvider.php git commit -m "feat : add EnvironmentHealthProvider for detailed env metrics" ``` --- ## Task 5: Frontend types and service **Files:** - Create: `frontend/services/dto/dashboard.ts` - Create: `frontend/services/dashboard.ts` - [ ] **Step 1: Create dashboard types** ```typescript type DashboardEnvironment = { id: number name: string status: string version: string } type DashboardApplication = { name: string slug: string giteaUrl?: string environments: DashboardEnvironment[] } type DashboardResponse = { applications: DashboardApplication[] } type EnvironmentHealth = { status: string version: string startedAt: string cpuPercent: number memoryUsage: string memoryLimit: string memoryPercent: number } ``` - [ ] **Step 2: Create dashboard service** ```typescript import type { DashboardResponse, EnvironmentHealth } from './dto/dashboard' export function getDashboard(): Promise { return useApi().get('/dashboard', undefined, { toast: false, }) } export function getEnvironmentHealth(envId: number): Promise { return useApi().get(`/environments/${envId}/health`, undefined, { toast: false, }) } ``` - [ ] **Step 3: Commit** ```bash git add frontend/services/dto/dashboard.ts frontend/services/dashboard.ts git commit -m "feat : add frontend dashboard types and service" ``` --- ## Task 6: i18n translations **Files:** - Modify: `frontend/i18n/locales/fr.json` - [ ] **Step 1: Add dashboard translations** Add a new `dashboard` section (replace the existing one which only has `title`): ```json { "dashboard": { "title": "Dashboard", "description": "Vue d'ensemble du SI", "refresh": "Actualiser", "status": { "running": "En ligne", "exited": "Arrete", "restarting": "Redemarrage", "not_found": "Introuvable" } } } ``` Add a `health` sub-object inside the `environments` section: ```json { "environments": { "...existing keys...", "health": { "title": "Sante du container", "status": "Statut", "version": "Version", "uptime": "Uptime", "cpu": "CPU", "memory": "Memoire", "noData": "Aucune donnee disponible" } } } ``` - [ ] **Step 2: Commit** ```bash git add frontend/i18n/locales/fr.json git commit -m "feat : add i18n translations for dashboard and health" ``` --- ## Task 7: Dashboard page **Files:** - Create: `frontend/pages/dashboard.vue` - [ ] **Step 1: Create the dashboard page** ```vue ``` - [ ] **Step 2: Commit** ```bash git add frontend/pages/dashboard.vue git commit -m "feat : add dashboard page with container status overview" ``` --- ## Task 8: Sidebar link + redirect **Files:** - Modify: `frontend/layouts/default.vue` - Modify: `frontend/middleware/auth.global.ts` - [ ] **Step 1: Add Dashboard link to sidebar** In `frontend/layouts/default.vue`, find the existing `SidebarLink` for applications (line ~41) and add the Dashboard link BEFORE it: ```vue ``` Note: move the `border-t` class from the applications link to the dashboard link (first item gets the border). - [ ] **Step 2: Update auth middleware redirect** In `frontend/middleware/auth.global.ts`, change the redirect from `/applications` to `/dashboard`: Change: ```typescript if (auth.isAuthenticated && (isLogin || to.path === '/')) { return navigateTo('/applications') } ``` To: ```typescript if (auth.isAuthenticated && (isLogin || to.path === '/')) { return navigateTo('/dashboard') } ``` - [ ] **Step 3: Commit** ```bash git add frontend/layouts/default.vue frontend/middleware/auth.global.ts git commit -m "feat : add Dashboard to sidebar and redirect / to /dashboard" ``` --- ## Task 9: Health metrics on detail page **Files:** - Modify: `frontend/pages/applications/[slug].vue` - [ ] **Step 1: Add imports and state** At the top of `