diff --git a/src/app/routes/authentification/auth/auth.component.html b/src/app/routes/authentification/auth/auth.component.html index 3c9e231..262a525 100644 --- a/src/app/routes/authentification/auth/auth.component.html +++ b/src/app/routes/authentification/auth/auth.component.html @@ -1,34 +1,52 @@ -
-
-
-
- -
-
- +
- +
+
+ + -
+ + +
+ Politiques + + Conditions + + Aide +
+
+
+ + +
+
+ + +
+

+ Identifiez-vous +

+

+ Accédez à votre espace +

+
+ + + +
+
diff --git a/src/app/routes/authentification/auth/auth.component.scss b/src/app/routes/authentification/auth/auth.component.scss index e69de29..552292d 100644 --- a/src/app/routes/authentification/auth/auth.component.scss +++ b/src/app/routes/authentification/auth/auth.component.scss @@ -0,0 +1,49 @@ +/* Animations simples et subtiles */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.animate-fade-in { + animation: fadeIn 0.6s ease-out; +} + +.animate-slide-up { + animation: slideUp 0.7s ease-out; +} + +.animate-slide-up-delay { + animation: slideUp 0.7s ease-out 0.1s both; +} + +.animate-fade-in-delay { + animation: fadeIn 0.8s ease-out 0.3s both; +} + +/* Amélioration de l'accessibilité */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} diff --git a/src/app/routes/home/home.component.html b/src/app/routes/home/home.component.html index 92fb773..7c39f50 100644 --- a/src/app/routes/home/home.component.html +++ b/src/app/routes/home/home.component.html @@ -1,23 +1,129 @@ -
-
-
-

- Dans quel secteur se cache votre prochaine -
- pépites? +
+ +
+
+
+
+
-
- Les finances - La Santé - Les Etudes + +
+
+
+ +
+ +
+ + +
+
+ + + + + + Plateforme de mise en relation professionnelle +
-

-
+
-
- + +
+

+ Dans quel secteur se cache + + votre prochaine +
+ + + pépite? + + +

+ + +
+
+
+
+ Les finances + La Santé + Les Études + La Tech +
+
+
+
+
+
+ + +
+

+ Connectez-vous avec des professionnels talentueux dans tous les secteurs d'activité +

+
+ + +
+
+ +
+
+ + +
+
+
+
+ + + +
+
+

250+

+

Profils actifs

+
+
+
+ +
+
+
+ + + + +
+
+

12+

+

Secteurs

+
+
+
+ +
+
+
+ + + +
+
+

500+

+

Projets

+
+
+
+
+ + +
+ + + +
diff --git a/src/app/routes/home/home.component.scss b/src/app/routes/home/home.component.scss index 1808d1a..0bcc31e 100644 --- a/src/app/routes/home/home.component.scss +++ b/src/app/routes/home/home.component.scss @@ -1,66 +1,220 @@ -h1 { - .word-animation { - overflow: hidden; +// Variables +$animation-duration: 12s; +$blob-duration: 7s; +$gradient-duration: 3s; - span { - color: #4ec7f3; - display: block; - text-transform: capitalize; - animation: rotateSpin 10s infinite; +// Mixins pour les animations +@mixin animation-delay($delay) { + animation-delay: $delay; + opacity: 0; + animation-fill-mode: forwards; +} - &.red { - color: red; - } +// Animation du gradient de fond +@keyframes gradient { + 0%, 100% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } +} - &.orange { - color: orange; - } +// Animation des blobs de fond +@keyframes blob { + 0%, 100% { + transform: translate(0, 0) scale(1); + } + 25% { + transform: translate(20px, -50px) scale(1.1); + } + 50% { + transform: translate(-20px, 20px) scale(0.9); + } + 75% { + transform: translate(50px, 50px) scale(1.05); + } +} - &.blue { - color: blue; - } +// Animations d'entrée +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +// Animation des mots qui défilent +@keyframes wordSlide { + 0%, 20% { + transform: translateY(0); + } + 25%, 45% { + transform: translateY(-100%); + } + 50%, 70% { + transform: translateY(-200%); + } + 75%, 95% { + transform: translateY(-300%); + } + 100% { + transform: translateY(-400%); + } +} + +// Classes d'animation +.animate-gradient { + background-size: 200% 200%; + animation: gradient $gradient-duration ease infinite; +} + +.animate-blob { + animation: blob $blob-duration infinite; +} + +.animate-fade-in { + animation: fadeIn 0.8s ease-out; +} + +.animate-slide-up { + animation: slideUp 0.8s ease-out; +} + +// Délais d'animation +.animation-delay-200 { + @include animation-delay(0.2s); +} + +.animation-delay-300 { + @include animation-delay(0.3s); +} + +.animation-delay-400 { + @include animation-delay(0.4s); +} + +.animation-delay-500 { + @include animation-delay(0.5s); +} + +.animation-delay-600 { + @include animation-delay(0.6s); +} + +.animation-delay-2000 { + animation-delay: 2s; +} + +.animation-delay-4000 { + animation-delay: 4s; +} + +// Section des mots animés +.word-animation { + position: relative; + overflow: hidden; + + .word-container { + display: flex; + flex-direction: column; + animation: wordSlide $animation-duration infinite; + } + + .word { + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + text-transform: capitalize; + + // Tailles responsive + font-size: 2.5rem; + height: 3rem; + + @media (min-width: 640px) { + font-size: 3rem; + height: 3.5rem; + } + + @media (min-width: 768px) { + font-size: 3.5rem; + height: 4rem; + } + + @media (min-width: 1024px) { + font-size: 4rem; + height: 5rem; + } + + // Variantes de couleur (avec support dark mode) + &.red { + @apply text-red-500 dark:text-red-400; + } + + &.orange { + @apply text-orange-500 dark:text-orange-400; + } + + &.blue { + @apply text-blue-500 dark:text-blue-400; + } + + &.green { + @apply text-green-500 dark:text-green-400; } } } -@keyframes rotateSpin { - 10% { - -webkit-transform-style: translateY(-102%); - transform: translateY(-102%); - } +// Shadow custom +.shadow-3xl { + box-shadow: 0 35px 60px -15px rgba(0, 0, 0, 0.3); +} - 25% { - -webkit-transform-style: translateY(-100%); - transform: translateY(-100%); - } +// Effet de hover sur les cards de statistiques +.stat-card { + @apply bg-white dark:bg-gray-800 rounded-2xl px-6 py-4 shadow-lg; + @apply transition-all duration-300 transform; - 35% { - -webkit-transform-style: translateY(-202%); - transform: translateY(-202%); - } + &:hover { + @apply shadow-xl -translate-y-1; - 50% { - -webkit-transform-style: translateY(-200%); - transform: translateY(-200%); - } - - 60% { - -webkit-transform-style: translateY(-302%); - transform: translateY(-302%); - } - - 75% { - -webkit-transform-style: translateY(-300%); - transform: translateY(-300%); - } - - 85% { - -webkit-transform-style: translateY(-402%); - transform: translateY(-402%); - } - - 100% { - -webkit-transform-style: translateY(-400%); - transform: translateY(-400%); + .icon-container { + @apply scale-110; + } + } +} + +// Amélioration de l'accessibilité +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} + +// Support pour les écrans très larges +@media (min-width: 1536px) { + .word { + font-size: 4.5rem !important; + height: 5.5rem !important; } } diff --git a/src/app/routes/profile/profile-detail/profile-detail.component.html b/src/app/routes/profile/profile-detail/profile-detail.component.html index e259620..4275afe 100644 --- a/src/app/routes/profile/profile-detail/profile-detail.component.html +++ b/src/app/routes/profile/profile-detail/profile-detail.component.html @@ -1,114 +1,188 @@ -
-
-
-
-
- - - Retour - - - +
+
+
- @if (profile().estVerifier) { - +
+
+ +
+ + +
+ - Profile verifier - - + + Retour + + + + + + @if (profile().estVerifier) { +
+ + Profil vérifié + + + +
+ } +
+
+ + +
+
+ +
+
+
+ @if (user().avatar) { + {{ user().username }} + } @else { + {{ user().username }} + } +
+
+ +
+
+ + +
+ @if (user().name) { +

+ {{ user().name }} +

+ } @else if (user().username) { +

+ {{ user().username }} +

+ } @else { +

+ {{ user().email }} +

+ } + +

+ {{ profile().profession | uppercase }} +

+
+
+
+
+ + +
+ + +
+ + +
+

+ + + + Biographie +

+ @if (profile().bio) { +

{{ profile().bio }}

+ } @else { +

+ Je suis sur la plateforme Trouve Ton Profile pour partager mon expertise et mes + compétences. N'hésitez pas à me contacter pour en savoir plus sur mon parcours et + mes domaines d'intervention. +

+ } +
+ + + @if (profile().secteur) { +
+

+ + + + Secteur +

+ +
+ } + + + @if (profile().reseaux) { +
+

+ + + + + Réseaux +

+ +
}
- -
-
-
-
- @if (user().avatar) { - {{ user().username }} - } @else { - {{ user().username }} - } + +
+ + +
+

+ + + + À propos +

+

+ {{ profile().apropos }} +

-
- @if (user().name) { -

- {{ user().name }} -

- } @else if (user().username) { -

- {{ user().username }} -

- } @else { -

- {{ user().email }} -

- } -
- @if (profile().bio) { -

{{ profile().bio }}

- } @else { -

- Je suis sur la plateforme Trouve Ton Profile pour partager mon expertise et mes - compétences. N’hésitez pas à me contacter pour en savoir plus sur mon parcours et - mes domaines d’intervention. -

- } - @if (profile().secteur) { -
-

Secteur

- -
- } - - @if (profile().reseaux) { -
-

Réseaux

- -
- } + +
+

+ + + + Projets +

+
-
-

- {{ profile().profession | uppercase }} -

-

{{ profile().apropos }}

- - -
diff --git a/src/app/routes/profile/profile-detail/profile-detail.component.scss b/src/app/routes/profile/profile-detail/profile-detail.component.scss index e69de29..64e747b 100644 --- a/src/app/routes/profile/profile-detail/profile-detail.component.scss +++ b/src/app/routes/profile/profile-detail/profile-detail.component.scss @@ -0,0 +1,61 @@ +/* Animations personnalisées */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes pulseSlow { + 0%, 100% { + opacity: 1; + } + 50% { + opacity: 0.8; + } +} + +.animate-fade-in { + animation: fadeIn 0.6s ease-out; +} + +.animate-slide-up { + animation: slideUp 0.6s ease-out; +} + +.animation-delay-100 { + animation-delay: 0.1s; + opacity: 0; + animation-fill-mode: forwards; +} + +.animation-delay-200 { + animation-delay: 0.2s; + opacity: 0; + animation-fill-mode: forwards; +} + +.animation-delay-300 { + animation-delay: 0.3s; + opacity: 0; + animation-fill-mode: forwards; +} + +.animate-pulse-slow { + animation: pulseSlow 3s ease-in-out infinite; +} diff --git a/src/app/routes/profile/profile-detail/profile-detail.component.ts b/src/app/routes/profile/profile-detail/profile-detail.component.ts index 5fcbe1c..7ed1bf1 100644 --- a/src/app/routes/profile/profile-detail/profile-detail.component.ts +++ b/src/app/routes/profile/profile-detail/profile-detail.component.ts @@ -1,6 +1,6 @@ import { Component, computed, inject } from '@angular/core'; import { ActivatedRoute, RouterLink } from '@angular/router'; -import {NgOptimizedImage, UpperCasePipe} from '@angular/common'; +import { UpperCasePipe } from '@angular/common'; import { User } from '@app/shared/models/user'; import { ChipsComponent } from '@app/shared/components/chips/chips.component'; import { ReseauxComponent } from '@app/shared/components/reseaux/reseaux.component'; @@ -18,7 +18,6 @@ import { Profile } from '@app/domain/profiles/profile.model'; RouterLink, UpperCasePipe, ProjectListComponent, - NgOptimizedImage, ], templateUrl: './profile-detail.component.html', styleUrl: './profile-detail.component.scss', diff --git a/src/app/routes/profile/profile-list/profile-list.component.html b/src/app/routes/profile/profile-list/profile-list.component.html index 547ea42..f463c04 100644 --- a/src/app/routes/profile/profile-list/profile-list.component.html +++ b/src/app/routes/profile/profile-list/profile-list.component.html @@ -1,19 +1,58 @@ -
-
-
- +
+ +
+ +
+
+
+
+
+ +
+ +
+

+ Découvrez les + + meilleurs profils + +

+

+ Explorez notre communauté de talents et trouvez le profil parfait pour votre projet +

+
+ + +
+
+ +
+
-
+ +
@if (loading().isLoading) { -
-

Chargement...

-
+ } @else { - + +
+

+ Tous les profils +

+
+ + + + {{ profiles().length }} profil(s) +
+
+ + +
+ +
}
diff --git a/src/app/routes/profile/profile-list/profile-list.component.scss b/src/app/routes/profile/profile-list/profile-list.component.scss index e69de29..e9aac65 100644 --- a/src/app/routes/profile/profile-list/profile-list.component.scss +++ b/src/app/routes/profile/profile-list/profile-list.component.scss @@ -0,0 +1,85 @@ +/* Animations blob pour le fond */ +@keyframes blob { + 0%, 100% { + transform: translate(0, 0) scale(1); + } + 25% { + transform: translate(20px, -50px) scale(1.1); + } + 50% { + transform: translate(-20px, 20px) scale(0.9); + } + 75% { + transform: translate(50px, 50px) scale(1.05); + } +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes spinSlow { + to { + transform: rotate(360deg); + } +} + +.animate-blob { + animation: blob 7s infinite; +} + +.animate-fade-in { + animation: fadeIn 0.6s ease-out; +} + +.animate-slide-up { + animation: slideUp 0.6s ease-out; +} + +.animate-spin-slow { + animation: spinSlow 3s linear infinite; +} + +.animation-delay-100 { + animation-delay: 0.1s; + opacity: 0; + animation-fill-mode: forwards; +} + +.animation-delay-200 { + animation-delay: 0.2s; + opacity: 0; + animation-fill-mode: forwards; +} + +.animation-delay-300 { + animation-delay: 0.3s; + opacity: 0; + animation-fill-mode: forwards; +} + +.animation-delay-2000 { + animation-delay: 2s; +} + +.animation-delay-4000 { + animation-delay: 4s; +} diff --git a/src/app/routes/profile/profile-list/profile-list.component.ts b/src/app/routes/profile/profile-list/profile-list.component.ts index 4194f7a..b2863e9 100644 --- a/src/app/routes/profile/profile-list/profile-list.component.ts +++ b/src/app/routes/profile/profile-list/profile-list.component.ts @@ -3,17 +3,21 @@ import { SearchComponent } from '@app/shared/features/search/search.component'; import { VerticalProfileListComponent } from '@app/shared/components/vertical-profile-list/vertical-profile-list.component'; import { UntilDestroy } from '@ngneat/until-destroy'; import { ProfileFacade } from '@app/ui/profiles/profile.facade'; +import {LoadingComponent} from "@app/shared/components/loading/loading.component"; +import {Router} from "@angular/router"; @Component({ selector: 'app-profile-list', standalone: true, - imports: [SearchComponent, VerticalProfileListComponent], + imports: [SearchComponent, VerticalProfileListComponent, LoadingComponent], templateUrl: './profile-list.component.html', styleUrl: './profile-list.component.scss', }) @UntilDestroy() export class ProfileListComponent implements OnInit { private readonly facade = inject(ProfileFacade); + private readonly router = inject(Router); + protected readonly profiles = this.facade.profiles; protected readonly loading = this.facade.loading; protected readonly error = this.facade.error; @@ -21,4 +25,8 @@ export class ProfileListComponent implements OnInit { ngOnInit() { this.facade.load(); } + + showNewQuery(newQuery: string) { + this.router.navigate(['/profiles'], { queryParams: { search: newQuery } }); + } } diff --git a/src/app/shared/components/loading/loading.component.html b/src/app/shared/components/loading/loading.component.html new file mode 100644 index 0000000..e121ae9 --- /dev/null +++ b/src/app/shared/components/loading/loading.component.html @@ -0,0 +1,10 @@ + +
+
+
+
+
+

+ {{message}} +

+
diff --git a/src/app/shared/components/loading/loading.component.scss b/src/app/shared/components/loading/loading.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/shared/components/loading/loading.component.spec.ts b/src/app/shared/components/loading/loading.component.spec.ts new file mode 100644 index 0000000..550e702 --- /dev/null +++ b/src/app/shared/components/loading/loading.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoadingComponent } from './loading.component'; + +describe('LoadingComponent', () => { + let component: LoadingComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LoadingComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LoadingComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/loading/loading.component.ts b/src/app/shared/components/loading/loading.component.ts new file mode 100644 index 0000000..de1b02d --- /dev/null +++ b/src/app/shared/components/loading/loading.component.ts @@ -0,0 +1,12 @@ +import {Component, Input} from '@angular/core'; + +@Component({ + selector: 'app-loading', + standalone: true, + imports: [], + templateUrl: './loading.component.html', + styleUrl: './loading.component.scss' +}) +export class LoadingComponent { + @Input() message: string = 'Chargement...'; +} diff --git a/src/app/shared/features/login/login.component.html b/src/app/shared/features/login/login.component.html index a5cdf20..9eb6bba 100644 --- a/src/app/shared/features/login/login.component.html +++ b/src/app/shared/features/login/login.component.html @@ -1,107 +1,116 @@ -
-
-
- - - - - + + +
+ +
+
+ + + + +
+ @if (loginForm.get('email')?.invalid && loginForm.get('email')?.touched) { +

Veuillez entrer une adresse email valide

+ }
-
-
-
- - - - - - - - + +
+ +
+
+ + +
- Mot de passe oublié? + +
+ @if (loginForm.get('password')?.invalid && loginForm.get('password')?.touched) { +

Le mot de passe est requis

+ }
-
- + + -
-

- Vous n'avez pas de compte? - Créez-en ici + + + + + +

+

+ Vous n'avez pas de compte ? + + Créez-en un ici +

+ + @if (isLoading()) { - - - +
+ +
} diff --git a/src/app/shared/features/register/register.component.html b/src/app/shared/features/register/register.component.html index 9638cef..6dc1e99 100644 --- a/src/app/shared/features/register/register.component.html +++ b/src/app/shared/features/register/register.component.html @@ -1,174 +1,180 @@ -
-
-
- - - - - + + +
+ +
+
+ + + + +
+ @if (registerForm.get('email')?.invalid && registerForm.get('email')?.touched) { +

Veuillez entrer une adresse email valide

+ }
-
-
-
- - - - - - - - + +
+ +
+
+ + +
-
-
- -
-
-
- - - + + - - - -
+ } @else { + + + + + } +
+ @if (registerForm.get('password')?.invalid && registerForm.get('password')?.touched) { +

Le mot de passe doit contenir au moins 8 caractères

+ }
-
- +
+ @if (registerForm.get('passwordConfirm')?.invalid && registerForm.get('passwordConfirm')?.touched) { +

Les mots de passe ne correspondent pas

+ } +
+ + + @if (registerForm.get('password')!.value; as pwd) { +
+
+
+
+
+
+

+ @if (pwd.length < 8) { + Mot de passe faible + } @else if (pwd.length < 10) { + Mot de passe moyen + } @else { + Mot de passe fort + } +

+
+ } + + + -
-
-

- Vous avez un compte? - Connectez vous ici + } + + + +

+

+ Vous avez déjà un compte ? + + Connectez-vous ici +

+ + +

+ En créant un compte, vous acceptez nos + Conditions d'utilisation + et notre + Politique de confidentialité +

+ @if (isLoading()) { - - - +
+ +
}