# 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 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 ['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)