From 500e14c4a82f41bb6e6960171448bfade16c33d3 Mon Sep 17 00:00:00 2001 From: Luca Sacchi Ricciardi Date: Tue, 7 Apr 2026 15:03:17 +0200 Subject: [PATCH] docs: add frontend development prompt --- prompt/prompt-frontend-dev-react.md | 1026 +++++++++++++++++++++++++++ 1 file changed, 1026 insertions(+) create mode 100644 prompt/prompt-frontend-dev-react.md diff --git a/prompt/prompt-frontend-dev-react.md b/prompt/prompt-frontend-dev-react.md new file mode 100644 index 0000000..ca6ecaf --- /dev/null +++ b/prompt/prompt-frontend-dev-react.md @@ -0,0 +1,1026 @@ +# πŸš€ @frontend-dev - React Frontend Implementation + +## πŸ“Š Stato Progetto + +**Data:** 2026-04-07 +**Fase:** 2 - Frontend Development +**Backend:** βœ… COMPLETATO + +### βœ… Cosa Γ¨ pronto +- **Backend API** completa con tutti gli endpoint +- **Database** PostgreSQL con dati di esempio +- **Documentazione API** disponibile su `/docs` +- **CORS** abilitato per comunicazione frontend-backend + +### 🎯 I tuoi task (PrioritΓ  P1) + +--- + +## FE-001: Setup Ambiente React +**Stima:** M (1-2 ore) + +### Obiettivo +Configurare l'ambiente di sviluppo React con Vite, TypeScript, Tailwind CSS e shadcn/ui. + +### Files da creare/configurare +``` +frontend/ +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ components/ +β”‚ β”œβ”€β”€ hooks/ +β”‚ β”œβ”€β”€ lib/ +β”‚ β”œβ”€β”€ pages/ +β”‚ β”œβ”€β”€ services/ +β”‚ β”œβ”€β”€ types/ +β”‚ β”œβ”€β”€ App.tsx +β”‚ └── main.tsx +β”œβ”€β”€ public/ +β”œβ”€β”€ index.html +β”œβ”€β”€ package.json +β”œβ”€β”€ tsconfig.json +β”œβ”€β”€ vite.config.ts +β”œβ”€β”€ tailwind.config.js +└── components.json +``` + +### Implementazione richiesta + +**1. Setup iniziale:** +```bash +cd /home/google/Sources/LucaSacchiNet/mockupAWS +mkdir -p frontend +cd frontend + +# Inizializza progetto Vite con React + TypeScript +npm create vite@latest . -- --template react-ts + +# Installa dipendenze base +npm install + +# Installa Tailwind CSS +npm install -D tailwindcss postcss autoprefixer +npx tailwindcss init -p + +# Installa shadcn/ui +npx shadcn-ui@latest init + +# Installa librerie necessarie +npm install @tanstack/react-query axios recharts lucide-react clsx tailwind-merge +npm install react-router-dom +npm install -D @types/node +``` + +**2. `tailwind.config.js`:** +```javascript +/** @type {import('tailwindcss').Config} */ +export default { + darkMode: ["class"], + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +} +``` + +**3. `src/index.css`:** +```css +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + --radius: 0.5rem; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} +``` + +**4. Installa componenti shadcn/ui necessari:** +```bash +npx shadcn-ui@latest add button +npx shadcn-ui@latest add card +npx shadcn-ui@latest add input +npx shadcn-ui@latest add label +npx shadcn-ui@latest add select +npx shadcn-ui@latest add badge +npx shadcn-ui@latest add table +npx shadcn-ui@latest add dialog +npx shadcn-ui@latest add dropdown-menu +npx shadcn-ui@latest add tabs +npx shadcn-ui@latest add toast +n``` + +### Criteri di accettazione +- [ ] Vite + React + TypeScript configurato +- [ ] Tailwind CSS funzionante +- [ ] shadcn/ui inizializzato +- [ ] Componenti base installati +- [ ] `npm run dev` avvia senza errori + +--- + +## FE-002: Configurazione API Client +**Stima:** S (30-60 min) +**Dipende da:** FE-001 + +### Obiettivo +Configurare Axios per chiamate API al backend. + +### Files da creare +``` +frontend/src/ +β”œβ”€β”€ lib/ +β”‚ └── api.ts +β”œβ”€β”€ types/ +β”‚ └── api.ts +└── .env +``` + +### Implementazione richiesta + +**1. `frontend/.env`:** +```env +VITE_API_URL=http://localhost:8000/api/v1 +``` + +**2. `frontend/src/lib/api.ts`:** +```typescript +import axios from 'axios'; + +const api = axios.create({ + baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8000/api/v1', + headers: { + 'Content-Type': 'application/json', + }, +}); + +// Request interceptor +api.interceptors.request.use( + (config) => { + // Add auth headers here if needed + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +// Response interceptor +api.interceptors.response.use( + (response) => response, + (error) => { + // Handle errors globally + console.error('API Error:', error.response?.data || error.message); + return Promise.reject(error); + } +); + +export default api; +``` + +**3. `frontend/src/types/api.ts`:** +```typescript +export interface Scenario { + id: string; + name: string; + description?: string; + tags: string[]; + status: 'draft' | 'running' | 'completed' | 'archived'; + region: string; + created_at: string; + updated_at: string; + completed_at?: string; + started_at?: string; + total_requests: number; + total_cost_estimate: number; +} + +export interface ScenarioCreate { + name: string; + description?: string; + tags?: string[]; + region: string; +} + +export interface ScenarioUpdate { + name?: string; + description?: string; + tags?: string[]; +} + +export interface ScenarioList { + items: Scenario[]; + total: number; + page: number; + page_size: number; +} + +export interface MetricSummary { + total_requests: number; + total_cost_usd: number; + sqs_blocks: number; + lambda_invocations: number; + llm_tokens: number; + pii_violations: number; +} + +export interface CostBreakdown { + service: string; + cost_usd: number; + percentage: number; +} + +export interface MetricsResponse { + scenario_id: string; + summary: MetricSummary; + cost_breakdown: CostBreakdown[]; + timeseries: { + timestamp: string; + metric_type: string; + value: number; + }[]; +} +``` + +### Criteri di accettazione +- [ ] Axios configurato con base URL +- [ ] Interceptors per errori +- [ ] TypeScript types definiti +- [ ] Test chiamata API funzionante + +--- + +## FE-003: React Query Hooks +**Stima:** M (1-2 ore) +**Dipende da:** FE-002 + +### Obiettivo +Creare hooks per fetchare dati dal backend usando React Query. + +### Files da creare +``` +frontend/src/ +β”œβ”€β”€ hooks/ +β”‚ β”œβ”€β”€ useScenarios.ts +β”‚ β”œβ”€β”€ useScenario.ts +β”‚ └── useMetrics.ts +└── providers/ + └── QueryProvider.tsx +``` + +### Implementazione richiesta + +**1. `frontend/src/providers/QueryProvider.tsx`:** +```typescript +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { ReactNode } from 'react'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: 1, + refetchOnWindowFocus: false, + }, + }, +}); + +export function QueryProvider({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} +``` + +**2. `frontend/src/hooks/useScenarios.ts`:** +```typescript +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import api from '@/lib/api'; +import type { Scenario, ScenarioCreate, ScenarioUpdate, ScenarioList } from '@/types/api'; + +const SCENARIOS_KEY = 'scenarios'; + +export function useScenarios(page = 1, pageSize = 20, status?: string, region?: string) { + return useQuery({ + queryKey: [SCENARIOS_KEY, page, pageSize, status, region], + queryFn: async () => { + const params = new URLSearchParams(); + params.append('page', page.toString()); + params.append('page_size', pageSize.toString()); + if (status) params.append('status', status); + if (region) params.append('region', region); + + const response = await api.get(`/scenarios?${params.toString()}`); + return response.data; + }, + }); +} + +export function useScenario(id: string) { + return useQuery({ + queryKey: [SCENARIOS_KEY, id], + queryFn: async () => { + const response = await api.get(`/scenarios/${id}`); + return response.data; + }, + enabled: !!id, + }); +} + +export function useCreateScenario() { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (data: ScenarioCreate) => { + const response = await api.post('/scenarios', data); + return response.data; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [SCENARIOS_KEY] }); + }, + }); +} + +export function useUpdateScenario(id: string) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (data: ScenarioUpdate) => { + const response = await api.put(`/scenarios/${id}`, data); + return response.data; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [SCENARIOS_KEY] }); + queryClient.invalidateQueries({ queryKey: [SCENARIOS_KEY, id] }); + }, + }); +} + +export function useDeleteScenario() { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (id: string) => { + await api.delete(`/scenarios/${id}`); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [SCENARIOS_KEY] }); + }, + }); +} + +export function useStartScenario(id: string) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async () => { + const response = await api.post(`/scenarios/${id}/start`); + return response.data; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [SCENARIOS_KEY, id] }); + }, + }); +} + +export function useStopScenario(id: string) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async () => { + const response = await api.post(`/scenarios/${id}/stop`); + return response.data; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [SCENARIOS_KEY, id] }); + }, + }); +} +``` + +**3. `frontend/src/hooks/useMetrics.ts`:** +```typescript +import { useQuery } from '@tanstack/react-query'; +import api from '@/lib/api'; +import type { MetricsResponse } from '@/types/api'; + +const METRICS_KEY = 'metrics'; + +export function useMetrics(scenarioId: string) { + return useQuery({ + queryKey: [METRICS_KEY, scenarioId], + queryFn: async () => { + const response = await api.get(`/scenarios/${scenarioId}/metrics`); + return response.data; + }, + enabled: !!scenarioId, + refetchInterval: 5000, // Refresh every 5 seconds for running scenarios + }); +} +``` + +### Criteri di accettazione +- [ ] QueryProvider configurato +- [ ] useScenarios con pagination/filters +- [ ] useScenario per dettaglio +- [ ] Mutations per CRUD operations +- [ ] Cache invalidation funzionante + +--- + +## FE-004: Layout e Navigazione +**Stima:** M (1-2 ore) +**Dipende da:** FE-003 + +### Obiettivo +Creare layout principale con header, sidebar e routing. + +### Files da creare +``` +frontend/src/ +β”œβ”€β”€ components/ +β”‚ β”œβ”€β”€ layout/ +β”‚ β”‚ β”œβ”€β”€ Layout.tsx +β”‚ β”‚ β”œβ”€β”€ Header.tsx +β”‚ β”‚ └── Sidebar.tsx +β”‚ └── ui/ # shadcn components +β”œβ”€β”€ pages/ +β”‚ β”œβ”€β”€ Dashboard.tsx +β”‚ β”œβ”€β”€ ScenariosPage.tsx +β”‚ β”œβ”€β”€ ScenarioDetail.tsx +β”‚ └── NotFound.tsx +β”œβ”€β”€ App.tsx +└── main.tsx +``` + +### Implementazione richiesta + +**1. `frontend/src/components/layout/Layout.tsx`:** +```typescript +import { Outlet } from 'react-router-dom'; +import { Header } from './Header'; +import { Sidebar } from './Sidebar'; + +export function Layout() { + return ( +
+
+
+ +
+ +
+
+
+ ); +} +``` + +**2. `frontend/src/components/layout/Header.tsx`:** +```typescript +import { Link } from 'react-router-dom'; +import { Cloud, Menu } from 'lucide-react'; +import { Button } from '@/components/ui/button'; + +export function Header() { + return ( +
+
+ + + mockupAWS + +
+ + AWS Cost Simulator + +
+
+
+ ); +} +``` + +**3. `frontend/src/components/layout/Sidebar.tsx`:** +```typescript +import { NavLink } from 'react-router-dom'; +import { LayoutDashboard, List } from 'lucide-react'; + +const navItems = [ + { to: '/', label: 'Dashboard', icon: LayoutDashboard }, + { to: '/scenarios', label: 'Scenarios', icon: List }, +]; + +export function Sidebar() { + return ( + + ); +} +``` + +**4. `frontend/src/App.tsx`:** +```typescript +import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import { QueryProvider } from './providers/QueryProvider'; +import { Toaster } from '@/components/ui/toaster'; +import { Layout } from './components/layout/Layout'; +import { Dashboard } from './pages/Dashboard'; +import { ScenariosPage } from './pages/ScenariosPage'; +import { ScenarioDetail } from './pages/ScenarioDetail'; +import { NotFound } from './pages/NotFound'; + +function App() { + return ( + + + + }> + } /> + } /> + } /> + } /> + + + + + + ); +} + +export default App; +``` + +**5. `frontend/src/main.tsx`:** +```typescript +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import './index.css'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + +); +``` + +### Criteri di accettazione +- [ ] Layout con Header e Sidebar +- [ ] React Router configurato +- [ ] Navigazione funzionante +- [ ] Responsive design +- [ ] Active state su menu + +--- + +## FE-005: Dashboard Page +**Stima:** L (2-4 ore) +**Dipende da:** FE-004 + +### Obiettivo +Creare la pagina dashboard con overview e statistiche. + +### Files da creare +``` +frontend/src/pages/Dashboard.tsx +frontend/src/components/dashboard/ +β”œβ”€β”€ StatCard.tsx +β”œβ”€β”€ RecentScenarios.tsx +└── CostOverview.tsx +``` + +### Implementazione richiesta + +**1. `frontend/src/components/dashboard/StatCard.tsx`:** +```typescript +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { LucideIcon } from 'lucide-react'; + +interface StatCardProps { + title: string; + value: string | number; + description?: string; + icon: LucideIcon; + trend?: { + value: number; + label: string; + }; +} + +export function StatCard({ title, value, description, icon: Icon, trend }: StatCardProps) { + return ( + + + {title} + + + +
{value}
+ {description && ( +

{description}

+ )} + {trend && ( +
= 0 ? 'text-green-600' : 'text-red-600'}`}> + {trend.value >= 0 ? '+' : ''}{trend.value}% {trend.label} +
+ )} +
+
+ ); +} +``` + +**2. `frontend/src/pages/Dashboard.tsx`:** +```typescript +import { useScenarios } from '@/hooks/useScenarios'; +import { StatCard } from '@/components/dashboard/StatCard'; +import { RecentScenarios } from '@/components/dashboard/RecentScenarios'; +import { Activity, DollarSign, Server, AlertTriangle } from 'lucide-react'; + +export function Dashboard() { + const { data: scenarios, isLoading } = useScenarios(1, 100); + + const totalScenarios = scenarios?.total || 0; + const runningScenarios = scenarios?.items.filter(s => s.status === 'running').length || 0; + const totalCost = scenarios?.items.reduce((sum, s) => sum + s.total_cost_estimate, 0) || 0; + + if (isLoading) { + return
Loading...
; + } + + return ( +
+
+

Dashboard

+

+ Overview of your AWS cost simulation scenarios +

+
+ +
+ + + + +
+ +
+ + {/* Add CostChart component here */} +
+
+ ); +} +``` + +### Criteri di accettazione +- [ ] Stat cards con dati reali +- [ ] Loading states +- [ ] Lista scenari recenti +- [ ] Layout responsive + +--- + +## FE-006: Scenarios List Page +**Stima:** L (2-4 ore) +**Dipende da:** FE-005 + +### Obiettivo +Pagina lista scenari con filtri, pagination e azioni. + +### Files da creare +``` +frontend/src/pages/ScenariosPage.tsx +frontend/src/components/scenarios/ +β”œβ”€β”€ ScenarioTable.tsx +β”œβ”€β”€ ScenarioFilters.tsx +β”œβ”€β”€ CreateScenarioDialog.tsx +└── DeleteScenarioDialog.tsx +``` + +### Implementazione richiesta + +**1. `frontend/src/components/scenarios/ScenarioTable.tsx`:** +```typescript +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { MoreHorizontal, Play, Square, Archive, Trash2 } from 'lucide-react'; +import type { Scenario } from '@/types/api'; + +interface ScenarioTableProps { + scenarios: Scenario[]; + onStart: (id: string) => void; + onStop: (id: string) => void; + onArchive: (id: string) => void; + onDelete: (id: string) => void; +} + +const statusColors = { + draft: 'secondary', + running: 'default', + completed: 'outline', + archived: 'destructive', +} as const; + +export function ScenarioTable({ + scenarios, + onStart, + onStop, + onArchive, + onDelete, +}: ScenarioTableProps) { + const navigate = useNavigate(); + + return ( + + + + Name + Status + Region + Requests + Cost + Actions + + + + {scenarios.map((scenario) => ( + navigate(`/scenarios/${scenario.id}`)} + > + + {scenario.name} + + + + {scenario.status} + + + {scenario.region} + {scenario.total_requests} + ${scenario.total_cost_estimate.toFixed(6)} + + + e.stopPropagation()}> + + + + {scenario.status === 'draft' && ( + onStart(scenario.id)}> + + Start + + )} + {scenario.status === 'running' && ( + onStop(scenario.id)}> + + Stop + + )} + {scenario.status === 'completed' && ( + onArchive(scenario.id)}> + + Archive + + )} + onDelete(scenario.id)} + > + + Delete + + + + + + ))} + +
+ ); +} +``` + +### Criteri di accettazione +- [ ] Tabella con dati reali +- [ ] Filtri per status e regione +- [ ] Pagination +- [ ] Azioni per ogni scenario +- [ ] Confirm dialog per delete + +--- + +## πŸ“‹ Checklist Completamento + +Prima di procedere, verifica: + +- [ ] Tutti i componenti creati +- [ ] TypeScript types corretti +- [ ] Error handling appropriato +- [ ] Loading states +- [ ] Responsive design +- [ ] API integration funzionante + +--- + +## πŸ§ͺ Testing + +```bash +# Installa dipendenze +cd frontend +npm install + +# Avvia dev server +npm run dev + +# Build produzione +npm run build +``` + +--- + +## πŸš€ Comandi Utili + +```bash +# Aggiungi nuovo componente shadcn +npx shadcn-ui@latest add [component-name] + +# Build +npm run build + +# Preview build +npm run preview +``` + +--- + +**@frontend-dev: INIZIA ORA! πŸš€** + +Procedi in ordine: FE-001 β†’ FE-002 β†’ FE-003 β†’ FE-004 β†’ FE-005 β†’ FE-006 + +**Domande?** Riferiti alla documentazione API su `http://localhost:8000/docs` + +**Commit convenzioni:** +- `feat: setup React environment with Vite and Tailwind` +- `feat: add API client with Axios` +- `feat: implement React Query hooks` +- `feat: add layout and navigation` +- `feat: create dashboard page` +- `feat: add scenarios list page` + +**Buon lavoro! πŸ’ͺ**