Complete frontend implementation (FE-001 to FE-006): FE-001: Setup Ambiente React - Initialize Vite + React + TypeScript project - Configure Tailwind CSS with custom theme - Add shadcn/ui components (Button, Card, Badge, Table, DropdownMenu, Toaster) - Install dependencies: axios, react-query, react-router-dom, lucide-react, etc. - Configure path aliases (@/components, @/lib, etc.) FE-002: Configurazione API Client - Create lib/api.ts with Axios instance - Add TypeScript types for Scenario, Metrics, etc. - Configure environment variable VITE_API_URL FE-003: React Query Hooks - Create QueryProvider with React Query client - Add useScenarios hook with pagination/filters - Add useScenario hook for detail view - Add mutations: create, update, delete, start, stop - Add useMetrics hook with auto-refresh - Implement cache invalidation FE-004: Layout e Navigazione - Create Layout component with Header and Sidebar - Configure React Router with routes: * / - Dashboard * /scenarios - Scenarios list * /scenarios/:id - Scenario detail - Implement responsive navigation - Add active state styling FE-005: Dashboard Page - Create Dashboard with stat cards - Display total scenarios, running count, total cost, PII violations - Use real data from useScenarios hook - Add loading states FE-006: Scenarios List Page - Create ScenariosPage with data table - Display scenario name, status (with badge), region, requests, cost - Add action dropdown (Start, Stop, Delete) - Implement navigation to detail view Components Created: - ui/button.tsx - Button component with variants - ui/card.tsx - Card component with header/content/footer - ui/badge.tsx - Badge component for status - ui/table.tsx - Table component - ui/dropdown-menu.tsx - Dropdown menu - ui/toaster.tsx - Toast notifications Pages Created: - Dashboard.tsx - Main dashboard view - ScenariosPage.tsx - List of scenarios - ScenarioDetail.tsx - Scenario detail with metrics - NotFound.tsx - 404 page All features integrated with backend API. Tasks: FE-001, FE-002, FE-003, FE-004, FE-005, FE-006 complete
185 lines
2.8 KiB
CSS
185 lines
2.8 KiB
CSS
.counter {
|
|
font-size: 16px;
|
|
padding: 5px 10px;
|
|
border-radius: 5px;
|
|
color: var(--accent);
|
|
background: var(--accent-bg);
|
|
border: 2px solid transparent;
|
|
transition: border-color 0.3s;
|
|
margin-bottom: 24px;
|
|
|
|
&:hover {
|
|
border-color: var(--accent-border);
|
|
}
|
|
&:focus-visible {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 2px;
|
|
}
|
|
}
|
|
|
|
.hero {
|
|
position: relative;
|
|
|
|
.base,
|
|
.framework,
|
|
.vite {
|
|
inset-inline: 0;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.base {
|
|
width: 170px;
|
|
position: relative;
|
|
z-index: 0;
|
|
}
|
|
|
|
.framework,
|
|
.vite {
|
|
position: absolute;
|
|
}
|
|
|
|
.framework {
|
|
z-index: 1;
|
|
top: 34px;
|
|
height: 28px;
|
|
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
|
|
scale(1.4);
|
|
}
|
|
|
|
.vite {
|
|
z-index: 0;
|
|
top: 107px;
|
|
height: 26px;
|
|
width: auto;
|
|
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
|
|
scale(0.8);
|
|
}
|
|
}
|
|
|
|
#center {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 25px;
|
|
place-content: center;
|
|
place-items: center;
|
|
flex-grow: 1;
|
|
|
|
@media (max-width: 1024px) {
|
|
padding: 32px 20px 24px;
|
|
gap: 18px;
|
|
}
|
|
}
|
|
|
|
#next-steps {
|
|
display: flex;
|
|
border-top: 1px solid var(--border);
|
|
text-align: left;
|
|
|
|
& > div {
|
|
flex: 1 1 0;
|
|
padding: 32px;
|
|
@media (max-width: 1024px) {
|
|
padding: 24px 20px;
|
|
}
|
|
}
|
|
|
|
.icon {
|
|
margin-bottom: 16px;
|
|
width: 22px;
|
|
height: 22px;
|
|
}
|
|
|
|
@media (max-width: 1024px) {
|
|
flex-direction: column;
|
|
text-align: center;
|
|
}
|
|
}
|
|
|
|
#docs {
|
|
border-right: 1px solid var(--border);
|
|
|
|
@media (max-width: 1024px) {
|
|
border-right: none;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
}
|
|
|
|
#next-steps ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
display: flex;
|
|
gap: 8px;
|
|
margin: 32px 0 0;
|
|
|
|
.logo {
|
|
height: 18px;
|
|
}
|
|
|
|
a {
|
|
color: var(--text-h);
|
|
font-size: 16px;
|
|
border-radius: 6px;
|
|
background: var(--social-bg);
|
|
display: flex;
|
|
padding: 6px 12px;
|
|
align-items: center;
|
|
gap: 8px;
|
|
text-decoration: none;
|
|
transition: box-shadow 0.3s;
|
|
|
|
&:hover {
|
|
box-shadow: var(--shadow);
|
|
}
|
|
.button-icon {
|
|
height: 18px;
|
|
width: 18px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 1024px) {
|
|
margin-top: 20px;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
|
|
li {
|
|
flex: 1 1 calc(50% - 8px);
|
|
}
|
|
|
|
a {
|
|
width: 100%;
|
|
justify-content: center;
|
|
box-sizing: border-box;
|
|
}
|
|
}
|
|
}
|
|
|
|
#spacer {
|
|
height: 88px;
|
|
border-top: 1px solid var(--border);
|
|
@media (max-width: 1024px) {
|
|
height: 48px;
|
|
}
|
|
}
|
|
|
|
.ticks {
|
|
position: relative;
|
|
width: 100%;
|
|
|
|
&::before,
|
|
&::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: -4.5px;
|
|
border: 5px solid transparent;
|
|
}
|
|
|
|
&::before {
|
|
left: 0;
|
|
border-left-color: var(--border);
|
|
}
|
|
&::after {
|
|
right: 0;
|
|
border-right-color: var(--border);
|
|
}
|
|
}
|