Produit StyloTech · Réf. SP-02

Lokal

Trouvez ce qu’il vous faut près de chez vous, et sachez qui est ouvert maintenant.

Plateformes

Web · iOS · Android

  • L’accueil de Lokal  « Tout ce qu’il vous faut à Maurice , un champ pour un produit ou un service et un autre pour une ville, les filtres « Près de moi  et « Peut me servir maintenant , et 30 commerces locaux trouvés.
    Recherche
  • Les commerces proches sur une carte de Maurice, une vingtaine de commerces épinglés, avec les commandes « Peut me servir maintenant  et « Centrer sur moi .
    Carte
  • Le profil de Boutik Ramdhun, une épicerie de Beau Bassin  marquée ouverte, notée 4,5 sur 12 avis, avec sa description et un bouton pour écrire au commerce.
    Profil commerce
  • Le tableau de bord vendeur  une carte de bienvenue avec la fiche active, un lien vers le profil public, les réglages de disponibilité — ouvert, fermé, ferme bientôt, fermé temporairement — et des commandes et messages en attente.
    Tableau de bord vendeur
Écrans de l’application Flutter sur un émulateur Android, alimentée par le jeu de démonstration du projet. Commerces et vendeurs inventés  jamais de données de production.

À Maurice, « qui a ça, et est-ce ouvert   se règle d’habitude par un coup de fil, une page sur les réseaux que personne ne met à jour, ou un trajet pour aller voir. Les petits commerces ont le stock et les horaires  ce qui leur manque, c’est un endroit pour le dire là où les gens cherchent vraiment. Lokal est cet endroit — et ne touche jamais à l’argent entre un client et un commerce.

Le rôle de StyloTech

Lokal est le produit de StyloTech. Nous le définissons, le concevons, le développons, l’exploitons et le supportons — le client web React, l’application Flutter pour iOS et Android, le schéma Supabase et sa sécurité au niveau des lignes, les Edge Functions et le déploiement.

Lokal facture aux commerces un abonnement mensuel fixe, sans commission  il doit donc coûter peu à exploiter et être difficile à détourner. Les autorisations vivent dans Postgres plutôt que dans les écrans, et les deux clients obéissent aux mêmes règles.

Bientôt sur les stores
  • App Store
  • Google Play

Aucune des deux fiches n’est encore publiée. L’application est développée et tourne sur les deux plateformes, et la version web est déjà en ligne.

Site du produitlokal.stylotech.mu
Plateformes
  • Web
  • iOS
  • Android
Construit avec
Client web
  • React 19
  • TypeScript
  • Vite
  • Tailwind CSS
  • TanStack Query
  • Zustand
Client mobile
  • Flutter
  • BLoC
  • go_router
  • get_it
  • flutter_map
Backend
  • Supabase
  • PostgreSQL
  • Row Level Security
  • Edge Functions
  • pg_cron
Infrastructure
  • Supabase Realtime
  • Firebase Cloud Messaging
  • OpenStreetMap
  • nginx
Qualité & livraison
  • Vitest
  • oxlint
  • GitHub Actions
Ce que StyloTech a livré
  • Stratégie produit
  • Design UI/UX
  • Design system
  • Architecture technique
  • Développement frontend
  • Développement backend
  • Développement mobile
  • Conception de la base de données
  • Intégrations
  • Tests & assurance qualité
  • Mise en production
  • Maintenance

Ce que ça fait

Chaque élément répond à une tâche réelle le jour de l’épreuve, pas à une liste de fonctionnalités. Le site du produit les détaille un par un.

01

Chercher ce dont on a besoin

Une recherche « brochette  ou « mécanicien  trouve ce que les commerces vendent, pas seulement leur nom — pondérée dans Postgres entre noms, étiquettes, descriptions et produits, et tenue à jour par des triggers.

02

Qui peut vous servir maintenant

Ouvert, fermé, ferme bientôt, occupé, livraison possible. Un statut posé par le vendeur expire au bout de 12 heures et laisse place à ses horaires, toujours lus à l’heure de Maurice plutôt qu’à celle de l’appareil.

03

Une carte de ce qui est proche

Chaque commerce publié placé sur OpenStreetMap, avec la distance depuis le visiteur et le même filtre « peut me servir maintenant  que la liste.

04

Profils et catalogues

Photos, horaires, liens de contact et un catalogue de produits avec état du stock et prix fixe ou « à partir de  — la page web qu’un petit commerce n’aurait pas autrement.

05

Écrire plutôt qu’appeler

Une messagerie client-commerce en temps réel, avec indicateur de saisie, accusés d’envoi et de lecture, pièces jointes, et WhatsApp à portée de doigt.

06

Les commandes en formule Advanced

Panier, validation et un cycle de commande de « en attente  à « terminée . L’acheteur paie le vendeur directement par Juice et dépose la preuve  le vendeur peut couper les commandes à tout moment.

07

Une inscription vendeur en sept étapes

Informations, contact, réseaux sociaux, produits et un aperçu en direct du profil public, avec une règle de complétude qui décide quand la fiche peut être soumise.

08

Une activation vérifiée par une personne

Le vendeur règle sa formule par Juice avec une référence générée. Un opérateur l’approuve ou la refuse, l’approbation prolonge l’abonnement, et une tâche nocturne dépublie les fiches expirées.

09

Avis et commerces enregistrés

Des notes avec réponses du vendeur, recalculées par la base, et une liste des commerces auxquels un client revient.

10

Des statistiques pour les vendeurs

Vues du profil et commandes dans le temps, pour qu’un vendeur voie si sa présence sur Lokal porte ses fruits.

11

Une console d’exploitation

Modération, activations, catégories, invitations et essais gratuits, paramètres de la plateforme, support vendeurs et boîte de contact, avec son propre tableau de bord.

12

Bilingue et notifié

Anglais et français partout, des notifications push rédigées dans la langue de chaque destinataire, thèmes clair et sombre, et un texte qui s’agrandit jusqu’à 2,5× sur mobile.

Une commande, retracée

Un client trouve une boutique sur Lokal et y passe commande. Voici tout ce qui se passe entre le bouton « Commander  et le téléphone du vendeur qui s’allume.

Relevé dans orders.service.ts, les migrations 033 et 036 et l’Edge Function push-dispatch. Chaque nom ci-dessous apparaît dans le code exactement tel quel.

  1. 01

    La commande exige un acheteur connecté

    La page de validation est protégée par la garde d’authentification, et les coordonnées de livraison sont validées par un schéma Zod avant tout envoi.

    • RequireAuth
    • checkoutSchema
    Ce qui s’affiche en cas de refus

    Un visiteur anonyme est d’abord envoyé à la connexion  un nom de moins de deux caractères ou un numéro de moins de six ne quittent jamais le formulaire.

  2. 02

    Le client additionne, la base décide

    usePlaceOrder transmet le panier au service des commandes, qui totalise les lignes et prépare l’écriture. Le total est une commodité — que la commande ait le droit d’exister ne dépend pas du client.

    • usePlaceOrder
    • orderItemsTotal
    • placeOrder
  3. 03

    Deux tables, des prix figés

    La commande est écrite « en attente , en roupies, avec un paiement marqué direct — Lokal ne détient aucun argent. Chaque ligne garde son nom et son prix unitaire, si bien qu’un changement de prix ne réécrit jamais une commande passée.

    • orders
    • order_items
    • payment_method: 'direct'
  4. 04

    La sécurité des lignes vérifie l’acheteur

    La politique d’insertion exige que l’acheteur inscrit sur la ligne soit l’utilisateur connecté, et que le commerce soit publié.

    • buyer_id = auth.uid()
    • business_is_public
    Ce qui s’affiche en cas de refus

    Commander au nom d’un autre, ou auprès d’une fiche qui n’est pas active, est refusé par Postgres lui-même — quoi que le client envoie.

  5. 05

    … puis le commerce

    La même politique vérifie que ce commerce accepte des commandes  son interrupteur est activé, il est en formule Advanced et son abonnement est actif.

    • business_accepts_orders
    • plan = 'advanced'
    • subscription_status
    Ce qui s’affiche en cas de refus

    Une fiche Basic, un abonnement expiré ou un vendeur qui vient de couper les commandes refusent l’insertion — même depuis un onglet ouvert avant le changement.

  6. 06

    Le vendeur est prévenu, dans sa langue

    Un trigger sur la nouvelle commande met en file une notification pour le vendeur, rédigée à partir de notification_copy dans la langue de son profil, avec l’anglais en repli.

    • queue_order_notification
    • notify_user
    • notification_copy
  7. 07

    Un envoi qui survit aux pannes

    La ligne de la file réveille aussitôt l’Edge Function push-dispatch, et une tâche cron vide le reste chaque minute. Chaque ligne n’est prise qu’une fois, envoyée à tous les appareils du vendeur via FCM et retentée jusqu’à cinq fois.

    • notification_outbox
    • push-dispatch
    • push-dispatch-drain
  8. 08

    Les deux côtés restent synchronisés

    Les commandes sont diffusées en Realtime, si bien que la liste du vendeur se met à jour en direct. Quand l’acheteur dépose sa preuve de paiement Juice, l’horodatage vient de l’horloge de la base, pas du téléphone.

    • supabase_realtime
    • orders_stamp_payment_proof

Détail technique

Relevé dans le code source du projet, pas dans une plaquette commerciale.

Modèle de données
Postgres sur Supabase  21 tables, toutes sous sécurité au niveau des lignes avec 84 politiques, 63 fonctions SQL, 32 triggers et 12 énumérations, construits au fil de 38 migrations
Autorisations
Appliquées dans la base, pas dans les écrans — un vendeur ne peut pas publier sa propre fiche, seule une fonction réservée aux administrateurs le peut  les fonctions security definer fixent leur search path  la clé service-role n’atteint jamais un client
Client web
React 19 en TypeScript strict avec Vite, TanStack Query, Zustand, React Hook Form et Zod  15 modules fonctionnels découpés en couches domaine, application, infrastructure et présentation  58 routes chargées à la demande derrière 4 gardes
Client mobile
Flutter avec BLoC, go_router et get_it sur le même projet Supabase et les mêmes textes — 154 fichiers Dart écrits à la main répartis en 15 fonctionnalités, avec flutter_map, geolocator, capture d’images et Firebase Cloud Messaging
Notifications
Une file de sortie des notifications avec prise en charge idempotente et jusqu’à cinq tentatives, déclenchée par un trigger et vidée chaque minute par pg_cron  le formulaire de contact est limité à trois messages par tranche de 15 minutes
Contrôles qualité
279 tests Vitest sur le client web et 16 fichiers de tests Flutter  TypeScript strict et oxlint sans erreur  chaque push sur main est vérifié, testé, construit et publié de façon atomique, puis contrôlé sur le site en ligne

Trouvez ce qu’il vous faut près de chez vous, et sachez qui est ouvert maintenant.

Lokal — Produit StyloTech — StyloTech