# TERRE FORTE — Référence de l'API REST

API publique consommée par le frontend \(phase 1\) et administrée via le back-office Filament. Base URL de production : `https://api.terreforte.gn/api`. Toutes les réponses sont en JSON UTF-8.

---

## 1. Conventions

**Format d'enveloppe.** Les réponses renvoient soit une ressource unique, soit une collection.

- Ressource unique : `{ "data": { ... } }`

- Collection paginée : `{ "data": [ ... ], "meta": { ... }, "links": { ... } }`

**Nommage.** Colonnes de base en `snake_case` \(`short_description`\), mais **clés JSON en camelCase** \(`shortDescription`\), pour rester directement compatibles avec le frontend de la phase 1. La conversion est faite par les *API Resources* Laravel.

**Pagination.** Paramètres `page` \(défaut `1`\) et `per_page` \(défaut `12`, maximum `50`\). Objet `meta` renvoyé :

```json
{
  "meta": {
    "current_page": 1,
    "per_page": 12,
    "total": 24,
    "last_page": 2,
    "from": 1,
    "to": 12
  }
}
```

**Erreurs.** Format uniforme.

| Code | Signification | Corps |
| --- | --- | --- |
| `200` | Succès | ressource ou collection |
| `201` | Création réussie \(formulaires\) | `{ "message": "...", "data": {...} }` |
| `404` | Ressource introuvable | `{ "message": "Ressource introuvable." }` |
| `422` | Validation échouée | `{ "message": "...", "errors": { "champ": ["message"] } }` |
| `429` | Trop de requêtes | `{ "message": "Trop de tentatives." }` avec en-tête `Retry-After` |

**Authentification.** Les routes publiques \(`GET`\) et les deux formulaires \(`POST /contact`, `POST /quotes`\) sont **ouverts**. L'administration se fait via Filament \(session\) sur `/admin`, donc l'API publique n'exige aucun jeton. Des routes protégées par **Laravel Sanctum** \(jeton Bearer\) sont fournies en option pour une future app mobile ou un tableau de bord externe \(section 4\).

**Throttling.** `POST /api/contact` et `POST /api/quotes` sont limités à **10 requêtes/minute par adresse IP** \(protection anti-spam\).

---

## 2. Index des endpoints

| Méthode | Endpoint | Description |
| --- | --- | --- |
| GET | `/api/settings` | Paramètres publics du site |
| GET | `/api/activities` | Les 5 pôles d'activité |
| GET | `/api/activities/{slug}` | Détail d'un pôle |
| GET | `/api/product-categories` | Catégories produits |
| GET | `/api/products` | Catalogue produits \(recherche/filtres/tri/pagination\) |
| GET | `/api/products/{slug}` | Fiche produit |
| GET | `/api/project-categories` | Catégories de réalisations |
| GET | `/api/projects` | Réalisations \(filtres/pagination\) |
| GET | `/api/projects/{slug}` | Détail d'une réalisation |
| GET | `/api/news-categories` | Catégories d'actualités |
| GET | `/api/news` | Actualités \(filtres/pagination\) |
| GET | `/api/news/{slug}` | Article détaillé |
| GET | `/api/media-categories` | Catégories de médias |
| GET | `/api/media` | Galerie médias \(type/catégorie\) |
| GET | `/api/partners` | Partenaires \(filtre par type\) |
| GET | `/api/pages/{slug}` | Page légale/éditoriale |
| GET | `/api/search` | Recherche globale groupée |
| POST | `/api/contact` | Envoi d'un message de contact |
| POST | `/api/quotes` | Envoi d'une demande de devis |

---

## 3. Contenu public

### 3.1 `GET /api/settings`

Renvoie les paramètres généraux \(source unique de vérité pour les coordonnées côté frontend\).

```json
{
  "data": {
    "siteName": "TERRE FORTE SARLU",
    "baseline": "Une solution globale",
    "address": "Comboyah, Commune de Forécariah, Conakry, Guinée",
    "phones": ["+224 624 53 63 61", "+224 611 84 82 94"],
    "whatsapp": "224624536361",
    "email": "contact@terreforte.gn",
    "socials": { "facebook": "https://facebook.com/...", "linkedin": "https://linkedin.com/company/..." },
    "mapsEmbed": "<iframe …>",
    "openHours": "Lun–Sam · 08h00–18h00"
  }
}
```

### 3.2 `GET /api/activities` — les 5 pôles

Aucun paramètre. Renvoie les activités publiées, triées par `position`.

```json
{
  "data": [
    {
      "id": 1,
      "slug": "agro-industrie-elevage",
      "title": "Agro-industrie & Élevage",
      "icon": "🌾",
      "image": "https://api.terreforte.gn/storage/activities/agro.jpg",
      "shortDescription": "Production agricole, agrobusiness et élevage…",
      "subActivities": ["Production agricole", "Agrobusiness", "Élevage", "Huile de palme guinéenne"],
      "stats": [{ "v": "100%", "l": "Production locale" }],
      "featured": true
    }
  ]
}
```

### 3.3 `GET /api/activities/{slug}`

Renvoie une activité unique enrichie \(`description` : tableau de paragraphes, `subActivities`, `stats`\). `404` si le slug est inconnu ou l'élément non publié.

### 3.4 `GET /api/product-categories`

Renvoie les catégories avec leur `slug`, `name`, `image` et le nombre de produits publiés \(`productsCount`\). Exemple : Agriculture, Élevage, EPI/EPC, Vêtements de travail, Commerce, Autres produits.

### 3.5 `GET /api/products`

Catalogue paginé.

| Paramètre | Type | Défaut | Description |
| --- | --- | --- | --- |
| `q` | string | — | Recherche sur `name`, `short_description`, `category` |
| `category` | string | — | Slug de catégorie |
| `availability` | enum | — | `disponible` \| `sur commande` \| `rupture` |
| `sort` | enum | `recent` | `recent`, `az`, `za`, `dispo` |
| `featured` | bool | — | `1` pour ne renvoyer que les produits mis en avant |
| `page` | int | `1` | Page courante |
| `per_page` | int | `12` | Taille de page \(max `50`\) |

```json
{
  "data": [
    {
      "id": 1,
      "slug": "huile-de-palme-guineenne",
      "name": "Huile de palme guinéenne",
      "category": { "slug": "agriculture", "name": "Agriculture" },
      "image": "https://api.terreforte.gn/storage/products/huile.jpg",
      "shortDescription": "Huile rouge 100% naturelle, pressée localement…",
      "availability": "disponible",
      "unit": "Bidon 5 L / 20 L",
      "priceLabel": "Sur devis",
      "featured": true
    }
  ],
  "meta": { "current_page": 1, "per_page": 12, "total": 24, "last_page": 2 }
}
```

### 3.6 `GET /api/products/{slug}`

Fiche complète : ajoute `gallery` \(tableau d'URL\), `description`, `features` \(tableau\), `subcategory`, `reference`.

### 3.7 `GET /api/project-categories`

Catégories de réalisations : Agriculture, BTP, Fourniture, Événements, Formation, Logistique, Partenariats \(`slug`, `name`\).

### 3.8 `GET /api/projects`

| Paramètre | Type | Défaut | Description |
| --- | --- | --- | --- |
| `q` | string | — | Recherche sur `title`, `short_description` |
| `category` | string | — | Slug de catégorie |
| `featured` | bool | — | Mises en avant uniquement |
| `page` / `per_page` | int | `1` / `12` | Pagination |

Réponse : `id`, `slug`, `title`, `category`, `cover`, `date` \(ISO `YYYY-MM-DD`\), `location`, `shortDescription`, `featured`.

### 3.9 `GET /api/projects/{slug}`

Ajoute `gallery`, `description`, `client`, `services`.

### 3.10 `GET /api/news`

| Paramètre | Type | Défaut | Description |
| --- | --- | --- | --- |
| `q` | string | — | Recherche sur `title`, `excerpt` |
| `category` | string | — | Slug de catégorie |
| `featured` | bool | — | À la une |
| `page` / `per_page` | int | `1` / `9` | Pagination |

Réponse : `id`, `slug`, `title`, `category`, `cover`, `excerpt`, `author`, `readingTime`, `publishedAt` \(ISO `YYYY-MM-DD`\).

### 3.11 `GET /api/news/{slug}`

Ajoute `content` \(texte long/Markdown\).

### 3.12 `GET /api/media`

| Paramètre | Type | Défaut | Description |
| --- | --- | --- | --- |
| `type` | enum | — | `photo` \| `video` |
| `category` | string | — | Slug de catégorie |

Réponse : `id`, `type`, `category`, `title`, `src`, `thumb`, `duration`.

### 3.13 `GET /api/partners`

| Paramètre | Type | Description |
| --- | --- | --- |
| `type` | enum | `Partenaire` \| `Fournisseur` \| `Client institutionnel` \| `Organisation` |

Réponse : `id`, `name`, `logo`, `type`, `website`.

### 3.14 `GET /api/pages/{slug}`

Pages éditoriales \(`mentions-legales`, `politique-de-confidentialite`\). Réponse : `slug`, `title`, `content`, `metaTitle`, `metaDescription`.

### 3.15 `GET /api/search`

| Paramètre | Type | Description |
| --- | --- | --- |
| `q` | string | **requis**, min. 2 caractères |

```json
{
  "data": {
    "activities": [ { "slug": "…", "title": "…" } ],
    "products":   [ { "slug": "…", "name": "…", "category": "…" } ],
    "projects":   [ { "slug": "…", "title": "…" } ],
    "news":       [ { "slug": "…", "title": "…" } ],
    "media":      [ { "id": 3, "title": "…" } ],
    "pages":      [ { "slug": "…", "title": "…" } ]
  }
}
```

Chaque groupe est limité aux 5 premiers résultats. Si `q` est absent ou trop court, renvoie `422`.

---

## 4. Authentification \(optionnelle — Sanctum\)

Fournie pour de futurs clients API \(mobile, tableau de bord externe\). Le back-office, lui, utilise la session Filament.

| Méthode | Endpoint | Corps | Réponse |
| --- | --- | --- | --- |
| POST | `/api/auth/login` | `{ "username", "password" }` | `{ "token": "…", "user": {…} }` |
| POST | `/api/auth/logout` | — \(Bearer\) | `{ "message": "Déconnecté." }` |
| GET | `/api/auth/me` | — \(Bearer\) | `{ "data": { …user } }` |

Jeton à transmettre : en-tête `Authorization: Bearer <token>.

---

## 5. Formulaires publics

### 5.1 `POST /api/contact`

`Content-Type: application/json`.

| Champ | Règle |
| --- | --- |
| `name` | requis, string, max 255 |
| `email` | requis, e-mail valide |
| `phone` | optionnel, string, max 50 |
| `subject` | optionnel, string, max 255 |
| `message` | requis, string, max 5000 |

Réponse `201` :

```json
{ "message": "Merci, votre message a bien été envoyé.", "data": { "id": 42 } }
```

### 5.2 `POST /api/quotes`

`Content-Type: multipart/form-data` \(car pièce jointe possible\).

| Champ | Règle |
| --- | --- |
| `firstName` | requis, string, max 255 |
| `lastName` | requis, string, max 255 |
| `company` | optionnel, string, max 255 |
| `phone` | requis, string, max 50 |
| `email` | requis, e-mail valide |
| `domain` | optionnel, string, max 255 \(domaine d'activité\) |
| `productService` | optionnel, string, max 255 |
| `quantity` | optionnel, string, max 255 |
| `budget` | optionnel, string, max 255 |
| `message` | optionnel, string, max 5000 |
| `attachment` | optionnel, fichier, max **5 Mo**, types `pdf,doc,docx,xls,xlsx,jpg,jpeg,png,zip` |

Réponse `201` :

```json
{ "message": "Votre demande de devis a été envoyée. Nous vous recontactons rapidement.", "data": { "id": 17 } }
```

**Erreur de validation \(exemple **<strong>`422`</strong>**\)** :

```json
{
  "message": "Les données transmises sont invalides.",
  "errors": {
    "email": ["Le champ email doit être une adresse e-mail valide."],
    "attachment": ["Le fichier ne doit pas dépasser 5 Mo."]
  }
}
```

---

## 6. Exemple d'appel depuis le frontend

```javascript
const API = 'https://api.terreforte.gn/api';

// Catalogue filtré
const res = await fetch(`${API}/products?category=agriculture&sort=az&page=1`);
const { data, meta } = await res.json();

// Demande de devis (multipart)
const fd = new FormData();
fd.append('firstName', 'Aïcha');
fd.append('lastName', 'Camara');
fd.append('phone', '+224 624 53 63 61');
fd.append('email', 'aicha@example.com');
fd.append('productService', 'Huile de palme guinéenne');
fd.append('attachment', fileInput.files[0]);
const r = await fetch(`${API}/quotes`, { method: 'POST', body: fd });
```

