---
name: step-05-frontend
description: Generate frontend code using MCP tools (component, API client, routes, i18n)
prev_step: steps/step-04-backend.md
next_step: steps/step-06-migration.md
---

# Step 5: Frontend Generation

## MANDATORY EXECUTION RULES

- ALWAYS use MCP tools for frontend generation
- ALWAYS generate i18n for 4 languages (fr, en, it, de)
- ALWAYS use nested routes (not flat)
- YOU ARE AN ORCHESTRATOR calling MCP, not a generator

## YOUR TASK

Use MCP tools to generate:
1. React component (scaffold_extension type: component)
2. TypeScript API client (scaffold_api_client)
3. Route configuration (scaffold_routes)
4. i18n files (scaffold_translations for UI)

---

## AVAILABLE STATE

From previous steps:

| Variable | Description |
|----------|-------------|
| `{full_path}` | Complete navigation path (navRoute) |
| `{entity_name}` | PascalCase entity name |
| `{entity_code}` | kebab-case code |
| `{labels}` | Object with fr, en, it, de |
| `{api_route}` | API endpoint path |

---

## EXECUTION SEQUENCE

### 1. Generate React Component

```
Tool: mcp__smartstack__scaffold_extension
Args:
  type: "component"
  name: "{entity_name}"
  options:
    outputPath: "web/src/pages/{context}/{application}/{module}"
    dryRun: false
```

This generates:
- `{EntityName}Page.tsx` - Main page component
- `{EntityName}ListView.tsx` - Reusable list view
- `use{EntityName}.ts` - Custom hook for data

### 2. Generate API Client

```
Tool: mcp__smartstack__scaffold_api_client
Args:
  navRoute: "{full_path}"
  name: "{entity_name}"
  methods: ["getAll", "getById", "create", "update", "delete"]
  options:
    includeTypes: true
    includeHook: true
```

This generates:
- `services/api/{entityCode}Api.ts` - API service
- `types/{entityName}.types.ts` - TypeScript interfaces
- `hooks/use{EntityName}Api.ts` - React Query hook (optional)

### 3. Update Route Configuration

```
Tool: mcp__smartstack__scaffold_routes
Args:
  source: "controllers"
  scope: "all"
  options:
    includeGuards: true
    generateRegistry: true
```

This updates:
- `navRoutes.generated.ts` - Route registry
- `routes.tsx` / `App.tsx` - React Router configuration

#### ⚠️ CRITICAL: Route Placement Inside Layout Wrapper

**BLOCKING RULE:** New routes MUST be added as **children** of the existing layout route for the target context. SmartStack layouts (`AdminLayout`, `BusinessLayout`, `UserLayout`) provide the shell (header with AvatarMenu, sidebar, navigation) via React Router's `<Outlet />`.

**If routes are placed OUTSIDE the layout wrapper, the entire shell (header, sidebar, AvatarMenu) will NOT render.**

| Context | Layout Route to find in App.tsx | Layout Component |
|---------|--------------------------------|------------------|
| `platform.*` | `<Route path="/platform" element={<AdminLayout />}>` | `AdminLayout` |
| `business.*` | `<Route path="/business" element={<BusinessLayout />}>` | `BusinessLayout` |
| `personal.*` | `<Route path="/personal/myspace" element={<UserLayout />}>` | `UserLayout` |

**Insertion rules:**

1. Open `App.tsx` (or `routes.tsx`)
2. Find the existing `<Route path="/{context}" element={<{Context}Layout />}>` block
3. Add the new routes **INSIDE** that block as children
4. Use **relative paths** (not absolute) since they are children of the context route

```tsx
// ✅ CORRECT - Route added INSIDE the layout wrapper
<Route path="/business" element={<BusinessLayout />}>
  {/* ... existing routes ... */}
  <Route path="{application}/{module}" element={<{EntityName}Page />} />
</Route>

// ❌ FORBIDDEN - Route added OUTSIDE layout (AvatarMenu will disappear!)
<Route path="/business/{application}/{module}" element={<{EntityName}Page />} />
```

**For new applications (no existing layout route for the context):**

If the context doesn't have a layout route yet, create one wrapping all child routes:

```tsx
<Route path="/{context}/{application}" element={<{Context}Layout />}>
  <Route index element={<Navigate to="{default_module}" replace />} />
  <Route path="{module}" element={<{EntityName}Page />} />
</Route>
```

**Remember:** Also add the routes inside the tenant-prefixed block (`/t/:slug/...`) if it exists.

### 4. Generate i18n Files

Create translation files for each language:

```markdown
### French (fr/{entityCode}.json)
```json
{
  "title": "{labels.fr}",
  "subtitle": "Gestion de {labels.fr}",
  "columns": {
    "code": "Code",
    "name": "Nom",
    "createdAt": "Créé le",
    "actions": "Actions"
  },
  "form": {
    "code": "Code",
    "name": "Nom",
    "submit": "Enregistrer",
    "cancel": "Annuler"
  },
  "messages": {
    "created": "{labels.fr} créé avec succès",
    "updated": "{labels.fr} mis à jour avec succès",
    "deleted": "{labels.fr} supprimé avec succès",
    "error": "Une erreur est survenue"
  }
}
```

Repeat for en, it, de with appropriate translations.

### 5. Present Output to User

```markdown
## Frontend Code Generated

### Pages
- `pages/{context}/{application}/{module}/{EntityName}Page.tsx`
- `pages/{context}/{application}/{module}/{EntityName}ListView.tsx`

### Components
- `components/{context}/{module}/{EntityName}.tsx`
- `hooks/use{EntityName}.ts`

### API Client
- `services/api/{entityCode}Api.ts`
- `types/{entityName}.types.ts`

### Routes
- Updated `navRoutes.generated.ts`
- Updated `routes.tsx` with nested route

### i18n
- `locales/fr/{entityCode}.json`
- `locales/en/{entityCode}.json`
- `locales/it/{entityCode}.json`
- `locales/de/{entityCode}.json`

### Route Configuration
```tsx
// In routes.tsx - NESTED routes (not flat!)
<Route path="{application}">
  <Route index element={<Navigate to="{module}" replace />} />
  <Route path="{module}" element={<{EntityName}Page />} />
</Route>
```
```

---

## ROUTING RULES

### Rule 1: Routes MUST be inside Layout wrappers

**CRITICAL:** All routes MUST be nested inside the appropriate context layout. This is what provides the SmartStack shell (header with AvatarMenu, sidebar, navigation).

```tsx
// ✅ CORRECT - Inside BusinessLayout (shell renders: header + AvatarMenu + sidebar)
<Route path="/business" element={<BusinessLayout />}>
  <Route path="sales/products" element={<ProductsPage />} />
</Route>

// ❌ FORBIDDEN - Outside layout (NO shell, NO header, NO AvatarMenu!)
<Route path="/business/sales/products" element={<ProductsPage />} />
```

### Rule 2: Use nested routes (not flat)

**CRITICAL:** SmartStack requires NESTED routes within the layout:

```tsx
// ✅ CORRECT - Nested routes inside layout
<Route path="/business" element={<BusinessLayout />}>
  <Route path="sales">
    <Route index element={<Navigate to="products" replace />} />
    <Route path="products" element={<ProductsPage />} />
    <Route path="orders" element={<OrdersPage />} />
  </Route>
</Route>

// ❌ FORBIDDEN - Flat routes (cause redirect issues + bypass layout)
<Route path="/business/sales" element={<Navigate to="products" />} />
<Route path="/business/sales/products" element={<ProductsPage />} />
```

---

## I18N STRUCTURE

All i18n files must follow this structure:

```
locales/
├── fr/
│   └── {entityCode}.json
├── en/
│   └── {entityCode}.json
├── it/
│   └── {entityCode}.json
└── de/
    └── {entityCode}.json
```

---

## POST-GENERATION VERIFICATION (MANDATORY)

**Before proceeding to step-06, run these checks on ALL generated frontend files:**

### 1. CSS Variables Check (BLOCKING)

Scan all generated `.tsx` files for hardcoded Tailwind colors:

```
FORBIDDEN patterns: bg-blue-, bg-red-, bg-green-, bg-gray-, bg-amber-,
                    text-blue-, text-red-, text-green-, text-gray-, text-amber-,
                    border-blue-, border-red-, border-green-, border-gray-
```

If ANY hardcoded color is found → **FIX IT** using CSS variables from style-guide.md before continuing.

### 2. API Client Check (BLOCKING)

Verify all API service files use the configured client:
- **CORRECT:** `import { api } from '@/services/api/apiClient'` or `import api from '../api'`
- **FORBIDDEN:** `import axios from 'axios'`

### 3. EntityCard Check

If a grid/card view is generated, verify it uses `EntityCard` component (not custom divs).

### 4. i18n Check (BLOCKING)

Verify exactly **4 language files** exist with identical key structures:
- `fr/{entityCode}.json`
- `en/{entityCode}.json`
- `it/{entityCode}.json`
- `de/{entityCode}.json`

### 5. Route Check (BLOCKING)

Verify routes are:
- **INSIDE** the Layout wrapper (`BusinessLayout`, `AdminLayout`, or `UserLayout`)
- **NESTED** (not flat)
- Following path convention: `/{context}/{application}/{module}`

### 6. States Check

Verify the list page includes:
- Loading state (spinner)
- Error state (icon + message + retry button)
- Empty state (icon + message)

### 7. TypeScript Build Check (BLOCKING)

Run the frontend type check to verify all generated files compile:

```bash
cd web && npx tsc --noEmit 2>&1 | head -50
```

OR if the project uses a build script:

```bash
cd web && npm run build 2>&1 | head -80
```

IF type errors exist **in generated files**: Fix them before proceeding.
Common issues: missing imports, wrong interface names, incorrect prop types, missing hook return types.

IF type errors exist **in other pre-existing files**: NON-BLOCKING. Note them but proceed.

**Focus only on errors in files generated by this step:**
- `pages/{context}/{application}/{module}/*.tsx`
- `services/api/{entityCode}Api.ts`
- `types/{entityName}.types.ts`
- `hooks/use{EntityName}*.ts`

---

## SUCCESS METRICS

- React component generated
- API client generated with types
- Routes updated (nested structure)
- i18n files created (4 languages)
- Post-generation verification passed (all 7 checks including TypeScript build)
- Proceeded to step-06-migration.md

## FAILURE MODES

- Component generation failed (check entity name)
- API client generation failed (check navRoute)
- Route configuration failed (check existing routes)
- Hardcoded colors detected → Fix before continuing
- Missing language files → Generate all 4 before continuing

---

## NEXT STEP

After displaying frontend code, proceed to `./step-06-migration.md`
