DESKTOP-SMCIPAV\falko 5 mesiacov pred
rodič
commit
3d487329f2

+ 8 - 3
angular-client/angular.json

@@ -26,7 +26,9 @@
             "assets": [
               "src/favicon.ico",
               "src/assets",
-              "src/manifest.webmanifest"
+              "src/manifest.webmanifest",
+              "src/robots.txt",      
+              "src/sitemap.xml"      
             ],
             "styles": [
               "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
@@ -105,7 +107,10 @@
             "browserTarget": "fatboar:build:production",
             "serverTarget": "fatboar:server:production",
             "routes": [
-              "/"
+              "/",                   
+              "/concours",
+              "/menus",
+              "/contact"
             ]
           }
         }
@@ -117,4 +122,4 @@
     "analytics": false,
     "packageManager": "npm"
   }
-}
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 32 - 2
angular-client/dist/fatboar/browser/index.html


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
angular-client/dist/fatboar/browser/main-es2015.fdfde3e41b4dc5cc3156.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
angular-client/dist/fatboar/browser/main-es5.fdfde3e41b4dc5cc3156.js


+ 12 - 0
angular-client/dist/fatboar/browser/robots.txt

@@ -0,0 +1,12 @@
+User-agent: *
+Allow: /
+Disallow: /login
+Disallow: /login/lost-password
+Disallow: /login/reset-password
+Disallow: /users
+Disallow: /statistique
+Disallow: /emailing
+Disallow: /assistance
+Disallow: /auth
+Disallow: /profil
+Sitemap: https://angular-preprod.foodgame.fr/sitemap.xml

+ 89 - 0
angular-client/dist/fatboar/browser/sitemap.xml

@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/</loc>
+        <changefreq>daily</changefreq>
+        <priority>1.0</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/login</loc>
+        <changefreq>weekly</changefreq>
+        <priority>0.8</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/login/lost-password</loc>
+        <changefreq>weekly</changefreq>
+        <priority>0.8</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/login/reset-password/:token</loc>
+        <changefreq>weekly</changefreq>
+        <priority>0.8</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/register</loc>
+        <changefreq>weekly</changefreq>
+        <priority>0.8</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/cgu</loc>
+        <changefreq>monthly</changefreq>
+        <priority>0.6</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/mention-legales</loc>
+        <changefreq>monthly</changefreq>
+        <priority>0.6</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/politique-de-confidentialite</loc>
+        <changefreq>monthly</changefreq>
+        <priority>0.6</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/admin</loc>
+        <changefreq>weekly</changefreq>
+        <priority>0.8</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/users</loc>
+        <changefreq>weekly</changefreq>
+        <priority>0.8</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/statistique</loc>
+        <changefreq>weekly</changefreq>
+        <priority>0.8</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/emailing</loc>
+        <changefreq>weekly</changefreq>
+        <priority>0.8</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/assistance</loc>
+        <changefreq>weekly</changefreq>
+        <priority>0.8</priority>
+    </url>
+    <url>
+        <loc>https://angular-preprod.foodgame.fr/contact</loc>
+        <changefreq>monthly</changefreq>
+        <priority>0.6</priority>
+    </url>
+
+    <!-- Autres routes -->
+    
+    <!-- Auth -->
+    <url><loc>https://angular-preprod.foodgame.fr/auth</loc></url>
+
+    <!-- Tirage -->
+    <url><loc>https://angular-preprod.foodgame.fr/tirage</loc></url>
+
+    <!-- Profil -->
+    <url><loc>https://angular-preprod.foodgame.fr/profil</loc></url>
+
+    <!-- Not Found -->
+    <!-- Cette route n'a pas besoin d'être incluse dans le sitemap -->
+    
+</urlset>
+

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1 - 1
angular-client/dist/fatboar/server/main.js


+ 1 - 0
angular-client/package.json

@@ -57,6 +57,7 @@
     "@types/jasmine": "~3.5.0",
     "@types/jasminewd2": "~2.0.3",
     "@types/leaflet": "1.5.6",
+    "@types/minimatch": "^6.0.0",
     "@types/node": "12.11.1",
     "chai": "^4.2.0",
     "codelyzer": "^5.1.2",

+ 90 - 5
angular-client/server.ts

@@ -6,23 +6,27 @@ import { AppServerModule } from './src/main.server';
 import { APP_BASE_HREF } from '@angular/common';
 import { existsSync } from 'fs';
 
+// Pour simuler le DOM côté serveur (utile pour les librairies qui accèdent à window/document)
 const domino = require('domino');
 const fs = require('fs');
 const path = require('path');
 
+// Dossiers de build
 const distFolder = join(process.cwd(), 'dist/fatboar/browser');
 const indexHtml = existsSync(join(distFolder, 'index.html')) ? 'index.html' : 'index';
 const template = fs.readFileSync(join(distFolder, indexHtml)).toString();
 
+// Simule le DOM pour SSR
 const win = domino.createWindow(template);
 global['window'] = win;
 global['document'] = win.document;
 global['navigator'] = win.navigator;
-global['CSS'] = null;
+global['CSS'] = null; // Certaines libs vérifient CSS
 
 export function app() {
   const server = express();
 
+  // Configure le moteur de rendu Angular Universal
   server.engine('html', ngExpressEngine({
     bootstrap: AppServerModule,
   }));
@@ -30,27 +34,29 @@ export function app() {
   server.set('view engine', 'html');
   server.set('views', distFolder);
 
-  // Fichiers statiques
+  // Sert les fichiers statiques (assets, images, etc.)
   server.get('*.*', express.static(distFolder, {
     maxAge: '1y'
   }));
 
-  // Routes SEO (déplacées depuis Express)
+  // Sert sitemap.xml pour le SEO
   server.get('/sitemap.xml', (req, res) => {
     res.header('Content-Type', 'application/xml');
     res.sendFile(join(distFolder, 'sitemap.xml'));
   });
 
+  // Sert robots.txt pour le SEO
   server.get('/robots.txt', (req, res) => {
     res.sendFile(join(distFolder, 'robots.txt'));
   });
 
-  // Toutes les autres routes
+  // Sert toutes les autres routes via Angular Universal
   server.get('*', (req, res) => {
     res.render(indexHtml, {
       req,
       providers: [
         { provide: APP_BASE_HREF, useValue: req.baseUrl },
+        // Fournit l'URL canonique pour les balises <link rel="canonical">
         { provide: 'CANONICAL_URL', useValue: `https://angular-preprod.foodgame.fr${req.originalUrl}` }
       ]
     });
@@ -67,6 +73,7 @@ function run() {
   });
 }
 
+// Démarre le serveur si ce fichier est lancé directement
 declare const __non_webpack_require__: NodeRequire;
 const mainModule = __non_webpack_require__.main;
 const moduleFilename = mainModule && mainModule.filename || '';
@@ -74,4 +81,82 @@ if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
   run();
 }
 
-export * from './src/main.server';
+export * from './src/main.server';
+
+// import 'zone.js/dist/zone-node';
+// import { ngExpressEngine } from '@nguniversal/express-engine';
+// import * as express from 'express';
+// import { join } from 'path';
+// import { AppServerModule } from './src/main.server';
+// import { APP_BASE_HREF } from '@angular/common';
+// import { existsSync } from 'fs';
+
+// const domino = require('domino');
+// const fs = require('fs');
+// const path = require('path');
+
+// const distFolder = join(process.cwd(), 'dist/fatboar/browser');
+// const indexHtml = existsSync(join(distFolder, 'index.html')) ? 'index.html' : 'index';
+// const template = fs.readFileSync(join(distFolder, indexHtml)).toString();
+
+// const win = domino.createWindow(template);
+// global['window'] = win;
+// global['document'] = win.document;
+// global['navigator'] = win.navigator;
+// global['CSS'] = null;
+
+// export function app() {
+//   const server = express();
+
+//   server.engine('html', ngExpressEngine({
+//     bootstrap: AppServerModule,
+//   }));
+
+//   server.set('view engine', 'html');
+//   server.set('views', distFolder);
+
+//   // Fichiers statiques
+//   server.get('*.*', express.static(distFolder, {
+//     maxAge: '1y'
+//   }));
+
+//   // Routes SEO (déplacées depuis Express)
+//   server.get('/sitemap.xml', (req, res) => {
+//     res.header('Content-Type', 'application/xml');
+//     res.sendFile(join(distFolder, 'sitemap.xml'));
+//   });
+
+//   server.get('/robots.txt', (req, res) => {
+//     res.sendFile(join(distFolder, 'robots.txt'));
+//   });
+
+//   // Toutes les autres routes
+//   server.get('*', (req, res) => {
+//     res.render(indexHtml, {
+//       req,
+//       providers: [
+//         { provide: APP_BASE_HREF, useValue: req.baseUrl },
+//         { provide: 'CANONICAL_URL', useValue: `https://angular-preprod.foodgame.fr${req.originalUrl}` }
+//       ]
+//     });
+//   });
+
+//   return server;
+// }
+
+// function run() {
+//   const port = process.env.PORT || 4000;
+//   const server = app();
+//   server.listen(port, () => {
+//     console.log(`Node Express server listening on http://localhost:${port}`);
+//   });
+// }
+
+// declare const __non_webpack_require__: NodeRequire;
+// const mainModule = __non_webpack_require__.main;
+// const moduleFilename = mainModule && mainModule.filename || '';
+// if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
+//   run();
+// }
+
+// export * from './src/main.server';

+ 98 - 38
angular-client/src/app/app-routing.module.ts

@@ -1,54 +1,114 @@
-import { ResetPasswordComponent } from './components/login/reset-password/reset-password.component';
-import { LostPasswordComponent } from './components/login/lost-password/lost-password.component';
-import { AssistanceComponent } from './components/admin/assistance/assistance.component';
-import { PolitiqueConfidentialiteComponent } from './components/shared/politique-confidentialite/politique-confidentialite.component';
-import { MentionsLegalesComponent } from './components/shared/mentions-legales/mentions-legales.component';
-import { CguComponent } from './components/shared/cgu/cgu.component';
-import { EmailingComponent } from './components/admin/emailing/emailing.component';
-import { StatisticComponent } from './components/admin/statistic/statistic.component';
-import { ProfilComponent } from './components/auth/profil/profil.component';
-import { GainComponent } from './components/auth/gain/gain.component';
-import { UsersComponent } from './components/admin/users/users.component';
-import { NotFoundComponent } from './components/shared/not-found/not-found.component';
 import { NgModule } from '@angular/core';
 import { Routes, RouterModule } from '@angular/router';
+
+import { HomeComponent } from './components/home/home.component';
 import { LoginComponent } from 'src/app/components/login/login.component';
+import { LostPasswordComponent } from './components/login/lost-password/lost-password.component';
+import { ResetPasswordComponent } from './components/login/reset-password/reset-password.component';
 import { RegisterComponent } from 'src/app/components/register/register.component';
-//import { NavbarComponent } from 'src/app/components/shared/navbar/navbar.component';
-import { ContactComponent } from 'src/app/components/shared/contact/contact.component';
-import { HomeComponent } from './components/home/home.component';
-import { AuthGuard } from './guards/auth.guard';
+import { CguComponent } from './components/shared/cgu/cgu.component';
+import { MentionsLegalesComponent } from './components/shared/mentions-legales/mentions-legales.component';
+import { PolitiqueConfidentialiteComponent } from './components/shared/politique-confidentialite/politique-confidentialite.component';
 import { AdminLoginComponent } from './components/admin/admin-login/admin-login.component';
+import { UsersComponent } from './components/admin/users/users.component';
+import { StatisticComponent } from './components/admin/statistic/statistic.component';
+import { EmailingComponent } from './components/admin/emailing/emailing.component';
+import { AssistanceComponent } from './components/admin/assistance/assistance.component';
+import { ContactComponent } from 'src/app/components/shared/contact/contact.component';
+import { GainComponent } from './components/auth/gain/gain.component';
 import { TirageComponent } from './components/auth/tirage/tirage.component';
+import { ProfilComponent } from './components/auth/profil/profil.component';
+import { NotFoundComponent } from './components/shared/not-found/not-found.component';
+import { AuthGuard } from './guards/auth.guard';
 
 const routes: Routes = [
-  { path: '', component: HomeComponent},
-  { path: 'login', component: LoginComponent },
-  { path: 'login/lost-password', component: LostPasswordComponent},
-  { path: 'login/reset-password/:token', component: ResetPasswordComponent},
-  { path: 'register', component: RegisterComponent},
-  { path: 'cgu', component: CguComponent},
-  { path: 'mention-legales', component: MentionsLegalesComponent},
-  { path: 'politique-de-confidentialite', component: PolitiqueConfidentialiteComponent},
-  { path: 'admin', component: AdminLoginComponent },
-
-  { path: 'users', component: UsersComponent },
-  { path: 'statistique', component: StatisticComponent, canActivate: [AuthGuard], data: {roles: ['admin'] }},
-  { path: 'emailing', component: EmailingComponent,  canActivate: [AuthGuard], data: {roles: ['admin'] }},
-  { path: 'assistance', component: AssistanceComponent, canActivate: [AuthGuard], data: {roles: ['admin'] }},
-  { path: 'contact', component: ContactComponent },
-  { path: 'home', component: HomeComponent },
-  { path: 'auth', component: GainComponent},
-  { path: 'tirage', component: TirageComponent},
-  { path: 'profil', component: ProfilComponent },
-  { path: '**', component: NotFoundComponent }
+  { 
+    path: '', 
+    component: HomeComponent,
+    data: {
+      title: 'Fatboar - Jeu-Concours',
+      description: 'Participez à des concours exclusifs pour gagner un Range Rover, des menus, burgers, desserts et réductions.'
+    }
+  },
+  { path: 'login', component: LoginComponent, data: { title: 'Connexion - Fatboar' } },
+  { path: 'login/lost-password', component: LostPasswordComponent, data: { title: 'Mot de passe perdu - Fatboar' } },
+  { path: 'login/reset-password/:token', component: ResetPasswordComponent, data: { title: 'Réinitialisation du mot de passe - Fatboar' } },
+  { path: 'register', component: RegisterComponent, data: { title: 'Inscription - Fatboar' } },
+  { path: 'cgu', component: CguComponent, data: { title: 'CGU - Fatboar' } },
+  { path: 'mention-legales', component: MentionsLegalesComponent, data: { title: 'Mentions légales - Fatboar' } },
+  { path: 'politique-de-confidentialite', component: PolitiqueConfidentialiteComponent, data: { title: 'Politique de confidentialité - Fatboar' } },
+  { path: 'admin', component: AdminLoginComponent, data: { title: 'Admin - Fatboar' } },
+  { path: 'users', component: UsersComponent, data: { title: 'Utilisateurs - Fatboar' } },
+  { path: 'statistique', component: StatisticComponent, canActivate: [AuthGuard], data: { roles: ['admin'], title: 'Statistiques - Fatboar' } },
+  { path: 'emailing', component: EmailingComponent, canActivate: [AuthGuard], data: { roles: ['admin'], title: 'Emailing - Fatboar' } },
+  { path: 'assistance', component: AssistanceComponent, canActivate: [AuthGuard], data: { roles: ['admin'], title: 'Assistance - Fatboar' } },
+  { path: 'contact', component: ContactComponent, data: { title: 'Contact - Fatboar' } },
+  { path: 'home', component: HomeComponent, data: { title: 'Accueil - Fatboar' } },
+  { path: 'auth', component: GainComponent, data: { title: 'Jeux & Gains - Fatboar' } },
+  { path: 'tirage', component: TirageComponent, data: { title: 'Tirage - Fatboar' } },
+  { path: 'profil', component: ProfilComponent, data: { title: 'Profil - Fatboar' } },
+  { path: '**', component: NotFoundComponent, data: { title: '404 - Page non trouvée - Fatboar' } }
 ];
 
 @NgModule({
   imports: [RouterModule.forRoot(routes, {
-    initialNavigation: 'enabled'
-})],
+    initialNavigation: 'enabled' // Important pour SSR
+  })],
   exports: [RouterModule]
 })
 export class AppRoutingModule { }
 
+// import { ResetPasswordComponent } from './components/login/reset-password/reset-password.component';
+// import { LostPasswordComponent } from './components/login/lost-password/lost-password.component';
+// import { AssistanceComponent } from './components/admin/assistance/assistance.component';
+// import { PolitiqueConfidentialiteComponent } from './components/shared/politique-confidentialite/politique-confidentialite.component';
+// import { MentionsLegalesComponent } from './components/shared/mentions-legales/mentions-legales.component';
+// import { CguComponent } from './components/shared/cgu/cgu.component';
+// import { EmailingComponent } from './components/admin/emailing/emailing.component';
+// import { StatisticComponent } from './components/admin/statistic/statistic.component';
+// import { ProfilComponent } from './components/auth/profil/profil.component';
+// import { GainComponent } from './components/auth/gain/gain.component';
+// import { UsersComponent } from './components/admin/users/users.component';
+// import { NotFoundComponent } from './components/shared/not-found/not-found.component';
+// import { NgModule } from '@angular/core';
+// import { Routes, RouterModule } from '@angular/router';
+// import { LoginComponent } from 'src/app/components/login/login.component';
+// import { RegisterComponent } from 'src/app/components/register/register.component';
+// //import { NavbarComponent } from 'src/app/components/shared/navbar/navbar.component';
+// import { ContactComponent } from 'src/app/components/shared/contact/contact.component';
+// import { HomeComponent } from './components/home/home.component';
+// import { AuthGuard } from './guards/auth.guard';
+// import { AdminLoginComponent } from './components/admin/admin-login/admin-login.component';
+// import { TirageComponent } from './components/auth/tirage/tirage.component';
+
+// const routes: Routes = [
+//   { path: '', component: HomeComponent},
+//   { path: 'login', component: LoginComponent },
+//   { path: 'login/lost-password', component: LostPasswordComponent},
+//   { path: 'login/reset-password/:token', component: ResetPasswordComponent},
+//   { path: 'register', component: RegisterComponent},
+//   { path: 'cgu', component: CguComponent},
+//   { path: 'mention-legales', component: MentionsLegalesComponent},
+//   { path: 'politique-de-confidentialite', component: PolitiqueConfidentialiteComponent},
+//   { path: 'admin', component: AdminLoginComponent },
+
+//   { path: 'users', component: UsersComponent },
+//   { path: 'statistique', component: StatisticComponent, canActivate: [AuthGuard], data: {roles: ['admin'] }},
+//   { path: 'emailing', component: EmailingComponent,  canActivate: [AuthGuard], data: {roles: ['admin'] }},
+//   { path: 'assistance', component: AssistanceComponent, canActivate: [AuthGuard], data: {roles: ['admin'] }},
+//   { path: 'contact', component: ContactComponent },
+//   { path: 'home', component: HomeComponent },
+//   { path: 'auth', component: GainComponent},
+//   { path: 'tirage', component: TirageComponent},
+//   { path: 'profil', component: ProfilComponent },
+//   { path: '**', component: NotFoundComponent }
+// ];
+
+// @NgModule({
+//   imports: [RouterModule.forRoot(routes, {
+//     initialNavigation: 'enabled'
+// })],
+//   exports: [RouterModule]
+// })
+// export class AppRoutingModule { }
+

+ 26 - 18
angular-client/src/app/app.component.ts

@@ -1,15 +1,13 @@
 import { Component, OnInit } from '@angular/core';
-import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
+import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
 import { SeoService } from './services/seo.service';
 import { filter, map, mergeMap } from 'rxjs/operators';
 
 @Component({
   selector: 'app-root',
-  templateUrl: './app.component.html',
-  styleUrls: ['./app.component.scss']
+  templateUrl: './app.component.html'
 })
 export class AppComponent implements OnInit {
-  title = 'fatboar';
 
   constructor(
     private router: Router,
@@ -18,18 +16,28 @@ export class AppComponent implements OnInit {
   ) {}
 
   ngOnInit() {
-    this.router.events
-      .pipe(
-        filter(event => event instanceof NavigationEnd),
-        map(() => this.activatedRoute),
-        map(route => {
-          while (route.firstChild) route = route.firstChild;
-          return route;
-        }),
-        mergeMap(route => route.data)
-      )
-      .subscribe(data => {
-        this.seoService.updateMetaTags(data);
-      });
+    // Surveille les changements de route pour mettre à jour le SEO dynamiquement
+    this.router.events.pipe(
+      filter(event => event instanceof NavigationEnd),
+      map(() => {
+        let route = this.activatedRoute;
+        while (route.firstChild) {
+          route = route.firstChild;
+        }
+        return route;
+      }),
+      mergeMap(route => route.data)
+    ).subscribe(data => {
+      // Mise à jour du titre
+      if (data['title']) {
+        this.seoService.updateTitle(data['title']);
+      }
+      // Mise à jour de la meta description
+      if (data['description']) {
+        this.seoService.updateMetaTags([
+          { name: 'description', content: data['description'] }
+        ]);
+      }
+    });
   }
-}
+}

+ 162 - 26
angular-client/src/app/app.module.ts

@@ -1,25 +1,25 @@
-
 import { registerLocaleData } from '@angular/common';
 import localeFr from '@angular/common/locales/fr';
 registerLocaleData(localeFr, 'fr');
-import { ErrorInterceptor } from './interceptors/error.interceptor';
-import { SatDatepickerModule, SatNativeDateModule } from 'saturn-datepicker';
-import { JwtInterceptor } from './interceptors/jwt.interceptor';
-import { BrowserModule } from '@angular/platform-browser';
-import { MatSnackBarModule } from '@angular/material/snack-bar';
+
 import { NgModule, LOCALE_ID } from '@angular/core';
+import { BrowserModule, Meta, Title } from '@angular/platform-browser'; // <-- Ajouté Meta, Title
 import { AppRoutingModule } from './app-routing.module';
 import { AppComponent } from './app.component';
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
 import { MaterialModule } from './material/material.module';
 import { FlexLayoutModule } from "@angular/flex-layout";
-import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
 import { ChartsModule } from 'ng2-charts';
-import { FormsModule } from '@angular/forms';
-// import { FlexLayoutServerModule } from '@angular/flex-layout/server';
-import { ReactiveFormsModule } from '@angular/forms';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { MatSnackBarModule } from '@angular/material/snack-bar';
+import { SatDatepickerModule, SatNativeDateModule } from 'saturn-datepicker';
+
+// Interceptors
+import { JwtInterceptor } from './interceptors/jwt.interceptor';
+// import { ErrorInterceptor } from './interceptors/error.interceptor';
 
-//components
+// Components (liste inchangée)
 import { LoginComponent } from './components/login/login.component';
 import { NavbarComponent } from './components/shared/navbar/navbar.component';
 import { FooterComponent } from './components/shared/footer/footer.component';
@@ -59,12 +59,10 @@ import { ResetPasswordComponent } from './components/login/reset-password/reset-
 import { ConfirmMessageComponent } from './components/shared/confirm-message/confirm-message.component';
 import { ResponseContactComponent } from './components/admin/assistance/response-contact/response-contact.component';
 import { AdminLoginComponent } from './components/admin/admin-login/admin-login.component';
-import { MatSnackBarRef } from '@angular/material/snack-bar';
 import { TirageComponent } from './components/auth/tirage/tirage.component';
 
-
-
-
+// Ajoute ton service SEO si tu l'as créé
+import { SeoService } from './services/seo.service'; // <-- à adapter selon ton arborescence
 
 @NgModule({
   declarations: [
@@ -108,12 +106,10 @@ import { TirageComponent } from './components/auth/tirage/tirage.component';
     ResetPasswordComponent,
     ConfirmMessageComponent,
     AdminLoginComponent,
-    TirageComponent,
-    
-    
+    TirageComponent
   ],
-  imports: [ 
-    BrowserModule.withServerTransition({ appId: 'serverApp' }),
+  imports: [
+    BrowserModule.withServerTransition({ appId: 'serverApp' }), // SSR ready
     AppRoutingModule,
     BrowserAnimationsModule,
     HttpClientModule,
@@ -122,17 +118,157 @@ import { TirageComponent } from './components/auth/tirage/tirage.component';
     ChartsModule,
     FormsModule,
     MatSnackBarModule,
-    ReactiveFormsModule, 
-    SatDatepickerModule, 
-    // FlexLayoutServerModule,
-    SatNativeDateModule,
-
+    ReactiveFormsModule,
+    SatDatepickerModule,
+    SatNativeDateModule
+    // FlexLayoutServerModule, // décommenter si besoin pour SSR
   ],
-  providers: [ 
+  providers: [
     { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
     // { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
     { provide: LOCALE_ID, useValue: 'fr-FR' },
+    Meta,   // <-- Ajouté pour SEO dynamique
+    Title,  // <-- Ajouté pour SEO dynamique
+    SeoService // <-- Ajouté si tu utilises un service SEO personnalisé
   ],
   bootstrap: [AppComponent]
 })
 export class AppModule { }
+
+// import { registerLocaleData } from '@angular/common';
+// import localeFr from '@angular/common/locales/fr';
+// registerLocaleData(localeFr, 'fr');
+// import { ErrorInterceptor } from './interceptors/error.interceptor';
+// import { SatDatepickerModule, SatNativeDateModule } from 'saturn-datepicker';
+// import { JwtInterceptor } from './interceptors/jwt.interceptor';
+// import { BrowserModule } from '@angular/platform-browser';
+// import { MatSnackBarModule } from '@angular/material/snack-bar';
+// import { NgModule, LOCALE_ID } from '@angular/core';
+// import { AppRoutingModule } from './app-routing.module';
+// import { AppComponent } from './app.component';
+// import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+// import { MaterialModule } from './material/material.module';
+// import { FlexLayoutModule } from "@angular/flex-layout";
+// import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
+// import { ChartsModule } from 'ng2-charts';
+// import { FormsModule } from '@angular/forms';
+// // import { FlexLayoutServerModule } from '@angular/flex-layout/server';
+// import { ReactiveFormsModule } from '@angular/forms';
+
+// //components
+// import { LoginComponent } from './components/login/login.component';
+// import { NavbarComponent } from './components/shared/navbar/navbar.component';
+// import { FooterComponent } from './components/shared/footer/footer.component';
+// import { StatisticComponent } from './components/admin/statistic/statistic.component';
+// import { AlertMessageComponent } from './components/shared/alert-message/alert-message.component';
+// import { RegisterComponent } from './components/register/register.component';
+// import { ContactComponent } from './components/shared/contact/contact.component';
+// import { MapComponent } from './components/shared/contact/map/map.component';
+// import { FormulaireComponent } from './components/shared/contact/formulaire/formulaire.component';
+// import { InformationComponent } from './components/shared/contact/information/information.component';
+// import { UsersComponent } from './components/admin/users/users.component';
+// import { UsersListComponent } from './components/admin/users/users-list/users-list.component';
+// import { UsersDetailsComponent } from './components/admin/users/users-details/users-details.component';
+// import { UsersEditComponent } from './components/admin/users/users-edit/users-edit.component';
+// import { UsersDeleteComponent } from './components/admin/users/users-delete/users-delete.component';
+// import { ConfirmComponent } from './components/shared/contact/formulaire/confirm/confirm.component';
+// import { NotFoundComponent } from './components/shared/not-found/not-found.component';
+// import { HomeComponent } from './components/home/home.component';
+// import { GainComponent } from './components/auth/gain/gain.component';
+// import { GainListComponent } from './components/auth/gain/gain-list/gain-list.component';
+// import { GainFormComponent } from './components/auth/gain/gain-form/gain-form.component';
+// import { MentionsLegalesComponent } from './components/shared/mentions-legales/mentions-legales.component';
+// import { PolitiqueConfidentialiteComponent } from './components/shared/politique-confidentialite/politique-confidentialite.component';
+// import { CguComponent } from './components/shared/cgu/cgu.component';
+// import { UsersFormComponent } from './components/admin/users/users-form/users-form.component';
+// import { EmailingComponent } from './components/admin/emailing/emailing.component';
+// import { ProfilComponent } from './components/auth/profil/profil.component';
+// import { EmailFormComponent } from './components/admin/emailing/email-form/email-form.component';
+// import { LotsComponent } from './components/admin/lots/lots.component';
+// import { EditProfilInfoConnexionComponent } from './components/auth/profil/edit-profil-info-connexion/edit-profil-info-connexion.component';
+// import { EditProfilInfoUserComponent } from './components/auth/profil/edit-profil-info-user/edit-profil-info-user.component';
+// import { AssistanceComponent } from './components/admin/assistance/assistance.component';
+// import { AssistanceListComponent } from './components/admin/assistance/assistance-list/assistance-list.component';
+// import { UsersBloqueComponent } from './components/admin/users/users-bloque/users-bloque.component';
+// import { LostPasswordComponent } from './components/login/lost-password/lost-password.component';
+// import { ResetPasswordComponent } from './components/login/reset-password/reset-password.component';
+// import { ConfirmMessageComponent } from './components/shared/confirm-message/confirm-message.component';
+// import { ResponseContactComponent } from './components/admin/assistance/response-contact/response-contact.component';
+// import { AdminLoginComponent } from './components/admin/admin-login/admin-login.component';
+// import { MatSnackBarRef } from '@angular/material/snack-bar';
+// import { TirageComponent } from './components/auth/tirage/tirage.component';
+
+
+
+
+
+// @NgModule({
+//   declarations: [
+//     AppComponent,
+//     NavbarComponent,
+//     FooterComponent,
+//     StatisticComponent,
+//     LoginComponent,
+//     AlertMessageComponent,
+//     RegisterComponent,
+//     ContactComponent,
+//     MapComponent,
+//     FormulaireComponent,
+//     InformationComponent,
+//     UsersComponent,
+//     UsersListComponent,
+//     UsersDetailsComponent,
+//     UsersEditComponent,
+//     UsersDeleteComponent,
+//     ConfirmComponent,
+//     NotFoundComponent,
+//     HomeComponent,
+//     GainComponent,
+//     GainListComponent,
+//     GainFormComponent,
+//     MentionsLegalesComponent,
+//     PolitiqueConfidentialiteComponent,
+//     CguComponent,
+//     UsersFormComponent,
+//     EmailingComponent,
+//     ProfilComponent,
+//     EmailFormComponent,
+//     LotsComponent,
+//     EditProfilInfoConnexionComponent,
+//     EditProfilInfoUserComponent,
+//     AssistanceComponent,
+//     AssistanceListComponent,
+//     ResponseContactComponent,
+//     UsersBloqueComponent,
+//     LostPasswordComponent,
+//     ResetPasswordComponent,
+//     ConfirmMessageComponent,
+//     AdminLoginComponent,
+//     TirageComponent,
+    
+    
+//   ],
+//   imports: [ 
+//     BrowserModule.withServerTransition({ appId: 'serverApp' }),
+//     AppRoutingModule,
+//     BrowserAnimationsModule,
+//     HttpClientModule,
+//     MaterialModule,
+//     FlexLayoutModule,
+//     ChartsModule,
+//     FormsModule,
+//     MatSnackBarModule,
+//     ReactiveFormsModule, 
+//     SatDatepickerModule, 
+//     // FlexLayoutServerModule,
+//     SatNativeDateModule,
+
+//   ],
+//   providers: [ 
+//     { provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
+//     // { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
+//     { provide: LOCALE_ID, useValue: 'fr-FR' },
+//   ],
+//   bootstrap: [AppComponent]
+// })
+// export class AppModule { }

+ 105 - 7
angular-client/src/app/components/home/home.component.ts

@@ -1,5 +1,7 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core';
 import { DomSanitizer } from '@angular/platform-browser';
+import { SeoService } from 'src/app/services/seo.service';
+import { isPlatformBrowser } from '@angular/common';
 
 const imgRange = 'assets/img/concours/range.png';
 const imgReduction = 'assets/img/concours/70.png';
@@ -48,13 +50,26 @@ export class HomeComponent implements OnInit {
     }
   };
 
-  constructor(private domSanitizer: DomSanitizer) {}
+  constructor(
+    private domSanitizer: DomSanitizer,
+    private seoService: SeoService,
+    @Inject(PLATFORM_ID) private platformId: Object
+  ) {}
 
   ngOnInit(): void {
-    const script = document.createElement('script');
-    script.type = 'application/ld+json';
-    script.text = JSON.stringify(this.jsonLd);
-    document.head.appendChild(script);
+    // SEO dynamique
+    this.seoService.updateTitle('Fatboar - Jeu-Concours');
+    this.seoService.updateMetaTags([
+      { name: 'description', content: this.jsonLd.description }
+    ]);
+
+    // Injection du JSON-LD uniquement côté navigateur (pour éviter les erreurs SSR)
+    if (isPlatformBrowser(this.platformId)) {
+      const script = document.createElement('script');
+      script.type = 'application/ld+json';
+      script.text = JSON.stringify(this.jsonLd);
+      document.head.appendChild(script);
+    }
   }
 
   getImgRange() {
@@ -80,4 +95,87 @@ export class HomeComponent implements OnInit {
   getReduction() {
     return this.domSanitizer.bypassSecurityTrustResourceUrl(imgReduction);
   }
-}
+}
+// import { Component, OnInit } from '@angular/core';
+// import { DomSanitizer } from '@angular/platform-browser';
+
+// const imgRange = 'assets/img/concours/range.png';
+// const imgReduction = 'assets/img/concours/70.png';
+// const imgBurger = 'assets/img/concours/burger2.png';
+// const imgDessert = 'assets/img/concours/dessert5.png';
+// const imgMenuJour = 'assets/img/concours/menu-jour.png';
+// const imgMenuChoix = 'assets/img/concours/menu-choix.png';
+
+// @Component({
+//   selector: 'app-home',
+//   templateUrl: './home.component.html',
+//   styleUrls: ['./home.component.scss']
+// })
+// export class HomeComponent implements OnInit {
+//   jsonLd = {
+//     '@context': 'https://schema.org',
+//     '@type': 'Event',
+//     name: 'Grand Jeu-Concours Fatboar',
+//     description: 'Participez au concours Fatboar pour gagner un Range Rover d’une valeur de 59 900€, des menus, burgers, desserts ou une réduction de 70%.',
+//     startDate: '2020-07-16',
+//     endDate: '2024-09-16',
+//     eventAttendanceMode: 'https://schema.org/OfflineEventAttendanceMode',
+//     eventStatus: 'https://schema.org/EventScheduled',
+//     location: {
+//       '@type': 'Place',
+//       name: 'Restaurants Fatboar',
+//       address: {
+//         '@type': 'PostalAddress',
+//         addressCountry: 'FR'
+//       }
+//     },
+//     image: 'https://angular-preprod.foodgame.fr/assets/img/concours/range.png',
+//     offers: {
+//       '@type': 'Offer',
+//       name: 'Participation au concours',
+//       price: '18',
+//       priceCurrency: 'EUR',
+//       availability: 'https://schema.org/InStock',
+//       validFrom: '2020-07-16',
+//       url: 'https://angular-preprod.foodgame.fr'
+//     },
+//     organizer: {
+//       '@type': 'Organization',
+//       name: 'Fatboar',
+//       url: 'https://angular-preprod.foodgame.fr'
+//     }
+//   };
+
+//   constructor(private domSanitizer: DomSanitizer) {}
+
+//   ngOnInit(): void {
+//     const script = document.createElement('script');
+//     script.type = 'application/ld+json';
+//     script.text = JSON.stringify(this.jsonLd);
+//     document.head.appendChild(script);
+//   }
+
+//   getImgRange() {
+//     return this.domSanitizer.bypassSecurityTrustResourceUrl(imgRange);
+//   }
+
+//   getDessert() {
+//     return this.domSanitizer.bypassSecurityTrustResourceUrl(imgDessert);
+//   }
+
+//   getBurger() {
+//     return this.domSanitizer.bypassSecurityTrustResourceUrl(imgBurger);
+//   }
+
+//   getMenuJour() {
+//     return this.domSanitizer.bypassSecurityTrustResourceUrl(imgMenuJour);
+//   }
+
+//   getMenuChoix() {
+//     return this.domSanitizer.bypassSecurityTrustResourceUrl(imgMenuChoix);
+//   }
+
+//   getReduction() {
+//     return this.domSanitizer.bypassSecurityTrustResourceUrl(imgReduction);
+//   }
+// }

+ 31 - 25
angular-client/src/app/services/seo.service.ts

@@ -1,35 +1,41 @@
+// src/app/services/seo.service.ts
+
 import { Injectable } from '@angular/core';
-import { Meta, Title } from '@angular/platform-browser';
+import { Title, Meta } from '@angular/platform-browser';
 
 @Injectable({
   providedIn: 'root'
 })
 export class SeoService {
-  constructor(private title: Title, private meta: Meta) {}
 
-  updateMetaTags(data: any) {
-    if (data.title) {
-      this.title.setTitle(data.title);
-    }
-    if (data.description) {
-      this.meta.updateTag({ name: 'description', content: data.description });
-    }
-    if (data.keywords) {
-      this.meta.updateTag({ name: 'keywords', content: data.keywords });
-    }
-    if (data.ogTitle) {
-      this.meta.updateTag({ property: 'og:title', content: data.ogTitle });
-    }
-    if (data.ogDescription) {
-      this.meta.updateTag({ property: 'og:description', content: data.ogDescription });
-    }
-    if (data.ogImage) {
-      this.meta.updateTag({ property: 'og:image', content: data.ogImage });
-    }
-    if (data.noIndex) {
-      this.meta.updateTag({ name: 'robots', content: 'noindex, nofollow' });
+  constructor(
+    private titleService: Title,
+    private metaService: Meta
+  ) {}
+
+  updateTitle(title: string): void {
+    this.titleService.setTitle(title);
+  }
+
+  updateDescription(description: string): void {
+    this.metaService.updateTag({ name: 'description', content: description });
+  }
+
+  updateMetaTags(tags: { name: string; content: string }[]): void {
+    tags.forEach(tag => {
+      this.metaService.updateTag(tag);
+    });
+  }
+
+  setCanonical(url: string): void {
+    let link: HTMLLinkElement = document.querySelector("link[rel='canonical']");
+    if (link) {
+      link.setAttribute('href', url);
     } else {
-      this.meta.removeTag("name='robots'");
+      link = document.createElement('link');
+      link.setAttribute('rel', 'canonical');
+      link.setAttribute('href', url);
+      document.head.appendChild(link);
     }
   }
-}
+}

+ 31 - 1
angular-client/src/index.html

@@ -5,6 +5,7 @@
   <title>Fatboar</title>
   <base href="/">
   <meta name="viewport" content="width=device-width, initial-scale=1">
+  <!-- Balises SEO génériques (seront remplacées dynamiquement par Angular SSR) -->
   <meta name="description" content="Fatboar - Participez à des concours exclusifs pour gagner un Range Rover, des menus, burgers, desserts et réductions.">
   <meta name="keywords" content="fatboar, concours, range rover, burgers, menus, réductions, restauration">
   <meta name="robots" content="index, follow">
@@ -25,4 +26,33 @@
 <body class="mat-typography">
   <app-root></app-root>
 </body>
-</html>
+</html>
+
+<!-- <!doctype html>
+<html lang="fr">
+<head>
+  <meta charset="utf-8">
+  <title>Fatboar</title>
+  <base href="/">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <meta name="description" content="Fatboar - Participez à des concours exclusifs pour gagner un Range Rover, des menus, burgers, desserts et réductions.">
+  <meta name="keywords" content="fatboar, concours, range rover, burgers, menus, réductions, restauration">
+  <meta name="robots" content="index, follow">
+  <meta property="og:title" content="Fatboar - Jeu-Concours">
+  <meta property="og:description" content="Tentez votre chance pour gagner un Range Rover et d’autres prix avec Fatboar.">
+  <meta property="og:image" content="https://angular-preprod.foodgame.fr/assets/fatboar-og.jpg">
+  <meta property="og:url" content="https://angular-preprod.foodgame.fr">
+  <meta property="og:type" content="website">
+  <meta name="twitter:card" content="summary_large_image">
+  <meta name="twitter:title" content="Fatboar - Jeu-Concours">
+  <meta name="twitter:description" content="Tentez votre chance pour gagner un Range Rover et d’autres prix avec Fatboar.">
+  <meta name="twitter:image" content="https://angular-preprod.foodgame.fr/assets/fatboar-og.jpg">
+  <link rel="canonical" href="https://angular-preprod.foodgame.fr">
+  <link rel="icon" type="image/x-icon" href="favicon.ico">
+  <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
+  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
+</head>
+<body class="mat-typography">
+  <app-root></app-root>
+</body>
+</html> -->