Web App mobile-responsive de découverte urbaine via des parcours de street art et anecdotes culturelles basés sur la géolocalisation.
URL publiée : https://boteropopcity.base44.app Date du document : 11 septembre 2026
Botero PoP City propose des parcours culturels guidés par GPS à travers le street art et l'histoire urbaine. L'utilisateur :
Monétisation : achat au parcours, codes promotionnels (liés optionnellement à un partenaire), gratuité possible (prix 0 ou promo 100%).
CRM séparé : l'espace d'administration gère les partenaires (personnes morales), codes promo, contenu éditorial et reporting.
| Couche | Technologie | |---|---| | Frontend | React + Vite + Tailwind CSS + shadcn/ui | | Cartographie | react-leaflet (OpenStreetMap) | | Backend (BaaS) | Base44 — entités, fonctions, auth, intégrations Core | | Paiement | Stripe (Live Mode) — Checkout Sessions + Webhook | | Auth | Base44 Auth (email/password, Google, Apple optionnelle) | | i18n | Système maison (FR natif + traductions IA EN/ES/IT/DE/NL) | | Hébergement | Base44 (https://boteropopcity.base44.app) |
Structure des dossiers clés :
src/pages/ — Pages de l'app (Home, RouteDetail, RoutePlay, Account, Admin, Welcome, etc.)src/components/ — Composants réutilisables + composants admin + ui (shadcn)src/lib/ — i18n, AuthContext, utils, query-clientbase44/entities/ — Schémas JSON des entitésbase44/functions/ — Fonctions backend (createCheckout, stripeWebhook, sendContactEmail, translateContent)base44/shared/ — Logique partagée (geo.ts : calcul Haversine)Toutes les entités héritent automatiquement de :
id, created_date, updated_date, created_by_id.
| Champ | Type | Rôle | |---|---|---| |
title | string (requis) | Nom du parcours (non traduit) |
| description | string | Description (traduite) |
| city | string (requis) | Ville de rattachement |
| price | number (requis) | Prix en EUR (0 = gratuit) |
| image_url | string | Image de présentation |
| duration_minutes | number | Durée estimée |
| distance_km | number | Distance estimée |
| display_order | number | Ordre d'affichage (défaut 0) |
| published | boolean | Visible côté client (défaut false) |
| coming_soon | boolean | Badge "Bientôt disponible" |
| completion_text | string | Texte affiché à la fin du parcours (traduit) |
| completion_image_url | string | Image de fin de parcours |
| instagram_image_url | string | Image du pop-up Instagram de fin |
| translations | object | Traductions IA ({ en: {...}, es: {...}, ... }) |
| Champ | Type | Rôle | |---|---|---| |
route_id | string (requis) | Référence vers Route |
| step_order | number (requis) | Ordre dans le parcours |
| title | string (requis) | Titre (non traduit) |
| description | string | Description courte affichée pendant le parcours (traduite, respecte les sauts de ligne) |
| latitude | number (requis) | Coordonnée GPS |
| longitude | number (requis) | Coordonnée GPS |
| validation_radius | number | Rayon de validation en mètres (défaut 50) |
| content | string | Contenu culturel débloqué à la validation (traduit, respecte les sauts de ligne) |
| image_url | string | Image de présentation de l'étape |
| validation_image_url | string | Image affichée dans le pop-up de validation |
| published | boolean | "À utiliser" — masque/affiche l'étape côté client (défaut true) |
| translations | object | Traductions IA |
| Champ | Type | Rôle | |---|---|---| |
user_id | string (requis) | Utilisateur acheteur |
| route_id | string (requis) | Parcours acheté |
| promo_code_id | string | Code promo utilisé (référence) |
| promo_code | string | Code promo utilisé (valeur lisible) |
| partner_id | string | Partenaire lié (via le code promo) |
| original_price | number | Prix avant remise |
| price_paid | number (requis) | Prix réellement payé |
| status | enum | pending / completed (défaut completed) |
| Champ | Type | Rôle | |---|---|---| |
user_id | string (requis) | Utilisateur |
| route_id | string (requis) | Parcours |
| current_step | number | Index de l'étape en cours (défaut 0) |
| completed_steps | number[] | Index des étapes validées |
| manual_steps | number[] | Étapes validées manuellement |
| validations | object[] | Détail par validation : { step, method ('manual'|'gps'), date } |
| started_date | datetime | Date de début |
| completed_date | datetime | Date de fin (si terminé) |
| status | enum | in_progress / completed (défaut in_progress) |
| Champ | Type | Rôle | |---|---|---| |
code | string (requis) | Le code saisi par l'utilisateur (non traduit) |
| discount_percent | number (requis) | Pourcentage de réduction |
| valid_until | date | Date d'expiration (optionnelle) |
| max_uses | number | Nombre max d'utilisations (optionnel) |
| used_count | number | Compteur d'utilisation (défaut 0) |
| active | boolean | Actif (défaut true) |
| partner_id | string | Partenaire lié (optionnel) |
| route_id | string | Parcours spécifique (optionnel — sinon tous parcours) |
| Champ | Type | Rôle | |---|---|---| |
name | string (requis) | Raison sociale (non traduit) |
| legal_form | enum | SAS, SARL, SASU, EURL, SA, SCI, Association, Auto-entrepreneur, Autre |
| siret | string | Numéro SIRET |
| contact_name | string | Nom du contact |
| email | string | Email |
| phone | string | Téléphone |
| address | string | Adresse |
| postal_code | string | Code postal |
| city | string | Ville |
| image_url | string | Logo / image |
| notes | string | Notes internes |
| active | boolean | Actif (défaut true) |
Entité singleton (premier enregistrement utilisé). Gère : bannière hero, page de présentation (welcome), consignes de sécurité, footer, introduction/clôture FAQ, introduction contact, email + objet par défaut du formulaire de contact. Tous les champs textuels sont traduits via
translations.
| Champ | Type | Rôle | |---|---|---| |
slug | string (requis) | Identifiant d'URL (ex : cgv) |
| title | string (requis) | Titre (non traduit) |
| content | string | Contenu HTML (traduit) |
| image_url | string | Image |
| visible | boolean | Visible (défaut true) |
| translations | object | Traductions IA |
| Champ | Type | Rôle | |---|---|---| |
question | string (requis) | Question (traduite) |
| answer | string (requis) | Réponse (traduite) |
| display_order | number | Ordre d'affichage |
| visible | boolean | Visible (défaut true) |
| translations | object | Traductions IA |
| Champ | Type | Rôle | |---|---|---| |
name | string (requis) | Nom du client (non traduit) |
| city | string | Ville |
| rating | number | Note 1-5 (défaut 5) |
| comment | string (requis) | Témoignage (traduit) |
| avatar_url | string | Avatar / photo |
| visible | boolean | Visible (défaut true) |
| translations | object | Traductions IA |
Entité native Base44, non créée manuellement. Champs :
id, email, full_name, role (admin / user). Les utilisateurs rejoignent l'app via invitation (base44.users.inviteUser) ou inscription.
Rôle : Crée une session de paiement Stripe (ou accorde l'accès gratuit). Entrées : { route_id, user_id, promo_code } Logique :
URL : https://boteropopcity.base44.app/functions/createCheckout
Rôle : Reçoit les événements Stripe (paiement complété). Déclenchement : Webhook Stripe → checkout.session.completed. Logique :
URL : https://boteropopcity.base44.app/functions/stripeWebhook
Rôle : Envoie un email depuis le formulaire de contact. Entrées : { name, email, message } Logique : Valide les champs → récupère le destinataire et l'objet depuis SiteContent → envoie via Core.SendEmail.
Rôle : Traduit des textes via IA (InvokeLLM) vers EN/ES/IT/DE/NL. Sécurité : Réservé aux admins (user.role === 'admin'). Entrées : { texts: { fieldKey: value, ... } } Logique : Prompt IA avec règles de traduction (pas de noms propres, conservation HTML) → parse le JSON retourné → retourne { translations: { en, es, it, de, nl } }.
| Intégration | Usage | |---|---| | InvokeLLM | Traduction automatique (translateContent) | | SendEmail | Formulaire de contact (sendContactEmail) | | UploadPublicFile | Uploads d'images côté client (formulaires admin) |
| Type de contenu | Traduit ? | |---|---| | Titres (parcours, étapes) | Non | | Descriptions | Oui | | Contenus culturels | Oui | | FAQ (questions + réponses) | Oui | | Avis (commentaires) | Oui | | Pages éditoriales (contenu) | Oui | | Contenu éditorial global (SiteContent) | Oui | | Codes promo | Non | | Noms de partenaires | Non | | Noms de clients (avis) | Non | | Noms de marques / villes / personnes | Non |
| Route | Accès | |---|---| | /welcome, /avis, /faq, /contact, /page/:slug | Public (sans auth) | | /login, /register, /forgot-password, /reset-password | Public | | / (Home), /route/:id, /play/:id, /account | Authentifié (ProtectedRoute) | | /admin | Admin uniquement (AdminRoute — role === 'admin') |
| Page | Route | Rôle | |---|---|---| | Welcome | /welcome | Page de présentation + connexion/inscription | | Home | / | Catalogue des parcours (filtre par ville) | | RouteDetail | /route/:id | Détails parcours + achat + code promo | | RoutePlay | /play/:id | Parcours en cours (carte + GPS + validation) | | Account | /account | Espace client (profil + progression) | | Admin | /admin | Back-office (CRUD + stats) | | Reviews | /avis | Avis clients | | Faq | /faq | FAQ | | Contact | /contact | Formulaire de contact | | SitePageView | /page/:slug | Page éditoriale (CGV, etc.) | | Login / Register / ForgotPassword / ResetPassword | — | Auth |
| Composant | Rôle | |---|---| | Layout | Layout principal (navbar + footer + Outlet) | | ProtectedRoute | Garde d'auth (redirige vers /welcome si non connecté) | | AdminRoute | Garde admin (redirige si non admin) | | RouteCard | Carte parcours (accueil) | | RouteCarousel | Carrousel parcours (page Welcome) | | RouteProgressDetail | Détail de progression (espace client) | | LanguageSwitcher | Sélecteur de langue | | Footer | Footer (gris anthracite, texte blanc, liens éditoriaux + Instagram) | | ImageUpload | Upload d'images (formulaires admin) | | TranslateButton | Bouton de génération manuelle des traductions IA |
RouteForm, StepForm, PromoForm, HeroBannerForm, SitePageForm, ReviewForm, FaqForm, PartnerForm, RouteStats, PromoStats — chacun déclenche autoTranslate à la sauvegarde.
| Limite | Détail | |---|---| | GPS en iframe | Ne fonctionne pas dans l'aperçu du builder — tester sur smartphone via l'URL publiée | | Connexion Apple | Nécessite une activation manuelle via le Dashboard Base44 (Settings → Authentication) | | Icône d'accès admin | Actuellement visible pour tous les utilisateurs | | Multilinguisme IA | Les traductions sont générées par IA — peuvent nécessiter une relecture pour les contenus culturels sensibles |
Document généré le 11 septembre 2026 — reflète l'état actuel du code source.