CS.03, CASE STUDY · CÔTÉ SYSTÈME
Le Boudoir d'Alice
Freelance · en production
voir le site en ligne↗CONTEXTE
Marmiton, Jow ou Yuka ne savent rien de vos pathologies. Hypertension (17 M de Français), diabète (3,8 M traités), insuffisance rénale, SOPK : cuisiner avec une maladie chronique reste un casse-tête quotidien. Et un créneau vide en francophonie.
MON RÔLE
Le projet le plus complet du portfolio : positionnement, identité visuelle, UX, développement full-stack et mise en production, seul de bout en bout.
RÉALISATIONS
- Base CIQUAL 2025 de l'ANSES intégrée : 3 484 aliments, ~74 composants nutritionnels chacun, import XML officiel
- 19 pathologies avec restrictions sourcées (HAS, ANSES, sociétés savantes) et moteur de compatibilité par seuils
- Fonctionnalités complètes : frigo virtuel, batch cooking, planning de repas, listes de courses, IA culinaire avec OCR
- Laravel 12 + React 19/Inertia SSR : 52 modèles, 68 migrations, 83 pages, Meilisearch, monitoring Sentry
CHIFFRES
CIQUAL : 3 484 aliments · 74 nutriments · 19 pathologies sourcées · 52 modèles · 83 pages · OCR vision
OUTILS
- Laravel 12
- PHP 8.2
- React 19
- Inertia v2
- Tailwind 4
- Meilisearch
- Sentry
- Docker
CS.03.M, LES MODULES, PIÈCE PAR PIÈCE
Sous le capot
01 · La base santé : CIQUAL 2025
Le socle du projet est la table de composition nutritionnelle officielle française, publiée par l'ANSES : 3 484 aliments décrits par une soixantaine de colonnes (énergie, macronutriments, 10 minéraux, 13 vitamines, acides gras détaillés). L'import parse les XML officiels, et un service de correspondance floue relie chaque ingrédient de recette à son aliment CIQUAL, avec un taux de couverture calculé par recette.

1 / 2$pathology->nutrientRestrictions()->createMany([
[
'nutrient_code' => 'sugars',
'nutrient_name_fr' => 'Sucres',
'restriction_type' => 'limit',
'max_daily' => 25,
'unit' => 'g',
'advice_fr' => 'Éviter les sucres ajoutés, sodas, jus de fruits, pâtisseries',
'severity' => 'danger',
'display_order' => 1,
],
[
'nutrient_code' => 'fiber',
'nutrient_name_fr' => 'Fibres',
'restriction_type' => 'increase',
'min_daily' => 30,
'unit' => 'g',
'advice_fr' => 'Augmenter les légumes, légumineuses, céréales complètes',
'severity' => 'info',
'display_order' => 2,
],
]);02 · Le moteur de compatibilité
Pour chaque recette et chaque pathologie du profil, le moteur évalue les valeurs nutritionnelles par portion contre des seuils typés (limiter, éviter, augmenter, surveiller) et gradue la sévérité. Une portion qui dépasse la moitié de l'apport journalier maximal déclenche l'alerte, le dépassement complet passe au rouge. En sortie : un feu tricolore et des messages explicites.
case 'limit':
// Vérifier par portion d'abord
if ($restriction->max_per_portion !== null && $perServingValue > $restriction->max_per_portion) {
return [
'message' => "{$nutrientName}: {$this->formatValue($perServingValue)}{$unit}/portion",
'severity' => $restriction->severity,
'value' => $perServingValue,
'threshold' => $restriction->max_per_portion,
];
}
// Vérifier si la recette entière dépasse la limite journalière
if ($restriction->max_daily !== null) {
$percentDaily = ($perServingValue / $restriction->max_daily) * 100;
if ($percentDaily > 50) {
$severityLevel = $percentDaily > 100 ? 'danger'
: ($percentDaily > 75 ? 'warning' : 'info');
return [
'message' => "{$nutrientName}: {$this->formatValue($percentDaily)}% de l'apport max quotidien",
'severity' => $severityLevel,
'value' => $perServingValue,
'threshold' => $restriction->max_daily,
];
}
}
break;03 · L'assistant IA et l'OCR
Trois fournisseurs de modèles en cascade avec quotas par utilisateur, prompts versionnés en base. La saisie d'une recette peut partir d'une simple photo : un modèle de vision extrait ingrédients et étapes, prêts à éditer. Génération d'images de recettes intégrée au même pipeline.
$payload = [
'model' => 'grok-2-vision-1212',
'max_tokens' => 4096,
'temperature' => 0.1,
'messages' => [
[
'role' => 'user',
'content' => [
[
'type' => 'image_url',
'image_url' => ['url' => $imageUrl],
],
[
'type' => 'text',
'text' => $prompt,
],
],
],
],
];
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $this->apiKey,
'Content-Type' => 'application/json',
])->timeout(60)->post("{$this->apiUrl}/chat/completions", $payload);04 · Un produit complet, étudié comme tel
Au-delà du code : frigo virtuel multi-frigos avec péremption, batch cooking en kanban, planning hebdomadaire, listes de courses générées, prix collaboratifs validés par votes, profils sociaux. Et un audit produit complet (marché, concurrence, SEO, monétisation) qui a établi le positionnement : personne en francophonie ne croise multi-pathologies et base officielle, et le persona le plus solvable est l'aidant qui cuisine pour un proche malade.
LA FACE VISIBLE, CÔTÉ STUDIO