This commit is contained in:
leroy
2025-04-19 17:12:52 +02:00
parent 2fb15b4a4c
commit 1ee0778e62
14 changed files with 153 additions and 184 deletions

6
public/.htaccess Normal file
View File

@@ -0,0 +1,6 @@
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.html [L]

4
public/robots.txt Normal file
View File

@@ -0,0 +1,4 @@
User-agent: *
Allow: /
Disallow: /*?*
Sitemap: https://johanleroy.fr/sitemap.xml

32
public/sitemap.xml Normal file
View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://johanleroy.fr</loc>
<lastmod>2025-04-19</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://johanleroy.fr/formations</loc>
<lastmod>2025-04-19</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://techos-asso.fr/experience</loc>
<lastmod>2025-04-19</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://techos-asso.fr/projets</loc>
<lastmod>2025-04-19</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
</urlset>

View File

@@ -1,91 +0,0 @@
<div
class="relative flex h-[calc(100vh-2rem)] w-full max-w-[20rem] flex-col rounded-xl bg-white bg-clip-border p-4 text-gray-700 shadow-xl shadow-blue-gray-900/5">
<div routerLink="/" class="p-4 mb-2">
<h5 class="block cursor-pointer font-sans text-xl antialiased font-semibold leading-snug tracking-normal text-blue-gray-900">
YouVideo
</h5>
</div>
<nav class="flex min-w-[240px] flex-col gap-1 p-2 font-sans text-base font-normal text-blue-gray-700">
<div *ngIf="user.id !== 0" class="relative block w-full">
<div role="button"
class="flex items-center w-full p-0 leading-tight transition-all rounded-lg outline-none bg-blue-gray-50/50 text-start text-blue-gray-700 hover:bg-blue-gray-50 hover:bg-opacity-80 hover:text-blue-gray-900 focus:bg-blue-gray-50 focus:bg-opacity-80 focus:text-blue-gray-900 active:bg-blue-gray-50 active:bg-opacity-80 active:text-blue-gray-900">
<button type="button"
class="flex items-center justify-between w-full p-3 font-sans text-xl antialiased font-semibold leading-snug text-left transition-colors border-b-0 select-none border-b-blue-gray-100 text-blue-gray-900 hover:text-blue-gray-900">
<div class="grid mr-4 place-items-center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"
class="w-5 h-5">
<path fill-rule="evenodd"
d="M2.25 2.25a.75.75 0 000 1.5H3v10.5a3 3 0 003 3h1.21l-1.172 3.513a.75.75 0 001.424.474l.329-.987h8.418l.33.987a.75.75 0 001.422-.474l-1.17-3.513H18a3 3 0 003-3V3.75h.75a.75.75 0 000-1.5H2.25zm6.04 16.5l.5-1.5h6.42l.5 1.5H8.29zm7.46-12a.75.75 0 00-1.5 0v6a.75.75 0 001.5 0v-6zm-3 2.25a.75.75 0 00-1.5 0v3.75a.75.75 0 001.5 0V9zm-3 2.25a.75.75 0 00-1.5 0v1.5a.75.75 0 001.5 0v-1.5z"
clip-rule="evenodd"></path>
</svg>
</div>
<p class="block mr-auto font-sans text-base antialiased font-normal leading-relaxed text-blue-gray-900">
Ma bibliothèque
</p>
</button>
</div>
<div class="overflow-hidden">
<div class="block w-full py-1 font-sans text-sm antialiased font-light leading-normal text-gray-700">
<nav class="flex min-w-[240px] flex-col gap-1 p-0 font-sans text-base font-normal text-blue-gray-700">
<div role="button"
class="flex items-center w-full p-3 leading-tight transition-all rounded-lg outline-none text-start hover:bg-blue-gray-50 hover:bg-opacity-80 hover:text-blue-gray-900 focus:bg-blue-gray-50 focus:bg-opacity-80 focus:text-blue-gray-900 active:bg-blue-gray-50 active:bg-opacity-80 active:text-blue-gray-900">
<div class="grid mr-4 place-items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="3"
stroke="currentColor" aria-hidden="true" class="w-5 h-3">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"></path>
</svg>
</div>
Mes vidéos
</div>
<div role="button"
class="flex items-center w-full p-3 leading-tight transition-all rounded-lg outline-none text-start hover:bg-blue-gray-50 hover:bg-opacity-80 hover:text-blue-gray-900 focus:bg-blue-gray-50 focus:bg-opacity-80 focus:text-blue-gray-900 active:bg-blue-gray-50 active:bg-opacity-80 active:text-blue-gray-900">
<div class="grid mr-4 place-items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="3"
stroke="currentColor" aria-hidden="true" class="w-5 h-3">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5"></path>
</svg>
</div>
Mes playlists
</div>
</nav>
</div>
</div>
</div>
<div *ngIf="user.id !== 0" routerLink="/login" role="button"
class="flex cursor-pointer items-center w-full p-3 leading-tight transition-all rounded-lg outline-none text-start hover:bg-blue-gray-50 hover:bg-opacity-80 hover:text-blue-gray-900 focus:bg-blue-gray-50 focus:bg-opacity-80 focus:text-blue-gray-900 active:bg-blue-gray-50 active:bg-opacity-80 active:text-blue-gray-900">
<div class="grid mr-4 place-items-center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"
class="w-5 h-5">
<path fill-rule="evenodd"
d="M18.685 19.097A9.723 9.723 0 0021.75 12c0-5.385-4.365-9.75-9.75-9.75S2.25 6.615 2.25 12a9.723 9.723 0 003.065 7.097A9.716 9.716 0 0012 21.75a9.716 9.716 0 006.685-2.653zm-12.54-1.285A7.486 7.486 0 0112 15a7.486 7.486 0 015.855 2.812A8.224 8.224 0 0112 20.25a8.224 8.224 0 01-5.855-2.438zM15.75 9a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z"
clip-rule="evenodd"></path>
</svg>
</div>
Connexion/Inscription
</div>
<div *ngIf="user.id !== 0" routerLink="/p" role="button"
class="flex cursor-pointer items-center w-full p-3 leading-tight transition-all rounded-lg outline-none text-start hover:bg-blue-gray-50 hover:bg-opacity-80 hover:text-blue-gray-900 focus:bg-blue-gray-50 focus:bg-opacity-80 focus:text-blue-gray-900 active:bg-blue-gray-50 active:bg-opacity-80 active:text-blue-gray-900">
<div class="grid mr-4 place-items-center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"
class="w-5 h-5">
<path fill-rule="evenodd"
d="M18.685 19.097A9.723 9.723 0 0021.75 12c0-5.385-4.365-9.75-9.75-9.75S2.25 6.615 2.25 12a9.723 9.723 0 003.065 7.097A9.716 9.716 0 0012 21.75a9.716 9.716 0 006.685-2.653zm-12.54-1.285A7.486 7.486 0 0112 15a7.486 7.486 0 015.855 2.812A8.224 8.224 0 0112 20.25a8.224 8.224 0 01-5.855-2.438zM15.75 9a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z"
clip-rule="evenodd"></path>
</svg>
</div>
Profile
</div>
<div *ngIf="user.id !== 0" (click)="logout()" role="button"
class="flex cursor-pointer items-center w-full p-3 leading-tight transition-all rounded-lg outline-none text-start hover:bg-blue-gray-50 hover:bg-opacity-80 hover:text-blue-gray-900 focus:bg-blue-gray-50 focus:bg-opacity-80 focus:text-blue-gray-900 active:bg-blue-gray-50 active:bg-opacity-80 active:text-blue-gray-900">
<div class="grid mr-4 place-items-center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"
class="w-5 h-5">
<path fill-rule="evenodd"
d="M12 2.25a.75.75 0 01.75.75v9a.75.75 0 01-1.5 0V3a.75.75 0 01.75-.75zM6.166 5.106a.75.75 0 010 1.06 8.25 8.25 0 1011.668 0 .75.75 0 111.06-1.06c3.808 3.807 3.808 9.98 0 13.788-3.807 3.808-9.98 3.808-13.788 0-3.808-3.807-3.808-9.98 0-13.788a.75.75 0 011.06 0z"
clip-rule="evenodd"></path>
</svg>
</div>
Déconnexion
</div>
</nav>
</div>

View File

@@ -1,63 +0,0 @@
import {Component, OnInit} from '@angular/core';
import {AuthService} from '../../_services/auth.service';
import {Users} from '../../_models/users';
import {catchError, of} from 'rxjs';
import {NgIf} from '@angular/common';
import {Router, RouterLink} from '@angular/router';
@Component({
selector: 'app-sidbar',
imports: [
NgIf,
RouterLink
],
templateUrl: './sidbar.component.html',
styleUrl: './sidbar.component.css'
})
export class SidbarComponent implements OnInit {
user: Users = { id: 0, email: '', firstname: '', lastname: '', password_hash: '', updated_at: new Date(), created_at: new Date() };
constructor(private authService: AuthService, private router: Router) {}
ngOnInit(): void {
this.checkAuthAndLoadUser();
}
checkAuthAndLoadUser(): void {
this.authService.isAuthenticated().subscribe(isAuthenticated => {
if (isAuthenticated) {
this.loadUsers();
} else {
this.user = { id: 0, email: '', firstname: '', lastname: '', password_hash: '', updated_at: new Date(), created_at: new Date() };
}
});
}
loadUsers(): void {
this.authService.getProfil().pipe(
catchError(error => {
console.error('Erreur lors du chargement du profil:', error);
return of(null);
})
).subscribe(users => {
if (users) {
console.log(users);
this.user = users;
}
});
}
logout(): void {
this.authService.logout().subscribe({
next: () => {
this.router.navigate(['/']);
},
error: (err) => {
console.error('Logout failed:', err);
}
});
}
}

8
src/app/_models/seo.ts Normal file
View File

@@ -0,0 +1,8 @@
export interface SEOConfig {
title: string;
description: string;
keywords?: string;
ogTitle?: string;
ogDescription?: string;
ogImage?: string;
}

View File

@@ -1,9 +0,0 @@
export interface Users {
id: number;
email: string;
firstname: string;
lastname: string;
password_hash: string;
updated_at: Date;
created_at: Date;
}

View File

@@ -1,13 +0,0 @@
export interface Videos {
data: Video[];
}
export interface Video {
id: number;
user_id: number;
name: string;
description: string;
link_miniature: string;
link_video: string;
created_at: Date;
updated_at: Date;
}

View File

@@ -0,0 +1,43 @@
import {inject, Injectable} from '@angular/core';
import {Meta, Title} from '@angular/platform-browser';
import {SEOConfig} from '../_models/seo';
@Injectable({
providedIn: 'root'
})
export class SeoService {
private meta = inject(Meta);
private title = inject(Title);
updateSEOMetaTags(config: SEOConfig) {
// Mise à jour du titre
this.title.setTitle(config.title);
// Mise à jour des meta tags standards
this.meta.updateTag({ name: 'description', content: config.description });
if (config.keywords) {
this.meta.updateTag({ name: 'keywords', content: config.keywords });
}
// Mise à jour des meta tags Open Graph
this.meta.updateTag({ property: 'og:title', content: config.ogTitle || config.title });
this.meta.updateTag({ property: 'og:description', content: config.ogDescription || config.description });
if (config.ogImage) {
this.meta.updateTag({ property: 'og:image', content: config.ogImage });
}
// Autres meta tags importants
this.meta.updateTag({ name: 'robots', content: 'index,follow' });
this.meta.updateTag({ name: 'viewport', content: 'width=device-width, initial-scale=1' });
}
createCanonicalLink(url?: string) {
const canURL = url || document.URL;
const link: HTMLLinkElement = document.createElement('link');
link.setAttribute('rel', 'canonical');
link.setAttribute('href', canURL);
document.head.appendChild(link);
}
}

View File

@@ -1,4 +1,5 @@
import { Component } from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {SeoService} from '../../../_services/seo.service';
@Component({
selector: 'app-experience',
@@ -6,6 +7,18 @@ import { Component } from '@angular/core';
templateUrl: './experience.component.html',
styleUrl: './experience.component.css'
})
export class ExperienceComponent {
export class ExperienceComponent implements OnInit {
constructor(private seoService: SeoService) { }
ngOnInit(): void {
this.seoService.updateSEOMetaTags({
title: 'Mes expériences - Portfolio',
description: 'Découvrez mes expériences',
keywords: 'johanleroy, johan, leroy, développeur, portfolio, johan leroy développeur, johan leroy portfolio, johan leroy dev web, johan leroy angular, johan leroy express, développeur web, développeur Angular, développeur Express.js, développeur fullstack, développeur JavaScript, développeur TypeScript, développeur Node.js, développeur REST API, développeur tailwind css, développeur MariaDB, développeur SQL, créer un site web, développeur freelance, portfolio développeur web, expert Angular, développeur application web, intégrateur tailwind, développeur web, freelance développeur\n',
ogImage: 'https://johanleroy.fr/favicon.ico'
});
this.seoService.createCanonicalLink();
}
}

View File

@@ -1,4 +1,5 @@
import { Component } from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {SeoService} from '../../../_services/seo.service';
@Component({
selector: 'app-formations',
@@ -6,6 +7,18 @@ import { Component } from '@angular/core';
templateUrl: './formations.component.html',
styleUrl: './formations.component.css'
})
export class FormationsComponent {
export class FormationsComponent implements OnInit {
constructor(private seoService: SeoService) { }
ngOnInit(): void {
this.seoService.updateSEOMetaTags({
title: 'Mes formations - Portfolio',
description: 'Découvrez mes formations',
keywords: 'johanleroy, johan, leroy, développeur, portfolio, johan leroy développeur, johan leroy portfolio, johan leroy dev web, johan leroy angular, johan leroy express, développeur web, développeur Angular, développeur Express.js, développeur fullstack, développeur JavaScript, développeur TypeScript, développeur Node.js, développeur REST API, développeur tailwind css, développeur MariaDB, développeur SQL, créer un site web, développeur freelance, portfolio développeur web, expert Angular, développeur application web, intégrateur tailwind, développeur web, freelance développeur\n',
ogImage: 'https://johanleroy.fr/favicon.ico'
});
this.seoService.createCanonicalLink();
}
}

View File

@@ -1,5 +1,6 @@
import {Component} from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {RouterLink} from '@angular/router';
import {SeoService} from '../../../_services/seo.service';
@Component({
selector: 'app-home',
@@ -9,6 +10,18 @@ import {RouterLink} from '@angular/router';
templateUrl: './home.component.html',
styleUrl: './home.component.css'
})
export class HomeComponent {
export class HomeComponent implements OnInit {
constructor(private seoService: SeoService) { }
ngOnInit(): void {
this.seoService.updateSEOMetaTags({
title: 'Johan Leroy - Portfolio',
description: 'Découvrez mon portfolio',
keywords: 'johanleroy, johan, leroy, développeur, portfolio, johan leroy développeur, johan leroy portfolio, johan leroy dev web, johan leroy angular, johan leroy express, développeur web, développeur Angular, développeur Express.js, développeur fullstack, développeur JavaScript, développeur TypeScript, développeur Node.js, développeur REST API, développeur tailwind css, développeur MariaDB, développeur SQL, créer un site web, développeur freelance, portfolio développeur web, expert Angular, développeur application web, intégrateur tailwind, développeur web, freelance développeur\n',
ogImage: 'https://johanleroy.fr/favicon.ico'
});
this.seoService.createCanonicalLink();
}
}

View File

@@ -1,4 +1,5 @@
import { Component } from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {SeoService} from '../../../_services/seo.service';
@Component({
selector: 'app-projets',
@@ -6,6 +7,18 @@ import { Component } from '@angular/core';
templateUrl: './projets.component.html',
styleUrl: './projets.component.css'
})
export class ProjetsComponent {
export class ProjetsComponent implements OnInit {
constructor(private seoService: SeoService) { }
ngOnInit(): void {
this.seoService.updateSEOMetaTags({
title: 'Mes projets - Portfolio',
description: 'Découvrez mes projets',
keywords: 'johanleroy, johan, leroy, développeur, portfolio, johan leroy développeur, johan leroy portfolio, johan leroy dev web, johan leroy angular, johan leroy express, développeur web, développeur Angular, développeur Express.js, développeur fullstack, développeur JavaScript, développeur TypeScript, développeur Node.js, développeur REST API, développeur tailwind css, développeur MariaDB, développeur SQL, créer un site web, développeur freelance, portfolio développeur web, expert Angular, développeur application web, intégrateur tailwind, développeur web, freelance développeur\n',
ogImage: 'https://johanleroy.fr/favicon.ico'
});
this.seoService.createCanonicalLink();
}
}