# Story 1.1: Initialisation du monorepo et infrastructure

Status: review

## Story

As a développeur,
I want un projet monorepo Nuxt 4 + Laravel 12 initialisé avec les configurations de base,
so that le développement peut commencer sur des fondations solides.

## Acceptance Criteria

1. **Given** un nouveau repository Git **When** le projet est initialisé **Then** la structure monorepo `frontend/` (Nuxt 4) + `api/` (Laravel 12) est en place
2. **And** Nuxt 4 est configuré avec SSR activé, TypeScript, et les modules `@nuxtjs/i18n`, `@nuxtjs/tailwindcss`, `@pinia/nuxt`, `nuxt/image`, `@nuxtjs/sitemap`
3. **And** Laravel 12 est configuré en mode API-only avec CORS autorisant le domaine frontend
4. **And** le middleware API Key (`X-API-Key`) est en place sur les routes API
5. **And** les fichiers `.env.example` existent pour frontend et backend
6. **And** TailwindCSS est configuré avec les design tokens (`sky-dark`, `sky-accent` #fa784f, `sky-text`)
7. **And** les polices sont définies (serif narrateur + sans-serif UI)
8. **And** le `.gitignore` est approprié pour les deux applications

## Tasks / Subtasks

- [x] **Task 1: Initialisation structure monorepo** (AC: #1)
  - [x] Créer le dossier racine `skycel/` avec README.md
  - [x] Configurer `.gitignore` global (node_modules, vendor, .env, etc.)
  - [x] Initialiser Git repository

- [x] **Task 2: Setup Frontend Nuxt 4** (AC: #1, #2)
  - [x] Exécuter `npx nuxi@latest init frontend`
  - [x] Confirmer structure Nuxt 4 avec dossier `app/`
  - [x] Activer TypeScript (déjà par défaut dans Nuxt 4)
  - [x] Installer modules: `@nuxtjs/i18n`, `@nuxtjs/tailwindcss`, `@pinia/nuxt`, `@nuxt/image`, `@nuxtjs/sitemap`
  - [x] Configurer `nuxt.config.ts` avec SSR activé
  - [x] Créer `frontend/.env.example`

- [x] **Task 3: Setup Backend Laravel 12** (AC: #1, #3, #4)
  - [x] Exécuter `composer create-project laravel/laravel api`
  - [x] Configurer en mode API-only (supprimer views Blade inutiles)
  - [x] Configurer CORS dans `config/cors.php` pour autoriser le domaine frontend
  - [x] Créer middleware `VerifyApiKey` pour vérifier header `X-API-Key`
  - [x] Enregistrer le middleware sur les routes API
  - [x] Créer `api/.env.example`

- [x] **Task 4: Configuration TailwindCSS avec design tokens** (AC: #6)
  - [x] Configurer `tailwind.config.js` avec thème custom
  - [x] Définir tokens couleurs: `sky-dark` (noir→bleu), `sky-accent` (#fa784f), `sky-text` (blanc cassé)
  - [x] Définir variantes hover/focus pour l'accent
  - [x] Configurer purge pour production

- [x] **Task 5: Configuration des polices** (AC: #7)
  - [x] Choisir police serif élégante pour narrateur/PNJ (ex: Merriweather, Lora, Playfair Display)
  - [x] Choisir police sans-serif moderne pour UI (ex: Inter, Open Sans, Nunito)
  - [x] Configurer les polices dans `tailwind.config.js` (fontFamily)
  - [x] Importer les polices via Google Fonts ou fichiers locaux

- [x] **Task 6: Fichiers .env.example** (AC: #5)
  - [x] `frontend/.env.example` avec: `NUXT_PUBLIC_API_URL`, `NUXT_PUBLIC_API_KEY`
  - [x] `api/.env.example` avec: `APP_KEY`, `DB_*`, `API_KEY`, `CORS_ALLOWED_ORIGINS`

- [x] **Task 7: Validation finale** (AC: tous)
  - [x] `cd frontend && npm run dev` fonctionne
  - [x] `cd api && php artisan serve` fonctionne
  - [x] Requête API avec header `X-API-Key` valide retourne 200
  - [x] Requête API sans header retourne 401
  - [x] Structure des dossiers conforme

## Dev Notes

### Architecture Monorepo

```
skycel/
├── frontend/                   # Application Nuxt 4
│   ├── app/                    # Code applicatif (structure Nuxt 4)
│   │   ├── pages/
│   │   ├── components/
│   │   ├── composables/
│   │   ├── stores/
│   │   ├── layouts/
│   │   ├── plugins/
│   │   ├── assets/
│   │   └── app.vue
│   ├── server/                 # Server routes/API Nuxt (si besoin)
│   ├── public/
│   ├── i18n/
│   ├── nuxt.config.ts
│   ├── tailwind.config.js
│   ├── .env.example
│   └── package.json
├── api/                        # Backend Laravel 12
│   ├── app/
│   │   ├── Http/
│   │   │   ├── Controllers/
│   │   │   ├── Middleware/
│   │   │   │   └── VerifyApiKey.php   # CRÉER
│   │   │   ├── Requests/
│   │   │   └── Resources/
│   │   └── Models/
│   ├── database/
│   ├── routes/
│   │   └── api.php
│   ├── config/
│   │   └── cors.php            # CONFIGURER
│   ├── bootstrap/
│   │   └── app.php             # Enregistrer middleware
│   ├── .env.example
│   └── composer.json
├── docs/                       # Documentation projet (existe déjà)
├── .gitignore
└── README.md
```

### Commandes d'initialisation

```bash
# Frontend Nuxt 4
npx nuxi@latest init frontend
cd frontend
npm install @nuxtjs/i18n @nuxtjs/tailwindcss @pinia/nuxt @nuxt/image @nuxtjs/sitemap pinia-plugin-persistedstate

# Backend Laravel 12
composer create-project laravel/laravel api
cd api
# Pas de packages supplémentaires pour cette story
```

### Configuration nuxt.config.ts

```typescript
// frontend/nuxt.config.ts
export default defineNuxtConfig({
  devtools: { enabled: true },

  // SSR activé (défaut)
  ssr: true,

  // Structure Nuxt 4
  future: {
    compatibilityVersion: 4,
  },

  modules: [
    '@nuxtjs/i18n',
    '@nuxtjs/tailwindcss',
    '@pinia/nuxt',
    '@nuxt/image',
    '@nuxtjs/sitemap',
  ],

  // i18n sera configuré en Story 1.3
  i18n: {
    locales: ['fr', 'en'],
    defaultLocale: 'fr',
    strategy: 'prefix_except_default',
  },

  // Transitions de page (configuré en Story 1.4)
  app: {
    pageTransition: { name: 'page', mode: 'out-in' },
  },

  runtimeConfig: {
    public: {
      apiUrl: process.env.NUXT_PUBLIC_API_URL || 'http://localhost:8000/api',
      apiKey: process.env.NUXT_PUBLIC_API_KEY || '',
    },
  },
})
```

### Design Tokens TailwindCSS

```javascript
// frontend/tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './app/**/*.{vue,js,ts}',
    './components/**/*.{vue,js,ts}',
    './layouts/**/*.{vue,js,ts}',
    './pages/**/*.{vue,js,ts}',
  ],
  theme: {
    extend: {
      colors: {
        'sky-dark': {
          DEFAULT: '#0a0e1a',      // Noir tirant vers le bleu
          50: '#1a1f2e',
          100: '#151a28',
          200: '#10141f',
          300: '#0c1019',
          400: '#080c14',
          500: '#0a0e1a',          // Base
          600: '#060810',
          700: '#04060c',
          800: '#020408',
          900: '#010204',
        },
        'sky-accent': {
          DEFAULT: '#fa784f',      // Orange chaud
          hover: '#fb8c68',
          active: '#f96436',
          50: '#fff4f0',
          100: '#ffe8e0',
          200: '#ffd1c1',
          300: '#ffb9a2',
          400: '#fca283',
          500: '#fa784f',          // Base
          600: '#e86940',
          700: '#d65a31',
          800: '#c44b22',
          900: '#b23c13',
        },
        'sky-text': {
          DEFAULT: '#f5f0e6',      // Blanc cassé tirant vers jaune
          muted: '#b8b3a8',
          50: '#fdfcfa',
          100: '#fbf9f5',
          200: '#f7f3eb',
          300: '#f5f0e6',          // Base
          400: '#e8e3d9',
          500: '#dbd6cc',
          600: '#cec9bf',
          700: '#c1bcb2',
          800: '#b4afa5',
          900: '#a7a298',
        },
      },
      fontFamily: {
        // Police narrative (serif) - pour narrateur, PNJ, dialogues
        'narrative': ['Merriweather', 'Georgia', 'serif'],
        // Police UI (sans-serif) - pour interface, boutons, labels
        'ui': ['Inter', 'system-ui', 'sans-serif'],
      },
    },
  },
  plugins: [],
}
```

### Middleware Laravel VerifyApiKey

```php
<?php
// api/app/Http/Middleware/VerifyApiKey.php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class VerifyApiKey
{
    public function handle(Request $request, Closure $next): Response
    {
        $apiKey = $request->header('X-API-Key');

        if (!$apiKey || $apiKey !== config('app.api_key')) {
            return response()->json([
                'error' => [
                    'code' => 'INVALID_API_KEY',
                    'message' => 'Invalid or missing API key',
                ]
            ], 401);
        }

        return $next($request);
    }
}
```

### Configuration CORS Laravel

```php
<?php
// api/config/cors.php

return [
    'paths' => ['api/*'],
    'allowed_methods' => ['*'],
    'allowed_origins' => explode(',', env('CORS_ALLOWED_ORIGINS', 'http://localhost:3000')),
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => false,
];
```

### Variables d'environnement

**frontend/.env.example:**
```env
# API Configuration
NUXT_PUBLIC_API_URL=http://localhost:8000/api
NUXT_PUBLIC_API_KEY=your-api-key-here

# Site URL (for sitemap, SEO)
NUXT_PUBLIC_SITE_URL=http://localhost:3000
```

**api/.env.example:**
```env
APP_NAME=Skycel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000

# Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=skycel
DB_USERNAME=root
DB_PASSWORD=

# API Security
API_KEY=your-api-key-here

# CORS
CORS_ALLOWED_ORIGINS=http://localhost:3000
```

### Project Structure Notes

- **Nuxt 4** utilise la nouvelle structure `app/` (pas `src/`)
- Les composants dans `app/components/` sont auto-importés
- Les composables dans `app/composables/` sont auto-importés
- Les stores Pinia dans `app/stores/` sont accessibles via auto-import
- Composants client-only: utiliser le suffixe `.client.vue` (pas de SSR)

### References

- [Source: docs/planning-artifacts/architecture.md#Starter-Template-Evaluation]
- [Source: docs/planning-artifacts/architecture.md#Structure-Monorepo]
- [Source: docs/planning-artifacts/architecture.md#Authentication-&-Security]
- [Source: docs/planning-artifacts/ux-design-specification.md#Color-System]
- [Source: docs/planning-artifacts/ux-design-specification.md#Typography-System]
- [Source: docs/planning-artifacts/epics.md#Story-1.1]

### Technical Requirements

| Requirement | Value | Source |
|-------------|-------|--------|
| Nuxt version | 4.x (latest) | Architecture |
| Laravel version | 12.x | Architecture |
| Node.js | 18+ | Nuxt 4 requirement |
| PHP | 8.2+ | Laravel 12 requirement |
| TypeScript | Enabled | Architecture |
| SSR | Enabled | Architecture, NFR5 |

### Libraries to Install

**Frontend (npm):**
| Package | Version | Purpose |
|---------|---------|---------|
| @nuxtjs/i18n | 8.x | Internationalisation |
| @nuxtjs/tailwindcss | 6.x | Styling |
| @pinia/nuxt | 0.5.x | State management |
| @nuxt/image | 1.x | Image optimization |
| @nuxtjs/sitemap | 5.x | SEO sitemap |
| pinia-plugin-persistedstate | 3.x | LocalStorage persistence |

**Backend (composer):**
- Aucun package supplémentaire pour cette story (Laravel de base suffit)

## Dev Agent Record

### Agent Model Used

Claude Opus 4.5 (claude-opus-4-5-20251101)

### Debug Log References

- TailwindCSS: Le module `@nuxtjs/tailwindcss` v6.14 provoquait une erreur PostCSS `Cannot use 'import.meta' outside a module` sur Node.js 18. Résolu en remplaçant le module par une configuration PostCSS directe dans `nuxt.config.ts`.
- PHP: Le PHP en PATH (8.0.3) est incompatible avec Laravel 12. Utilisation de PHP 8.2.29 disponible dans Laragon pour la création du projet et l'exécution.
- pinia-plugin-persistedstate: La v4 requiert pinia 3+, incompatible avec @pinia/nuxt 0.9.0 (pinia 2). Downgrade vers v3.2.

### Completion Notes List

- Structure monorepo `frontend/` + `api/` créée et fonctionnelle
- Nuxt 4 (3.17.5) configuré avec SSR, TypeScript, i18n, Pinia, @nuxt/image, sitemap
- TailwindCSS v3 configuré via PostCSS avec design tokens (sky-dark, sky-accent, sky-text)
- Polices Merriweather (narrative) et Inter (UI) importées via Google Fonts
- Laravel 12.50 installé en mode API-only avec CORS et middleware VerifyApiKey
- Middleware API Key vérifié : 401 sans clé, 200 avec clé valide
- Fichiers .env.example créés pour frontend et backend

### Change Log
| Date | Change | Author |
|------|--------|--------|
| 2026-02-03 | Story créée avec contexte complet | SM Agent |
| 2026-02-05 | Implémentation complète de toutes les tâches (Tasks 1-7) | Dev Agent (Claude Opus 4.5) |

### File List

**Nouveaux fichiers :**
- `README.md` - Documentation racine du monorepo
- `.gitignore` - Gitignore global
- `frontend/package.json` - Dependencies Nuxt 4
- `frontend/nuxt.config.ts` - Configuration Nuxt 4 avec SSR, modules, PostCSS
- `frontend/tsconfig.json` - Config TypeScript
- `frontend/tailwind.config.js` - Design tokens TailwindCSS
- `frontend/app/app.vue` - Composant racine Vue
- `frontend/app/pages/index.vue` - Page d'accueil placeholder
- `frontend/app/assets/css/main.css` - CSS global avec import polices
- `frontend/.env.example` - Variables d'environnement frontend
- `api/` - Projet Laravel 12 complet (via composer create-project)
- `api/app/Http/Middleware/VerifyApiKey.php` - Middleware authentification API Key
- `api/config/cors.php` - Configuration CORS
- `api/routes/api.php` - Routes API avec endpoint /health

**Fichiers modifiés :**
- `api/bootstrap/app.php` - Routing API-only, enregistrement middleware VerifyApiKey
- `api/config/app.php` - Ajout config api_key
- `api/.env.example` - Ajout APP_NAME=Skycel, DB config MySQL, API_KEY, CORS_ALLOWED_ORIGINS
- `api/.env` - Mêmes ajouts que .env.example avec valeurs dev
- `api/routes/web.php` - Vidé (mode API-only)
