============================================================
VERSION 19 — SLIDER, IMAGES ET CHATBOT INTELLIGENT (NOUVEAU)
============================================================
- Slider d'accueil : plus de zone noire vide. Le fond reprend l'image (floutée), texte aligné
sur le site, animation d'entrée, barre de progression, flèches modernes.
- Images : partout on peut coller un LIEN ou IMPORTER un fichier, avec la taille conseillée
affichée sous le champ (slider 1920x800, produit 1000x1000, logo marque 400x200...).
- Chatbot (js/chatbot.js) : lit les prix et le stock, affiche les fiches techniques, compare
2 produits (« comparer X et Y »), cherche dans la FAQ / livraison / retours / garantie / CGV
et dans les documents ajoutés (PDF, TXT, CSV) dans Admin > Chatbot.
Auto-apprentissage : questions sans réponse ou jugées inutiles -> Admin > Chatbot > « À apprendre ».
Réglages du chatbot enregistrés dans la base (plus seulement dans le navigateur).
- Bouton WhatsApp vert (en bas à gauche) sur toutes les pages : il utilise le Téléphone 2 des réglages.
- Le chatbot est maintenant sur toutes les pages de la boutique (Boutique, Contact, Commande inclus).
Pour l'ajouter à une nouvelle page : inclure après js/data.js.
À FAIRE CHEZ VOTRE HÉBERGEUR (obligatoire, une seule fois)
Ces commandes créent les nouvelles tables du chatbot et rendent visibles les images importées.
Sans elles : le chatbot fonctionne, mais n'enregistre pas l'apprentissage, et les images importées
n'apparaissent pas.
Installation de la version 19
1) Copier le CONTENU de frontend/ dans public/ de Laravel.
2) Copier les nouveaux fichiers backend : migration 13, ChatbotApiController, AdminChatbotApiController,
AdminMediaApiController, routes/api.php, ShopSettings.php, ProductApiController.php.
3) php artisan migrate (tables chatbot_questions et chatbot_documents)
4) php artisan storage:link (une seule fois : rend les images importées visibles)
5) php artisan route:cache
============================================================
ÉTAPE 17 — RÉFÉRENCEMENT GOOGLE (pages fixes)
============================================================
- Contact, FAQ, Blog, Livraison, Retours, Garantie, CGV, Mentions légales,
Confidentialité et Cookies ont maintenant : adresse officielle (canonique),
balise robots et aperçu pour Facebook / WhatsApp / X.
- Ces balises utilisent https://www.amyevolution.tn/ . Si votre domaine est différent,
remplacez ce texte dans ces 10 fichiers HTML.
- Les pages Accueil, Boutique, Catégories, Marques et Produits étaient déjà optimisées
par le serveur (titre, description, données produit, fil d'Ariane, sitemap.xml, robots.txt).
- À faire après la mise en ligne :
1) .env : APP_ENV=production et APP_URL=https://www.amyevolution.tn
2) Supprimer public/robots.txt livré avec Laravel
3) Google Search Console : ajouter le site puis envoyer /sitemap.xml
4) Fiche Google Business Profile (adresse du magasin) pour apparaître sur Google Maps
5) Remplir titre et description SEO des catégories et des produits importants dans l'admin
AMY EVOLUTION - BACKEND LARAVEL + POSTGRESQL
=====================================================
#####################################################
ÉTAPE 16 — AVIS CLIENTS MODÉRÉS, FAVORIS ET CLIENTS ADMIN (NOUVEAU)
#####################################################
AVANT : un avis déposé restait dans le navigateur du visiteur (l'admin ne le voyait jamais),
les favoris étaient perdus en changeant d'appareil, et Admin > Clients montrait des clients de démo.
MAINTENANT (site en ligne avec Laravel) :
- Fiche produit : l'avis part au serveur (POST /api/v1/products/{id}/reviews, 5 envois / minute,
champ piège anti-robots). Il reste « En attente » et n'est visible qu'après validation.
« Achat vérifié » si le client connecté a déjà commandé ce produit.
- Admin > Avis clients : liste de la base. Publier, dépublier, refuser, supprimer enregistrés.
La note et le nombre d'avis du produit sont recalculés automatiquement. Nouveau filtre « Refusés ».
- Favoris : client connecté → enregistrés sur son compte (table wishlist_items) et retrouvés sur
tous ses appareils. Les favoris ajoutés avant la connexion sont fusionnés automatiquement.
- Admin > Clients : vrais clients (comptes inscrits + clients des commandes), nombre de commandes,
total dépensé, segment calculé et nombre de favoris de chaque client.
- Sans serveur : mode démo inchangé.
Installation de l'étape 16
1) Copier le CONTENU de frontend/ dans public/ de Laravel.
2) Aucune nouvelle migration (tables reviews et wishlist_items déjà créées par la migration 4).
3) php artisan route:clear && php artisan cache:clear
Fichiers ajoutés / modifiés
- backend : Api/ReviewApiController.php, Api/WishlistApiController.php (nouveau),
Admin/AdminReviewApiController.php (nouveau), Admin/AdminCustomerApiController.php (nouveau),
Models/WishlistItem.php (nouveau), routes/api.php
- frontend : js/data.js, js/app.js, js/product.js, js/admin.js, js/admin-import.js, product.html, admin.html
#####################################################
ÉTAPE 15 — CONTACT ET NEWSLETTER ENREGISTRÉS DANS LA BASE (NOUVEAU)
#####################################################
AVANT : messages du formulaire de contact et abonnés newsletter restaient dans le navigateur du
visiteur : l'administrateur ne les voyait jamais.
MAINTENANT (site en ligne avec Laravel) :
- contact.html envoie le message au serveur (POST /api/v1/contact, 5 envois / minute, champ piège anti-robots).
- Les formulaires newsletter (accueil, blog) envoient l'email au serveur (POST /api/v1/newsletter/subscribe).
- Inscription client avec la case « Recevoir les offres » : abonné automatiquement. La case du profil
abonne / désabonne aussi.
- Admin > Messages : liste de la base, « Marquer comme traité » et « Supprimer » enregistrés.
- Admin > Newsletter : abonnés de la base, désinscription enregistrée (l'abonné est désactivé).
- Sans serveur : mode démo inchangé.
Installation de l'étape 15
1) Copier le CONTENU de frontend/ dans public/ de Laravel.
2) php artisan migrate (migration 12 : table contact_messages + colonne source des abonnés)
3) php artisan route:clear && php artisan cache:clear
Fichiers ajoutés / modifiés
- backend : migration 2025_01_01_000012, Models/ContactMessage.php, Models/NewsletterSubscriber.php,
Api/ContactApiController.php, Api/NewsletterApiController.php, Admin/AdminInboxApiController.php,
Api/AuthApiController.php, routes/api.php
- frontend : js/data.js, js/app.js, js/admin.js, js/account.js, contact.html, blog.html
#####################################################
ÉTAPE 14 — DIAPORAMA ET BLOG DYNAMIQUES (NOUVEAU)
#####################################################
AVANT : le back-office écrivait les articles du blog dans SON navigateur : les visiteurs ne les
voyaient jamais. Le diaporama s'affichait « enregistré » même si le serveur l'avait refusé.
MAINTENANT (site en ligne avec Laravel) :
- Admin > Blog : la liste vient de la table « blog_posts ». Créer, modifier, publier / dépublier,
mettre à la une et supprimer enregistrent directement dans la base (un seul article à la une).
- blog.html lit les articles publiés sur le serveur (GET /api/v1/blog) : tous les visiteurs voient
la même chose. Un article programmé apparaît automatiquement à sa date (au plus 5 min après).
- Admin > Slider Hero : chaque action (ajouter, modifier, monter / descendre, masquer, supprimer,
réinitialiser) attend la réponse du serveur. En cas de refus (ex. image sans https://), le
message d'erreur s'affiche et rien n'est changé. L'accueil affiche le diaporama de la base.
- Sans serveur : mode démo inchangé (données dans le navigateur).
Installation de l'étape 14
1) Copier le CONTENU de frontend/ dans public/ de Laravel.
2) Si ce n'est pas déjà fait : php artisan migrate (table blog_posts, migration 11)
3) php artisan route:clear && php artisan cache:clear
Rien d'autre côté serveur : les routes /api/v1/blog et /api/v1/admin/blog existent déjà.
Fichiers modifiés
- frontend : js/data.js, js/admin.js, js/admin-blog.js, blog.html
#####################################################
ÉTAPE 12 — CODES PROMO ENREGISTRÉS DANS LA BASE (NOUVEAU)
#####################################################
AVANT : les coupons étaient gardés dans le navigateur de l'administrateur. Un coupon créé dans
l'admin n'existait pas pour les clients, et la liste des codes était lisible dans data.js.
MAINTENANT (site en ligne avec Laravel) :
- Admin > Coupons : la liste vient de la table « coupons ». Créer, modifier, activer / désactiver
et supprimer enregistrent directement dans la base. Le nombre d'utilisations est le vrai.
- Panier et commande : le code saisi est vérifié par le serveur (POST /api/v1/coupons/check),
avec un message clair (expiré, désactivé, épuisé, minimum d'achat). La liste des codes
n'est jamais envoyée au navigateur. 20 essais / minute maximum (contre les robots).
- À la commande, le serveur revérifie le code et compte l'utilisation (comme avant).
- Supprimer un coupon ne touche pas les anciennes commandes (elles gardent leur remise).
- Une date d'expiration reste valable jusqu'à la fin de la journée choisie.
- Sans serveur : mode démo inchangé.
Installation de l'étape 12
1) Copier les fichiers ci-dessous, et le CONTENU de frontend/ dans public/ de Laravel.
2) Aucune nouvelle table (la table coupons existe déjà, migration 3).
3) Facultatif, créer les 2 codes de départ (BIENVENUE10, LIVRAISON) :
php artisan db:seed --class=CouponSeeder
4) php artisan route:clear
Fichiers ajoutés
- app/Http/Controllers/Admin/AdminCouponApiController.php
- app/Http/Controllers/Api/CouponApiController.php
- database/seeders/CouponSeeder.php
Fichiers modifiés
- routes/api.php, app/Models/Coupon.php, app/Http/Controllers/Api/OrderApiController.php
- frontend : js/data.js, js/admin.js, cart.html, checkout.html, cookies.html
Adresses de l'API (nouvelles)
POST /api/v1/coupons/check { code, subtotal } -> { ok, coupon } ou { ok:false, error }
Avec le jeton du back-office :
GET /api/v1/admin/coupons POST /api/v1/admin/coupons
PUT /api/v1/admin/coupons/{id} DELETE /api/v1/admin/coupons/{id}
Corps : { code, type: percent|fixed|shipping, value, min, max (0 = illimité), expires: AAAA-MM-JJ, active }
#####################################################
ÉTAPE 11 — CONNECTEURS API SÉCURISÉS (NOUVEAU)
#####################################################
Back-office > Système > « Connecteurs API » :
- ENVOYER : votre site prévient automatiquement vos logiciels (nouveau client, client modifié,
nouvelle commande, commande modifiée). Chaque envoi est signé et réessayé en cas d'échec.
- CONSOMMER : un logiciel partenaire (ERP, facturation, stock) lit et écrit chez vous avec une clé.
- Clé générée en un clic, affichée UNE seule fois. Seule une empreinte est enregistrée.
- Permissions par clé (lecture / écriture, séparées pour clients, produits et commandes),
désactivation immédiate, renouvellement, expiration, adresses IP autorisées, limite par minute.
- Chaque requête : HTTPS + signature + horodatage de moins de 5 minutes + code unique (anti-rejeu).
- Journal d'activité : date, clé, appel, résultat (le contenu des données n'est jamais conservé).
- Montants en millimes (3 799,000 TND = 3799000). Dates ISO 8601 en heure universelle.
- Création, modification et suppression réservées au Super Admin.
Mode d'emploi pour l'informaticien : bouton « Voir le mode d'emploi » sur la page.
Installation de l'étape 11
1) Copier les fichiers, et le CONTENU de frontend/ dans public/ de Laravel.
2) php artisan migrate (tables customers, api_keys, api_request_logs, webhook_endpoints, webhook_deliveries)
3) Tâche planifiée (nouveaux essais d'envoi + nettoyage du journal), une seule fois :
crontab -e puis ajouter :
* * * * * cd /var/www/amyevolution/backend && php artisan schedule:run >> /dev/null 2>&1
4) CACHE_STORE=database (ou redis) dans .env : il sert à la protection anti-rejeu.
5) Ne JAMAIS changer APP_KEY après avoir créé des clés (sinon : renouveler toutes les clés).
6) php artisan config:clear && php artisan route:clear
Adresses : https://www.amyevolution.tn/api/connect/v1/{customers|products|orders}
Fichiers ajoutés
- database/migrations/2025_01_01_000009_create_api_connector_tables.php
- app/Models : Customer, ApiKey, ApiRequestLog, WebhookEndpoint, WebhookDelivery
- app/Support/Connectors : ApiKeyManager, ConnectorRules, ConnectorPresenter, CustomerSync,
ApiDates, Money, UrlGuard, WebhookDispatcher
- app/Http/Middleware/AuthenticateApiKey.php
- app/Http/Controllers/Connector : Customer/Product/Order ConnectorController
- app/Http/Controllers/Admin/AdminConnectorApiController.php
- app/Console/Commands/ConnectorsMaintenanceCommand.php
- frontend/js/admin-connectors.js
Fichiers modifiés
- routes/api.php, bootstrap/app.php, app/Models/Order.php, app/Models/ApiKey.php
- Api/OrderApiController.php, Admin/AdminOrderApiController.php
- frontend/admin.html, js/admin.js, css/admin.css
#####################################################
==================================================
ÉTAPE 9 — PRODUITS DE L'ADMINISTRATION RELIÉS AU SERVEUR
==================================================
- Une fois connecté au serveur, la page Produits affiche les vrais produits
de la base (y compris brouillons et archivés).
- Ajouter, modifier, dupliquer, changer le stock et supprimer enregistrent
directement dans la base. La boutique est mise à jour aussitôt.
- Une marque inconnue est créée automatiquement.
- Images : utilisez des adresses web (https://...). Les fichiers collés
depuis l'ordinateur sont refusés (trop lourds pour la base).
- Supprimer un produit le cache (suppression douce) : les anciennes
commandes restent intactes.
- Les catégories restent en lecture seule en mode serveur (étape suivante).
- Sur le serveur, lancer seulement : php artisan route:clear
ÉTAPE 8 — COMMANDES DU BACK-OFFICE RELIÉES À LA BASE (NOUVEAU)
#####################################################
- Connecté au serveur, la page Commandes (et le tableau de bord, les statistiques, la recherche)
affiche les VRAIES commandes de la base PostgreSQL (500 plus récentes).
- Changer le statut d'une commande l'enregistre dans la base :
annulation = stock remis ; réactivation = stock repris (refusée s'il n'en reste pas assez).
Un email part au client si l'envoi d'emails (SMTP) est réglé.
- Sans serveur : mode démo inchangé.
Fichiers : app/Http/Controllers/Admin/AdminOrderApiController.php (nouveau), routes/api.php,
Api/OrderApiController.php (present() public), frontend/js/admin.js.
Adresses : GET /api/v1/admin/orders · PATCH /api/v1/admin/orders/{numéro} { status }
Puis : php artisan route:clear
#####################################################
ÉTAPE 7 — PETITES ERREURS VISIBLES CORRIGÉES (NOUVEAU)
#####################################################
- product.html ouvert sans produit choisi : renvoie vers la boutique (plus de « Produit introuvable »).
- Même en-tête complet (recherche, menu Catégories, Compte, favoris, panier, menu mobile) sur
cart.html, contact.html, cgv.html, legal.html, privacy.html et product.html.
- Accueil : une catégorie sans produit affiche « Bientôt disponible » au lieu de « 0 produit ».
(Gaming et Accessoires sont vides car aucun produit n'y est encore rangé : ajoutez-en depuis l'admin.)
- Mobile : le logo ne dépasse plus de l'écran.
Fichiers modifiés : frontend/cart.html, contact.html, cgv.html, legal.html, privacy.html, product.html,
js/product.js, js/app.js, css/style.css. Rien à faire côté serveur.
#####################################################
ÉTAPE 6 — BACK-OFFICE SÉCURISÉ (NOUVEAU)
#####################################################
AVANT : le mot de passe administrateur était vérifié dans le navigateur (comptes écrits dans data.js,
ex. admin123). N'importe qui pouvait les lire ou fabriquer une fausse session.
MAINTENANT (site en ligne avec Laravel) :
- backend/login.html envoie l'email et le mot de passe au serveur (POST /api/v1/admin/login).
- Seuls les comptes de la base avec le rôle « admin » ou « superadmin » et actifs peuvent entrer.
- Le serveur délivre un jeton « back-office » valable 12 heures (fermé à la déconnexion
ou à la fermeture de l'onglet). Un jeton client de la boutique ne donne PAS accès à l'admin.
- 5 essais par minute maximum, même message d'erreur pour un email inconnu ou un mauvais mot de passe.
- admin.html reste caché tant que le serveur n'a pas confirmé la session.
- Les anciens comptes de démo (admin123, gestion123, support123) sont REFUSÉS quand le serveur répond.
Ils ne servent plus qu'en mode démo (fichiers ouverts sans serveur).
- Page « Utilisateurs » du back-office : liste des administrateurs de la base. Ajouter, modifier,
désactiver ou retirer un accès est réservé au Super Admin. On ne peut pas se désactiver soi-même.
À FAIRE UNE FOIS sur le serveur (créer le premier Super Admin) :
php artisan amy:create-admin votre@email.com "Votre Nom"
-> le mot de passe est demandé (8 caractères mini, lettre + chiffre), il n'est pas affiché.
La même commande remet un mot de passe si vous l'oubliez.
Puis : php artisan route:clear && php artisan config:clear
Aucune nouvelle table (les jetons utilisent la table personal_access_tokens de Sanctum).
Fichiers ajoutés
- app/Http/Controllers/Api/AdminAuthApiController.php .... connexion / déconnexion / session admin
- app/Http/Controllers/Admin/AdminUserApiController.php .. gestion des comptes du back-office
- app/Console/Commands/CreateAdminCommand.php ............ commande amy:create-admin
Fichiers modifiés
- routes/api.php, bootstrap/app.php, app/Http/Middleware/EnsureUserIsAdmin.php (exige un jeton « admin »)
- backend/login.html, frontend/admin.html, frontend/js/data.js, frontend/js/admin.js
Adresses de l'API (nouvelles)
POST /api/v1/admin/login { email, password } -> { token, user }
Avec « Authorization: Bearer » :
POST /api/v1/admin/logout GET /api/v1/admin/me
GET /api/v1/admin/users POST /api/v1/admin/users
PUT /api/v1/admin/users/{id} DELETE /api/v1/admin/users/{id}
RESTE À FAIRE (étapes suivantes) : les autres pages du back-office (produits, commandes, réglages)
lisent encore les données du navigateur ; elles seront branchées sur le serveur une par une.
#####################################################
ÉTAPE 5 — COMPTES CLIENTS : INSCRIPTION ET CONNEXION (NOUVEAU)
#####################################################
Les clients peuvent créer un compte, se connecter, retrouver leurs commandes, modifier leurs
informations et leur mot de passe, et recevoir un lien « Mot de passe oublié » par email.
Commander SANS compte reste possible (comme avant).
- Page « Mon compte » (account.html) : onglets « Se connecter » / « Créer un compte » / mot de passe oublié.
Une fois connecté : Mes commandes, Mes informations, Mot de passe, Se déconnecter.
- L'en-tête de toutes les pages affiche le prénom du client connecté.
- Commande : client connecté = formulaire pré-rempli, commande reliée à son compte (orders.user_id).
Visiteur = bouton « Se connecter » qui le ramène ensuite à la commande.
- « Mes commandes » montre aussi les anciennes commandes passées SANS compte avec le même téléphone.
- Sécurité : mots de passe chiffrés (bcrypt), 8 caractères mini avec lettre + chiffre,
10 essais / minute maximum, même message si l'email n'existe pas (on ne révèle pas qui est client),
connexion valable 30 jours, changer ou réinitialiser le mot de passe ferme les autres connexions.
Le rôle « admin » ne peut jamais être obtenu par l'inscription.
- Sans serveur (fichiers ouverts seuls) : mode démo, les comptes restent dans le navigateur.
L'email « mot de passe oublié » ne part qu'avec le serveur.
Fichiers ajoutés
- app/Http/Controllers/Api/AuthApiController.php ...... inscription, connexion, profil, mots de passe
- app/Mail/ResetPasswordMail.php + resources/views/emails/reset-password.blade.php
- frontend : js/account.js, mot-de-passe.html (page ouverte depuis l'email)
Fichiers modifiés
- routes/api.php (routes /api/v1/auth/... et /api/v1/me/orders actives)
- app/Models/User.php (email « mot de passe oublié » vers mot-de-passe.html)
- app/Http/Controllers/Api/OrderApiController.php (commande reliée au compte, liste « mes commandes »)
- app/Http/Controllers/SitemapController.php (mot-de-passe.html non indexée)
- frontend : account.html, checkout.html, js/data.js, js/app.js, css/style.css
Adresses de l'API (nouvelles)
POST /api/v1/auth/register { firstName, lastName, email, phone?, password, newsletter? }
POST /api/v1/auth/login { email, password } -> { token, user }
POST /api/v1/auth/forgot-password { email }
POST /api/v1/auth/reset-password { email, token, password, password_confirmation }
Avec « Authorization: Bearer » :
POST /api/v1/auth/logout · GET /api/v1/auth/me · PUT /api/v1/auth/profile
PUT /api/v1/auth/password { currentPassword, password, password_confirmation }
GET /api/v1/me/orders
Installation de l'étape 5
1) Laravel Sanctum doit être installé (déjà demandé à l'étape 1) :
composer require laravel/sanctum
Laravel 11/12 : si routes/api.php n'était pas encore actif, « php artisan install:api » l'active.
2) Copier les fichiers ci-dessus, et le CONTENU de frontend/ dans public/ de Laravel.
3) Aucune nouvelle table : les tables users, personal_access_tokens et password_reset_tokens
existent déjà (migration 1). Si besoin : php artisan migrate
4) .env : APP_URL doit être la vraie adresse du site (le lien de l'email l'utilise) et
les réglages MAIL_... doivent fonctionner (tester : php artisan amy:mail-test votre@adresse.com).
5) php artisan config:clear && php artisan route:clear
6) Tester : créer un compte sur « Mon compte », se déconnecter, se reconnecter, passer une commande
connecté (elle apparaît dans « Mes commandes » et la table orders a user_id rempli),
puis « Mot de passe oublié » (l'email doit arriver, le lien ouvre mot-de-passe.html).
#####################################################
ÉTAPE 4 — COMMANDES ENREGISTRÉES SUR LE SERVEUR (NOUVEAU)
#####################################################
Avant : les commandes restaient dans le navigateur du client (le gérant ne les voyait pas).
Maintenant : quand le site tourne avec Laravel, chaque commande est enregistrée dans la base PostgreSQL.
Le serveur recalcule tout lui-même (prix, stock, code promo, livraison, total) : un client ne peut pas
modifier les prix depuis son navigateur. Le stock est réservé de façon sûre (deux clients ne peuvent pas
acheter le dernier article en même temps).
- Paiement à la livraison = choix par défaut. Les 5 modes (livraison, carte, D17, Flouci, virement)
peuvent être retirés un par un dans le .env : SHOP_PAY_CARD=false, SHOP_PAY_D17=false, etc.
- Fenêtre « Êtes-vous sûr de passer la commande ? » avant l'envoi.
- « Mon compte » : le client retrouve ses commandes avec son téléphone (+ numéro de commande).
- Sans serveur (fichiers ouverts seuls), le site reste en mode démo comme avant.
- Paiement en ligne réel (carte, D17, Flouci) : PAS ENCORE BRANCHÉ. La commande est enregistrée
« En attente de paiement » ; il faudra les comptes marchands pour encaisser automatiquement.
Fichiers ajoutés
- app/Http/Controllers/Api/OrderApiController.php .... création et suivi des commandes
- app/Exceptions/OrderException.php
- database/migrations/2025_01_01_000006_add_customer_phone_to_orders.php
Fichiers modifiés
- routes/api.php (POST /api/v1/orders et /api/v1/orders/track, limités contre les abus)
- config/shop.php (prix express SHOP_EXPRESS_PRICE=15, modes de paiement)
- app/Models/Order.php
- frontend : checkout.html, account.html, js/data.js
Installation de l'étape 4
1) Copier les fichiers ci-dessus, et le CONTENU de frontend/ dans public/ de Laravel.
2) php artisan migrate
3) php artisan config:clear && php artisan route:clear
4) Tester : passer une commande sur le site, puis vérifier qu'elle apparaît dans la table « orders »
et dans l'administration (/administration-back/commandes). Puis la retrouver dans « Mon compte ».
5) Les frais de livraison doivent être les mêmes dans l'admin du site (réglages) et dans le .env
(SHOP_SHIPPING_PRICE, SHOP_EXPRESS_PRICE, SHOP_FREE_SHIPPING_FROM) : c'est le serveur qui décide.
#####################################################
ÉTAPE 3 — SEO CÔTÉ SERVEUR (NOUVEAU)
#####################################################
Google reçoit maintenant, dès le premier chargement et sans JavaScript : le titre, la description,
l'adresse canonique, Open Graph (Facebook/WhatsApp), les données produit (prix, stock, étoiles,
livraison, retours), le fil d'Ariane et le texte des pages.
Adresses propres
/produit/iphone-16-pro-max · /categorie/smartphone · /marque/apple · /boutique
Les anciennes adresses (product.html?id=1, shop.html?cat=x) redirigent (301) vers les nouvelles.
Plan du site automatique : /sitemap.xml (pages, catégories, marques, produits + images)
robots.txt automatique : /robots.txt (bloque admin, panier, compte ; tout bloqué hors production)
Fichiers ajoutés
- config/shop.php ........................ nom, contacts, livraison, retours (modifiables dans .env)
- app/Support/ShopTemplate.php ........... remplit les zones des pages HTML
- app/Support/Seo/ (ShopUrls, SeoHead, StructuredData, ShopHtml)
- app/Http/Controllers/ShopPageController.php, SitemapController.php
Fichiers modifiés
- routes/web.php (routes inexistantes mises en commentaire : route:list ne plante plus)
- bootstrap/app.php, CatalogPresenter.php (le plan du site se met à jour avec le catalogue)
- frontend : index.html, product.html, shop.html, js/product.js
Installation de l'étape 3
1) Copier le CONTENU du dossier frontend/ dans le dossier public/ de Laravel.
2) SUPPRIMER public/robots.txt et public/index.html de Laravel s'ils existent
(ATTENTION : garder public/index.php !). Sinon ils passent avant les pages SEO.
3) nginx : tout envoyer à Laravel sauf les fichiers existants :
location / { try_files $uri /index.php?$query_string; }
4) .env : APP_URL=https://www.amyevolution.tn APP_ENV=production
Optionnel : SHOP_LOGO, SHOP_DEFAULT_IMAGE, SHOP_SOCIAL_URLS=https://facebook.com/...,https://instagram.com/...
(SHOP_CLEAN_URLS=false pour garder les anciennes adresses)
5) php artisan config:clear && php artisan route:clear
6) Vérifier : /produit/ (clic droit > Code source : le titre du produit doit apparaître)
/sitemap.xml /robots.txt
Puis tester une fiche sur https://search.google.com/test/rich-results
7) Déclarer https://www.amyevolution.tn/sitemap.xml dans Google Search Console.
#####################################################
ÉTAPE 2 — CATALOGUE DYNAMIQUE (NOUVEAU)
#####################################################
Les produits, catégories, marques et avis de la boutique viennent maintenant de la base PostgreSQL.
Une modification en base est visible par TOUS les visiteurs (avant : chaque navigateur avait sa copie).
Fichiers ajoutés
- app/Support/CatalogPresenter.php ........ met la base au format de la boutique + cache 5 min
- app/Http/Controllers/Api/ProductApiController.php, CategoryApiController.php,
BrandApiController.php, SearchApiController.php, ReviewApiController.php
- database/migrations/2025_01_01_000005_add_shop_flags_to_products.php (vente flash, nouveauté)
- database/seeders/CatalogSeeder.php + database/seeders/data/catalog.json (les 8 produits de démo)
Fichiers modifiés
- routes/api.php : routes du catalogue actives ; les routes dont le contrôleur n'existe pas encore
sont en commentaire (avant, « php artisan route:list » et « route:cache » plantaient).
- Product, Category, Brand, Review : le cache du catalogue est vidé à chaque modification.
- frontend/*.html : chargent /api/v1/catalog.js avant js/data.js.
- frontend/js/data.js : utilise le catalogue du serveur s'il répond, sinon les données de démo.
Installation de l'étape 2
1) php artisan migrate
2) php artisan db:seed --class=CatalogSeeder (peut être relancé sans créer de doublons)
3) Le dossier frontend et Laravel doivent être sur le MÊME domaine. Exemple nginx :
root /var/www/amyevolution/frontend;
location /api/ { try_files $uri /index.php?$query_string; } # -> public/index.php de Laravel
(ou copier le contenu de frontend/ dans le dossier public/ de Laravel : c'est le plus simple)
4) Vérifier dans le navigateur :
https://www.amyevolution.tn/api/v1/catalog -> la liste des produits en JSON
https://www.amyevolution.tn/api/v1/products?cat=smartphone&sort=price_asc
Adresses de l'API (lecture publique)
GET /api/v1/catalog · /api/v1/catalog.js
GET /api/v1/products ?cat= &brand= &q= &min= &max= &in_stock=1 &sort=newest|price_asc|price_desc|popular|rating|name &per_page=
GET /api/v1/products/featured · flash-sale · new · bestsellers
GET /api/v1/products/{slug ou id} · /api/v1/products/{id}/related · /api/v1/products/{id}/reviews
GET /api/v1/categories · /api/v1/categories/{slug} · /api/v1/brands
GET /api/v1/search?q= · /api/v1/search/suggestions?q= (60 requêtes / minute / visiteur)
Encore local pour l'instant (étapes suivantes) : panier, back-office. (Commandes : étape 4, comptes clients : étape 5.)
#####################################################
ÉTAPE 1 — BASE, MODÈLES ET CORRECTIONS
#####################################################
CE QUI A CHANGÉ DANS CETTE VERSION
- Base de données : PostgreSQL (comme Amy ERP). Le site garde SA propre base « amyevolution »,
séparée de celle de l'ERP. Les deux communiqueront par l'API (étape 2), jamais en direct.
- Migrations remises dans le bon ordre (avant : les produits étaient créés avant les catégories
et les commandes avant les coupons -> « php artisan migrate » échouait).
- Montants en dinars avec 3 décimales (millimes) au lieu de 2. Pays par défaut : TN.
- Modèles qui manquaient ajoutés : User, OrderItem, Category, Brand, Review, Coupon, Address.
- Middleware « admin » ajouté (les routes d'administration en avaient besoin).
- Contrôleurs d'administration rangés dans app/Http/Controllers/Admin (leur emplacement officiel).
- Emails : email au client à chaque changement de statut de commande, email d'essai,
commande « php artisan amy:mail-test » et bouton de test dans Paramètres.
CORRECTIONS DE BUGS
- Numéro de commande : deux commandes simultanées pouvaient avoir le même numéro -> séquence PostgreSQL.
- Stock : pouvait devenir négatif quand deux clients achètent le dernier article -> baisse vérifiée.
- Recherche : sous PostgreSQL « LIKE » respecte les majuscules -> « ILIKE ». % et _ neutralisés.
- Le prix d'achat (cost_price) n'est plus jamais envoyé à la boutique.
- Un client ne peut plus se donner le rôle « admin » par un formulaire (role non remplissable).
- Supprimer une catégorie ne supprime plus tous ses produits.
- Tableau de bord : « ?period=abc » faisait planter la page.
- Liste des produits : un tri invalide faisait planter la page ; « en stock » ignorait le seuil d'alerte.
- Modification d'un produit : le slug suit le nouveau nom ; règle « prix barré > prix » appliquée aussi.
- Import CSV : séparateur « ; » d'Excel accepté, lignes incomplètes signalées au lieu de tout bloquer,
produits sans référence plus écrasés les uns sur les autres, bons numéros de ligne dans les erreurs.
- Export CSV : accents corrects dans Excel, lecture par paquets (gros catalogues).
- Remboursement : affiché en TND (et non en €), refusé si la commande n'est pas payée,
l'ancienne note n'est plus effacée.
- Routes : « produits/export » était masquée par la route d'un produit (ordre corrigé).
INSTALLATION (sur le serveur, dans le dossier backend d'un projet Laravel 11 ou 12)
1) Créer la base (une seule fois) :
sudo -u postgres psql
CREATE USER amyevolution WITH PASSWORD 'un-mot-de-passe-solide';
CREATE DATABASE amyevolution OWNER amyevolution;
\q
2) PHP doit avoir l'extension pdo_pgsql (Ubuntu : sudo apt install php8.3-pgsql).
3) composer require laravel/sanctum
4) Copier env.example.txt en .env, le remplir, puis :
php artisan key:generate
php artisan migrate
5) Créer le premier administrateur :
php artisan tinker
$u = App\Models\User::create(['first_name'=>'Admin','last_name'=>'AMY','email'=>'admin@amyevolution.tn','password'=>'MotDePasseSolide2026']);
$u->role = 'admin'; $u->save();
TESTER L'ENVOI DES EMAILS
- En ligne de commande : php artisan amy:mail-test votre@adresse.com
-> « Email d'essai envoyé » ou l'erreur exacte du serveur SMTP.
- Sans vrai envoi : MAIL_MAILER=log dans .env, l'email apparaît dans storage/logs/laravel.log
- Après chaque modification du .env : php artisan config:clear
- Gmail : activer la validation en 2 étapes puis créer un « mot de passe d'application ».
À VENIR
- Étape 2 : connecteur vers l'API ouverte d'Amy ERP (commandes envoyées à l'ERP, stock reçu).
- Étape 3 : tests de connexion entre le site et l'ERP.
============================================================
VERSION 18 — SITE PLUS RAPIDE (serveur)
============================================================
Ce qui change :
- /api/v1/catalog.js est 3 à 5 fois plus léger : il ne contient plus que ce qui sert
aux listes (nom, prix, 2 photos, stock...). La description, les caractéristiques
et toutes les photos sont chargées sur la fiche produit uniquement.
- Le catalogue est préparé une seule fois et gardé en cache ; si le visiteur l'a
déjà, le serveur répond « 304 » (rien à retélécharger).
- Nouveaux index de base de données (migration 10) pour des listes rapides
même avec 10 000+ produits.
À faire une fois chez l'hébergeur :
1) php artisan migrate
2) Dans .env : CACHE_STORE=redis si disponible (sinon database ou file)
3) php artisan config:cache && php artisan route:cache && php artisan view:cache
4) Activer OPcache dans PHP (souvent déjà actif chez l'hébergeur)