---
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 |
| `{application_kebab}` | kebab-case route segment for application (PascalCase → kebab-case, e.g., `HumanResources` → `human-resources`) |
| `{module_kebab}` | kebab-case route segment for module (PascalCase → kebab-case, e.g., `TimeManagement` → `time-management`) |

---

## ROUTE NAMING RULE

> **BLOCKING:** All route paths MUST use **kebab-case** to match navigation seed data exactly.
>
> - Transformation: `PascalCase` → `kebab-case` (e.g., `HumanResources` → `human-resources`)
> - Single words stay lowercase: `Employees` → `employees`
> - Multi-word segments MUST have hyphens: `TimeManagement` → `time-management`
> - **FORBIDDEN:** `humanresources`, `timemanagement` (concatenated words without hyphens)
> - **REQUIRED:** `human-resources`, `time-management` (proper kebab-case)
>
> Route paths MUST match the navigation seed data routes (which use `ToKebabCase()`).

---

## EXECUTION SEQUENCE

### 1. Generate Frontend Pages (conventions-compliant)

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

This generates (all natively compliant with /ui-components conventions):
- `{EntityName}Page.tsx` — List page wrapper with i18n
- `{EntityName}ListView.tsx` — EntityCard grid + DataTable + ViewToggle + responsive + loading/error/empty states
- `{EntityName}DetailPage.tsx` — Detail page with back button + pill tabs + info cards
- `use{EntityName}Preferences.ts` — Preferences hook (viewMode, sorting, filters, columns)
- i18n files for 4 languages (fr, en, it, de)

> **Note:** The `type: "page"` scaffold already includes CSS variables, EntityCard, responsive grid,
> semantic HTML, memo/useCallback, and all ui-components conventions. No manual corrections needed.

### 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. Generate Route Configuration

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

This generates:
- `navRoutes.generated.ts` - Route registry (NavRoute to API/web path mapping)
- `clientRoutes.generated.tsx` - Route fragments grouped by context with page imports

### 4. Wire Routes to App.tsx (BLOCKING)

> **CRITICAL:** This step is MANDATORY. Without it, routes exist as files but are invisible to the React Router. The page will be BLANK.

After `scaffold_routes` generates the route files, you MUST manually insert the routes into `App.tsx`.

**Step 4a:** Import the page components at the top of App.tsx:

```tsx
import { {EntityName}Page } from '@/pages/{Context}/{Application}/{Module}/{EntityName}Page';
// Or lazy-loaded:
const {EntityName}Page = lazy(() => import('@/pages/{Context}/{Application}/{Module}/{EntityName}Page'));
```

**Step 4b:** Detect the App.tsx routing pattern:

Read App.tsx and detect which pattern is used:

**Pattern A** — If App.tsx contains `contextRoutes: ContextRouteExtensions`:
  → Add routes to `contextRoutes.{context}[]` with **RELATIVE** paths (no leading `/`)

  ```tsx
  const contextRoutes: ContextRouteExtensions = {
    business: [
      // existing routes...
      { path: '{application_kebab}/{module_kebab}', element: <{EntityName}ListPage /> },
      { path: '{application_kebab}/{module_kebab}/new', element: <Create{EntityName}Page /> },
      { path: '{application_kebab}/{module_kebab}/:id', element: <{EntityName}DetailPage /> },
      { path: '{application_kebab}/{module_kebab}/:id/edit', element: <Create{EntityName}Page /> },
    ],
  };
  ```

  Routes are automatically injected into BOTH standard (`/business/...`) and tenant-prefixed (`/t/:slug/business/...`) route trees by `mergeRoutes()`. No manual duplication needed.

**Pattern B** — If App.tsx contains `<Route path="/{context}" element={<{Layout} />}>` (JSX Routes):
  → Insert `<Route>` children inside the Layout wrapper (see below)

  ```tsx
  <Route path="/business" element={<BusinessLayout />}>
    {/* ... existing routes ... */}
    <Route path="{application_kebab}/{module_kebab}" element={<{EntityName}Page />} />
  </Route>
  ```

  ALSO add the same routes inside the tenant-prefixed block:
  Find `<Route path="/t/:slug">` and add the **same route entries** there.

**Step 4c:** Context-to-Layout mapping:

| Context | Layout Component | Route path |
|---------|------------------|------------|
| `platform.*` | `AdminLayout` | `/platform` |
| `business.*` | `BusinessLayout` | `/business` |
| `personal.*` | `UserLayout` | `/personal/myspace` |

**Step 4d:** Verify wiring:

```
Tool: mcp__smartstack__validate_frontend_routes
Args:
  scope: "routes"
```

If `appWiring.issues` is not empty, fix the wiring before proceeding.

**FORBIDDEN (both patterns):**
- Adding business/platform/personal routes to `clientRoutes[]` with absolute paths — `clientRoutes` is ONLY for routes outside SmartStack contexts (e.g., `/about`, `/pricing`)
- Adding routes OUTSIDE the Layout wrapper (shell will not render)
- Using `createBrowserRouter` (SmartStack uses `useRoutes()` + `mergeRoutes()`)

### 5. Verify i18n Files

> **Note:** i18n files are now auto-generated by `type: "page"` scaffold (step 1).
> Verify the 4 language files were created and customize the `title`/`subtitle` with the proper labels.

If the generated translations need customization (e.g., replacing generic title with `{labels.fr}`):
- `i18n/locales/fr/{entityCode}.json` → set `"title": "{labels.fr}"`
- `i18n/locales/en/{entityCode}.json` → set `"title": "{labels.en}"`
- `i18n/locales/it/{entityCode}.json` → set `"title": "{labels.it}"`
- `i18n/locales/de/{entityCode}.json` → set `"title": "{labels.de}"`

### 6. 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`
- Generated `clientRoutes.generated.tsx`
- Wired routes in `App.tsx` (standard + tenant blocks)

### 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_kebab}">
  <Route index element={<Navigate to="{module_kebab}" replace />} />
  <Route path="{module_kebab}" element={<{EntityName}Page />} />
</Route>
```
```

---

## ROUTING RULES

See [references/frontend-verification.md](../references/frontend-verification.md) for detailed routing rules with code examples:
- **Rule 1:** Routes MUST be inside Layout wrappers (BusinessLayout, AdminLayout, UserLayout)
- **Rule 2:** Use NESTED routes (not flat) — flat routes bypass layout and break the shell

---

## 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 all 7 checks on generated frontend files.**

See [references/frontend-verification.md](../references/frontend-verification.md) for the complete checklist:
1. CSS Variables (BLOCKING) — no hardcoded Tailwind colors
2. API Client (BLOCKING) — use configured client, not raw axios
3. EntityCard — use EntityCard component for grid/card views
4. i18n (BLOCKING) — exactly 4 language files with identical keys
5. Route (BLOCKING) — inside Layout wrapper, nested, correct path convention
6. States — loading, error, empty states present
7. TypeScript Build (BLOCKING) — `npx tsc --noEmit` passes for generated files

---

## SUCCESS METRICS

- React component generated
- API client generated with types
- Routes updated (nested structure)
- Routes wired in App.tsx (standard + tenant blocks)
- i18n files created (4 languages)
- Post-generation verification passed (all 7 checks + route wiring check)
- 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`
