# Conventions du projet

## Nommage

### Types de blocs
- Format : **kebab-case**
- Exemples : `hero`, `text`, `property-list`, `booking-form`
- Identique dans schema.js, index.js, et en BDD

### Composants Vue
- Format : **PascalCase**
- Exemples : `HeroBlock`, `PropertyList`, `MenuEditor`, `PageBuilder`

### Fichiers Vue
- Composants : `PascalCase.vue` → `NavMenu.vue`, `BlockPanel.vue`
- Pages Inertia : `PascalCase.vue` → `Dashboard.vue`, `Edit.vue`

### Controllers Laravel
- Format : **PascalCase + Controller**
- Exemples : `PageController`, `MenuController`
- Toujours dans `Admin/` ou `Front/` selon l'espace

### Services Laravel
- Format : **PascalCase + Service**
- Exemples : `PageService`, `MenuService`

### Tables BDD
- Format : **snake_case pluriel**
- Exemples : `menu_items`, `page_blocks`, `menu_locations`

### Clés de réglages
- Format : **dot.notation**
- Exemples : `site.name`, `site.primary_color`, `contact.email`

### Clés de cache Redis
- Format : `{entité}.{identifiant}` ou `{entité}.all`
- Exemples : `page.about`, `menus.all`, `settings.shared`

---

## PHP / Laravel

```php
// Controller : mince — valide, appelle service, retourne
public function update(UpdatePageRequest $request, Page $page): Response
{
    $this->pageService->update($page, $request->validated());
    return Inertia::render('Admin/Pages/Edit', ['page' => $page->fresh()]);
}

// Service : contient la logique
public function update(Page $page, array $data): Page
{
    $page->update($data);
    cache()->forget("page.{$page->slug}");
    return $page;
}

// Toujours typer les paramètres et retours
// Toujours utiliser les Form Requests pour la validation
// Jamais de logique dans un Controller
// Jamais de requêtes SQL dans un Controller
```

---

## Vue / JavaScript

```vue
<script setup>
// Toujours Composition API avec <script setup>
// Jamais d'Options API

import { ref, computed } from 'vue'
import { usePage } from '@inertiajs/vue3'

// Props typées
const props = defineProps({
  page: { type: Object, required: true },
  blocks: { type: Object, default: () => ({}) },
})

// Emit typé
const emit = defineEmits(['update:blocks'])

// Données réactives
const isLoading = ref(false)

// Computed
const contentBlocks = computed(() => props.blocks['content'] ?? [])
</script>
```

**Règles Vue :**
- Toujours `<script setup>` — jamais Options API
- Toujours typer les props (type + required ou default)
- Imports nommés depuis vue : `import { ref, computed } from 'vue'`
- Accès aux données globales Inertia : `usePage().props`
- Pas de `this` — Composition API uniquement

---

## CSS

```vue
<style scoped>
/* Toujours scoped dans les composants de blocs */
/* Pas de classes globales dans les blocs */

.hero-block {
  /* Variables CSS pour les couleurs du thème */
  background-color: var(--site-primary-color, #3B82F6);
}
</style>
```

**Règles CSS :**
- Blocs front-office : toujours `scoped`
- Utiliser les variables CSS pour les couleurs du thème (`--site-primary-color`, etc.)
- Back-office : utiliser les classes Quasar natives autant que possible

---

## Cache

```php
// Durée standard : 3600 secondes (1h) pour les données CMS
// Invalidation immédiate après toute mutation

// Pattern standard dans un Service :
public function getBySlug(string $slug): Page
{
    return cache()->remember("page.{$slug}", 3600, fn() =>
        Page::with(['blocks', 'template'])->whereSlug($slug)->firstOrFail()
    );
}

public function update(Page $page, array $data): void
{
    $page->update($data);
    cache()->forget("page.{$page->slug}");
}

// Menus et settings : invalidation globale
cache()->forget('menus.all');
cache()->forget('settings.shared');
```

---

## Routes

```php
// routes/web.php — Front-office
Route::get('/', [Front\HomeController::class, 'index'])->name('home');
Route::get('/{slug}', [Front\PageController::class, 'show'])->name('page.show');

// routes/admin.php — Back-office
Route::prefix('admin')->name('admin.')->middleware(['auth', 'admin'])->group(function () {
    Route::get('/', [Admin\DashboardController::class, 'index'])->name('dashboard');
    Route::resource('pages', Admin\PageController::class);
    Route::put('pages/{page}/blocks', [Admin\BlockController::class, 'sync'])->name('pages.blocks.sync');
    Route::resource('menus', Admin\MenuController::class);
    Route::post('menus/{menu}/items/reorder', [Admin\MenuController::class, 'reorder'])->name('menus.items.reorder');
    Route::get('media', [Admin\MediaController::class, 'index'])->name('media.index');
    Route::post('media', [Admin\MediaController::class, 'store'])->name('media.store');
    Route::delete('media/{media}', [Admin\MediaController::class, 'destroy'])->name('media.destroy');
    Route::get('settings', [Admin\SettingsController::class, 'index'])->name('settings.index');
    Route::put('settings', [Admin\SettingsController::class, 'update'])->name('settings.update');
});
```

**Règles routes :**
- Toujours nommer les routes
- Admin : préfixe `admin.` sur tous les noms
- Utiliser `resource()` pour les CRUD standards
- Routes spéciales (sync, reorder) en dehors du resource
