refacto des pages home, register, login, detail profile
This commit is contained in:
@@ -1,34 +1,52 @@
|
||||
<section class="relative flex justify-center items-center">
|
||||
<div class="">
|
||||
<div class="relative z-10 grid w-full min-h-full py-6 place-content-center">
|
||||
<div class="flex flex-row md:w-[760px] xl:w-[1008px] bg-white rounded-lg overflow-hidden">
|
||||
<section class="hidden md:block md:w-1/2 bg-cover bg-auth">
|
||||
<div class="relative flex items-center justify-center w-full h-full">
|
||||
<div class="w-[300px] mx-auto space-y-4 p-6 text-gray-50">
|
||||
<h2 class="text-2xl font-extrabold text-center">Bon retour</h2>
|
||||
<p class="text-sm leading-tight text-center">
|
||||
Les solutions à tes besoins sont à portée de main.
|
||||
<section class="min-h-screen flex justify-center items-center ">
|
||||
|
||||
<div class=" w-full max-w-6xl animate-fade-in">
|
||||
<div class="flex flex-col md:flex-row bg-white dark:bg-gray-800 rounded-2xl shadow-2xl overflow-hidden">
|
||||
<!-- Panneau gauche - Image de fond -->
|
||||
<section class="hidden md:flex md:w-1/2 bg-cover bg-center bg-auth relative">
|
||||
<!-- Overlay pour améliorer la lisibilité -->
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-indigo-900/40 to-purple-900/40"></div>
|
||||
|
||||
<div class="relative z-10 flex flex-col justify-between w-full h-full p-8">
|
||||
<!-- Contenu principal -->
|
||||
<div class="flex-1 flex items-center justify-center">
|
||||
<div class="text-center space-y-4 animate-slide-up">
|
||||
<h2 class="text-4xl font-bold text-white">Bon retour</h2>
|
||||
<p class="text-lg text-white/90 leading-relaxed max-w-sm mx-auto">
|
||||
Les solutions à vos besoins sont à portée de main.
|
||||
</p>
|
||||
<div class="absolute space-x-2 text-xs font-bold -translate-x-1/2 bottom-4 left-1/2">
|
||||
<a href="">Politiques</a>
|
||||
<a href="">Terms</a>
|
||||
<a href="">Conditions</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer links -->
|
||||
<div class="flex justify-center gap-6 text-sm text-white/80 animate-fade-in-delay">
|
||||
<a href="#" class="hover:text-white transition-colors">Politiques</a>
|
||||
<span>•</span>
|
||||
<a href="#" class="hover:text-white transition-colors">Conditions</a>
|
||||
<span>•</span>
|
||||
<a href="#" class="hover:text-white transition-colors">Aide</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="w-[300px] md:w-1/2 border bg-gray-50">
|
||||
<div class="w-[300px] mx-auto space-y-6 p-6">
|
||||
<div class="text-[#002B2F] text-center w-full mb-6">
|
||||
<a [routerLink]="['']" class="inline-block h-[40px]">
|
||||
<h2 class="text-3xl font-bold">Identifiez-vous</h2>
|
||||
</a>
|
||||
|
||||
<!-- Panneau droit - Formulaire -->
|
||||
<section class="w-full md:w-1/2 bg-gray-50 dark:bg-gray-800">
|
||||
<div class="w-full max-w-md mx-auto p-8 md:p-12 animate-slide-up-delay">
|
||||
|
||||
<!-- Titre -->
|
||||
<div class="text-center mb-8">
|
||||
<h2 class="text-3xl font-bold text-gray-900 dark:text-white">
|
||||
Identifiez-vous
|
||||
</h2>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
Accédez à votre espace
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Formulaire (login ou register) -->
|
||||
<router-outlet />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,129 @@
|
||||
<section class="">
|
||||
<div class="w-full mx-auto px-4 sm:px-6 lg:px-8 pt-24 pb-10">
|
||||
<div class="mt-5 max-w-2xl text-center mx-auto">
|
||||
<h1
|
||||
class="block font-bold text-gray-800 gap-6 dark:text-white text-3xl md:text-5xl lg:text-6xl"
|
||||
>
|
||||
Dans quel secteur se cache votre prochaine
|
||||
<section class="relative min-h-screen flex items-center justify-center overflow-hidden bg-gradient-to-br from-white via-indigo-50 to-purple-50 dark:from-gray-900 dark:via-gray-800 dark:to-gray-900">
|
||||
<!-- Décorations de fond animées -->
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<div class="absolute top-1/4 -left-20 w-72 h-72 bg-indigo-400 rounded-full mix-blend-multiply filter blur-3xl opacity-30 animate-blob"></div>
|
||||
<div class="absolute top-1/3 -right-20 w-96 h-96 bg-purple-400 rounded-full mix-blend-multiply filter blur-3xl opacity-30 animate-blob animation-delay-2000"></div>
|
||||
<div class="absolute bottom-1/4 left-1/2 transform -translate-x-1/2 w-80 h-80 bg-pink-400 rounded-full mix-blend-multiply filter blur-3xl opacity-30 animate-blob animation-delay-4000"></div>
|
||||
</div>
|
||||
|
||||
<!-- Grille de points en arrière-plan (effet moderne) -->
|
||||
<div class="absolute inset-0 opacity-10 dark:opacity-5">
|
||||
<div class="absolute inset-0" style="background-image: radial-gradient(circle, #6366f1 1px, transparent 1px); background-size: 50px 50px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="relative w-full mx-auto px-4 sm:px-6 lg:px-8 pt-20 pb-16 sm:pt-24 sm:pb-20">
|
||||
<!-- Contenu principal -->
|
||||
<div class="max-w-5xl mx-auto">
|
||||
|
||||
<!-- Badge ou tag -->
|
||||
<div class="flex justify-center mb-6 animate-fade-in">
|
||||
<div class="inline-flex items-center gap-2 bg-white dark:bg-gray-800 rounded-full px-4 py-2 shadow-lg border border-indigo-100 dark:border-indigo-900">
|
||||
<span class="relative flex h-2 w-2">
|
||||
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-indigo-400 opacity-75"></span>
|
||||
<span class="relative inline-flex rounded-full h-2 w-2 bg-indigo-500"></span>
|
||||
</span>
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Plateforme de mise en relation professionnelle
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Titre principal avec animation de mots -->
|
||||
<div class="text-center mb-8 sm:mb-12">
|
||||
<h1 class="block font-bold text-gray-900 dark:text-white text-4xl sm:text-5xl md:text-6xl lg:text-7xl leading-tight animate-slide-up">
|
||||
Dans quel secteur se cache
|
||||
<br class="hidden sm:block" />
|
||||
votre prochaine
|
||||
<br />
|
||||
<span class="text-gray-800 dark:text-white">pépites?</span>
|
||||
|
||||
<div class="word-animation max-sm:h-10 max-md:h-11 h-16">
|
||||
<span class="red">Les finances</span>
|
||||
<span class="orange">La Santé</span>
|
||||
<span class="blue">Les Etudes</span>
|
||||
</div>
|
||||
<span class="inline-block mt-2 sm:mt-4">
|
||||
<span class="text-transparent bg-clip-text bg-gradient-to-r from-indigo-600 via-purple-600 to-pink-600 animate-gradient">
|
||||
pépite?
|
||||
</span>
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<!-- Animation des mots qui défilent -->
|
||||
<div class="mt-6 sm:mt-8 flex justify-center animate-slide-up animation-delay-200">
|
||||
<div class="relative inline-block">
|
||||
<div class="word-animation h-12 sm:h-14 md:h-16 lg:h-20 overflow-hidden">
|
||||
<div class="word-container">
|
||||
<span class="word text-red-500 dark:text-red-400">Les finances</span>
|
||||
<span class="word text-orange-500 dark:text-orange-400">La Santé</span>
|
||||
<span class="word text-blue-500 dark:text-blue-400">Les Études</span>
|
||||
<span class="word text-green-500 dark:text-green-400">La Tech</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="absolute inset-0 bg-gradient-to-r from-transparent via-transparent to-transparent pointer-events-none"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 mx-auto w-full space-y-2">
|
||||
<!-- Description -->
|
||||
<div class="text-center mb-8 sm:mb-12 animate-fade-in animation-delay-300">
|
||||
<p class="text-lg sm:text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto leading-relaxed">
|
||||
Connectez-vous avec des professionnels talentueux dans tous les secteurs d'activité
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Barre de recherche -->
|
||||
<div class="max-w-3xl mx-auto mb-12 animate-slide-up animation-delay-400">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl p-4 sm:p-6 hover:shadow-3xl transition-all duration-300 transform hover:scale-[1.02]">
|
||||
<app-search (onSearchChange)="showNewQuery($event)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA ou statistiques -->
|
||||
<div class="flex flex-wrap justify-center gap-4 sm:gap-6 animate-fade-in animation-delay-500">
|
||||
<div class="group bg-white dark:bg-gray-800 rounded-2xl px-6 py-4 shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-indigo-100 dark:bg-indigo-900 rounded-full p-3 group-hover:scale-110 transition-transform duration-300">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-indigo-600 dark:text-indigo-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M9 6a3 3 0 11-6 0 3 3 0 016 0zM17 6a3 3 0 11-6 0 3 3 0 016 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 015 5v1H1v-1a5 5 0 015-5z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-2xl sm:text-3xl font-bold text-indigo-600 dark:text-indigo-400">250+</p>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Profils actifs</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="group bg-white dark:bg-gray-800 rounded-2xl px-6 py-4 shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-purple-100 dark:bg-purple-900 rounded-full p-3 group-hover:scale-110 transition-transform duration-300">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-purple-600 dark:text-purple-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M6 6V5a3 3 0 013-3h2a3 3 0 013 3v1h2a2 2 0 012 2v3.57A22.952 22.952 0 0110 13a22.95 22.95 0 01-8-1.43V8a2 2 0 012-2h2zm2-1a1 1 0 011-1h2a1 1 0 011 1v1H8V5zm1 5a1 1 0 011-1h.01a1 1 0 110 2H10a1 1 0 01-1-1z" clip-rule="evenodd" />
|
||||
<path d="M2 13.692V16a2 2 0 002 2h12a2 2 0 002-2v-2.308A24.974 24.974 0 0110 15c-2.796 0-5.487-.46-8-1.308z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-2xl sm:text-3xl font-bold text-purple-600 dark:text-purple-400">12+</p>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Secteurs</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="group bg-white dark:bg-gray-800 rounded-2xl px-6 py-4 shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-pink-100 dark:bg-pink-900 rounded-full p-3 group-hover:scale-110 transition-transform duration-300">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-pink-600 dark:text-pink-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-2xl sm:text-3xl font-bold text-pink-600 dark:text-pink-400">500+</p>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Projets</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Scroll indicator (optionnel) -->
|
||||
<div class="flex justify-center mt-12 sm:mt-16 animate-bounce animation-delay-600">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-gray-400 dark:text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -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;
|
||||
|
||||
&.red {
|
||||
color: red;
|
||||
// Mixins pour les animations
|
||||
@mixin animation-delay($delay) {
|
||||
animation-delay: $delay;
|
||||
opacity: 0;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&.orange {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
&.blue {
|
||||
color: blue;
|
||||
}
|
||||
// Animation du gradient de fond
|
||||
@keyframes gradient {
|
||||
0%, 100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateSpin {
|
||||
10% {
|
||||
-webkit-transform-style: translateY(-102%);
|
||||
transform: translateY(-102%);
|
||||
// Animation des blobs de fond
|
||||
@keyframes blob {
|
||||
0%, 100% {
|
||||
transform: translate(0, 0) scale(1);
|
||||
}
|
||||
|
||||
25% {
|
||||
-webkit-transform-style: translateY(-100%);
|
||||
transform: translate(20px, -50px) scale(1.1);
|
||||
}
|
||||
50% {
|
||||
transform: translate(-20px, 20px) scale(0.9);
|
||||
}
|
||||
75% {
|
||||
transform: translate(50px, 50px) scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
// 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%);
|
||||
}
|
||||
|
||||
35% {
|
||||
-webkit-transform-style: translateY(-202%);
|
||||
transform: translateY(-202%);
|
||||
}
|
||||
|
||||
50% {
|
||||
-webkit-transform-style: translateY(-200%);
|
||||
50%, 70% {
|
||||
transform: translateY(-200%);
|
||||
}
|
||||
|
||||
60% {
|
||||
-webkit-transform-style: translateY(-302%);
|
||||
transform: translateY(-302%);
|
||||
}
|
||||
|
||||
75% {
|
||||
-webkit-transform-style: translateY(-300%);
|
||||
75%, 95% {
|
||||
transform: translateY(-300%);
|
||||
}
|
||||
|
||||
85% {
|
||||
-webkit-transform-style: translateY(-402%);
|
||||
transform: translateY(-402%);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform-style: translateY(-400%);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shadow custom
|
||||
.shadow-3xl {
|
||||
box-shadow: 0 35px 60px -15px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
&:hover {
|
||||
@apply shadow-xl -translate-y-1;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
<section class="text-gray-600">
|
||||
<div class="container px-5 py-12 mx-auto flex flex-col">
|
||||
<div class="lg:w-4/6 mx-auto">
|
||||
<div class="rounded-lg min-h-56 max-h-64 overflow-hidden bg-cover bg-auth">
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<a [routerLink]="['/profiles']">
|
||||
<section class="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800">
|
||||
<div class="container mx-auto px-4 py-6 md:py-12">
|
||||
<div class="max-w-5xl mx-auto">
|
||||
|
||||
<!-- Header avec bannière et bouton retour -->
|
||||
<div class="relative rounded-2xl overflow-hidden shadow-xl mb-8 animate-fade-in">
|
||||
<div class="h-48 md:h-64 bg-cover bg-center bg-auth relative">
|
||||
<!-- Overlay gradient -->
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-transparent to-black/30"></div>
|
||||
|
||||
<!-- Bouton retour -->
|
||||
<div class="relative z-10 p-4 flex justify-between items-start">
|
||||
<a
|
||||
[routerLink]="['/profiles']"
|
||||
class="group flex items-center justify-center w-10 h-10 md:w-12 md:h-12 bg-white/20 backdrop-blur-md rounded-full hover:bg-white/30 transition-all duration-300 hover:scale-110"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="hover:text-gray-300 text-white size-6 w-5 h-5 sm:w-9 sm:h-9 mx-4 my-4 hover:w-10 hover:h-10"
|
||||
class="w-6 h-6 text-white group-hover:text-white transition-transform group-hover:-translate-x-1"
|
||||
>
|
||||
<title>Retour</title>
|
||||
<path
|
||||
@@ -19,97 +29,161 @@
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<!-- Badge vérifié -->
|
||||
@if (profile().estVerifier) {
|
||||
<div class="flex items-center gap-2 bg-purple-500/20 backdrop-blur-md px-3 py-2 rounded-full animate-pulse-slow">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="hover:text-purple-300 size-6 text-purple-500 w-6 h-6 sm:w-11 sm:h-11 text-end mx-4 my-4"
|
||||
class="w-5 h-5 md:w-6 md:h-6 text-purple-300"
|
||||
>
|
||||
<title>Profile verifier</title>
|
||||
<title>Profil vérifié</title>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M8.603 3.799A4.49 4.49 0 0 1 12 2.25c1.357 0 2.573.6 3.397 1.549a4.49 4.49 0 0 1 3.498 1.307 4.491 4.491 0 0 1 1.307 3.497A4.49 4.49 0 0 1 21.75 12a4.49 4.49 0 0 1-1.549 3.397 4.491 4.491 0 0 1-1.307 3.497 4.491 4.491 0 0 1-3.497 1.307A4.49 4.49 0 0 1 12 21.75a4.49 4.49 0 0 1-3.397-1.549 4.49 4.49 0 0 1-3.498-1.306 4.491 4.491 0 0 1-1.307-3.498A4.49 4.49 0 0 1 2.25 12c0-1.357.6-2.573 1.549-3.397a4.49 4.49 0 0 1 1.307-3.497 4.49 4.49 0 0 1 3.497-1.307Zm7.007 6.387a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-white text-sm font-medium hidden md:inline">Vérifié</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- <img alt="{{user().username}}" class="object-cover object-center h-full w-full" src="https://api.dicebear.com/9.x/adventurer/svg?seed={{user().username}}">-->
|
||||
</div>
|
||||
<div class="flex flex-col sm:flex-row mt-10">
|
||||
<div class="sm:w-1/3 text-center sm:pr-8 sm:py-8">
|
||||
<div
|
||||
class="w-20 h-20 rounded-full inline-flex items-center justify-center bg-gray-200 text-gray-400"
|
||||
>
|
||||
|
||||
<!-- Avatar positionné sur le bord de la bannière -->
|
||||
<div class="relative -mt-16 md:-mt-20 px-4 md:px-8 pb-6">
|
||||
<div class="flex flex-col md:flex-row items-center md:items-end gap-4 md:gap-6">
|
||||
<!-- Avatar avec animation -->
|
||||
<div class="relative group animate-slide-up">
|
||||
<div class="w-28 h-28 md:w-36 md:h-36 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 p-1 shadow-2xl group-hover:scale-105 transition-transform duration-300">
|
||||
<div class="w-full h-full rounded-full overflow-hidden bg-white">
|
||||
@if (user().avatar) {
|
||||
<img
|
||||
alt="{{ user().username }}"
|
||||
class="object-cover object-center h-full w-full rounded-full"
|
||||
class="object-cover w-full h-full"
|
||||
src="{{ environment.baseUrl }}/api/files/users/{{ user().id }}/{{ user().avatar }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
} @else {
|
||||
<img
|
||||
alt="{{ user().username }}"
|
||||
class="object-cover object-center h-full w-full rounded-full"
|
||||
class="object-cover w-full h-full"
|
||||
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{ user().username }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
<div class="flex flex-col items-center text-center justify-center">
|
||||
</div>
|
||||
<!-- Indicateur en ligne (optionnel) -->
|
||||
<div class="absolute bottom-2 right-2 w-4 h-4 md:w-5 md:h-5 bg-green-500 rounded-full border-4 border-white shadow-lg"></div>
|
||||
</div>
|
||||
|
||||
<!-- Nom et profession -->
|
||||
<div class="flex-1 text-center md:text-left mb-4 md:mb-0 animate-slide-up animation-delay-100">
|
||||
@if (user().name) {
|
||||
<h2 class="font-medium title-font mt-4 text-gray-900 text-lg dark:text-white">
|
||||
<h1 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white mb-1">
|
||||
{{ user().name }}
|
||||
</h2>
|
||||
</h1>
|
||||
} @else if (user().username) {
|
||||
<h2 class="font-medium title-font mt-4 text-gray-900 text-lg dark:text-white">
|
||||
<h1 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white mb-1">
|
||||
{{ user().username }}
|
||||
</h2>
|
||||
</h1>
|
||||
} @else {
|
||||
<h2 class="font-medium title-font mt-4 text-gray-900 text-lg dark:text-white">
|
||||
<h1 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white mb-1">
|
||||
{{ user().email }}
|
||||
</h2>
|
||||
}
|
||||
<div class="w-12 h-1 bg-indigo-500 rounded mt-2 mb-4"></div>
|
||||
@if (profile().bio) {
|
||||
<p class="text-base dark:text-white">{{ profile().bio }}</p>
|
||||
} @else {
|
||||
<p class="text-base dark:text-white">
|
||||
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.
|
||||
</p>
|
||||
</h1>
|
||||
}
|
||||
|
||||
<p class="text-lg md:text-xl text-indigo-600 dark:text-indigo-400 font-semibold">
|
||||
{{ profile().profession | uppercase }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contenu principal -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 md:gap-8">
|
||||
|
||||
<!-- Colonne gauche - Informations -->
|
||||
<div class="lg:col-span-1 space-y-6 animate-slide-up animation-delay-200">
|
||||
|
||||
<!-- Card Biographie -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 hover:shadow-xl transition-shadow duration-300">
|
||||
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-3 flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-indigo-500" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-6-3a2 2 0 11-4 0 2 2 0 014 0zm-2 4a5 5 0 00-4.546 2.916A5.986 5.986 0 0010 16a5.986 5.986 0 004.546-2.084A5 5 0 0010 11z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
Biographie
|
||||
</h3>
|
||||
@if (profile().bio) {
|
||||
<p class="text-gray-600 dark:text-gray-300 text-sm leading-relaxed">{{ profile().bio }}</p>
|
||||
} @else {
|
||||
<p class="text-gray-600 dark:text-gray-300 text-sm leading-relaxed">
|
||||
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.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Card Secteur -->
|
||||
@if (profile().secteur) {
|
||||
<div class="space-y-2 flex flex-col my-4">
|
||||
<p class="text-base dark:text-white">Secteur</p>
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 hover:shadow-xl transition-shadow duration-300">
|
||||
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-3 flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-indigo-500" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z" />
|
||||
</svg>
|
||||
Secteur
|
||||
</h3>
|
||||
<app-chips [sectorId]="profile().secteur" />
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Card Réseaux -->
|
||||
@if (profile().reseaux) {
|
||||
<div class="space-y-2 flex flex-col my-4">
|
||||
<p class="text-base dark:text-white">Réseaux</p>
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 hover:shadow-xl transition-shadow duration-300">
|
||||
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-3 flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-indigo-500" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M2 5a2 2 0 012-2h7a2 2 0 012 2v4a2 2 0 01-2 2H9l-3 3v-3H4a2 2 0 01-2-2V5z" />
|
||||
<path d="M15 7v2a4 4 0 01-4 4H9.828l-1.766 1.767c.28.149.599.233.938.233h2l3 3v-3h2a2 2 0 002-2V9a2 2 0 00-2-2h-1z" />
|
||||
</svg>
|
||||
Réseaux
|
||||
</h3>
|
||||
<app-reseaux [reseaux]="profile().reseaux" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="sm:w-2/3 sm:pl-8 sm:py-8 sm:border-l border-gray-200 sm:border-t-0 border-t mt-4 pt-4 sm:mt-0 text-center sm:text-left flex-col flex space-y-2"
|
||||
>
|
||||
<h2 class="text-3xl font-extrabold text-black dark:text-white">
|
||||
{{ profile().profession | uppercase }}
|
||||
</h2>
|
||||
<p class="leading-relaxed text-lg mb-4 dark:text-white">{{ profile().apropos }}</p>
|
||||
|
||||
<!-- Colonne droite - À propos et Projets -->
|
||||
<div class="lg:col-span-2 space-y-6 animate-slide-up animation-delay-300">
|
||||
|
||||
<!-- Card À propos -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 md:p-8 hover:shadow-xl transition-shadow duration-300">
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-indigo-500" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
À propos
|
||||
</h2>
|
||||
<p class="text-gray-700 dark:text-gray-300 leading-relaxed text-base">
|
||||
{{ profile().apropos }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Card Projets -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 md:p-8 hover:shadow-xl transition-shadow duration-300">
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white mb-6 flex items-center gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-indigo-500" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M2 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" />
|
||||
</svg>
|
||||
Projets
|
||||
</h2>
|
||||
<app-project-list [userProjectId]="profile().utilisateur" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -1,19 +1,58 @@
|
||||
<section class="pb-10 relative">
|
||||
<div
|
||||
class="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8 pt-24 pb-10 flex sm:flex-row flex-col space-y-2 items-center sm:space-x-4"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<app-search />
|
||||
<section class="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 pb-16">
|
||||
<!-- Hero Section avec recherche -->
|
||||
<div class="relative overflow-hidden">
|
||||
<!-- Décorations de fond -->
|
||||
<div class="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<div class="absolute -top-40 -right-40 w-80 h-80 bg-indigo-400 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob"></div>
|
||||
<div class="absolute -bottom-40 -left-40 w-80 h-80 bg-purple-400 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob animation-delay-2000"></div>
|
||||
<div class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-80 h-80 bg-pink-400 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-blob animation-delay-4000"></div>
|
||||
</div>
|
||||
|
||||
<div class="relative max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8 pt-24 pb-12">
|
||||
<!-- Titre de la page -->
|
||||
<div class="text-center mb-8 animate-fade-in">
|
||||
<h1 class="text-3xl sm:text-4xl lg:text-5xl font-extrabold text-gray-900 dark:text-white mb-4">
|
||||
Découvrez les
|
||||
<span class="text-transparent bg-clip-text bg-gradient-to-r from-indigo-600 to-purple-600">
|
||||
meilleurs profils
|
||||
</span>
|
||||
</h1>
|
||||
<p class="text-lg text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
|
||||
Explorez notre communauté de talents et trouvez le profil parfait pour votre projet
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Barre de recherche améliorée -->
|
||||
<div class="max-w-3xl mx-auto animate-slide-up animation-delay-200">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-2xl shadow-xl p-4 sm:p-6 hover:shadow-2xl transition-shadow duration-300">
|
||||
<app-search (onSearchChange)="showNewQuery($event)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-w-6xl mx-auto px-4">
|
||||
<!-- Liste des profils -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mt-8">
|
||||
@if (loading().isLoading) {
|
||||
<div class="flex justify-center items-center h-96">
|
||||
<p>Chargement...</p>
|
||||
</div>
|
||||
<app-loading message="Chargement des profils..."/>
|
||||
} @else {
|
||||
<!-- Titre de section -->
|
||||
<div class="mb-6 flex items-center justify-between animate-fade-in">
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Tous les profils
|
||||
</h2>
|
||||
<div class="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M9 6a3 3 0 11-6 0 3 3 0 016 0zM17 6a3 3 0 11-6 0 3 3 0 016 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 015 5v1H1v-1a5 5 0 015-5z" />
|
||||
</svg>
|
||||
<span>{{ profiles().length }} profil(s)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Liste des profils avec animation d'apparition -->
|
||||
<div class="animate-slide-up animation-delay-100">
|
||||
<app-vertical-profile-list [profiles]="profiles()" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 } });
|
||||
}
|
||||
}
|
||||
|
||||
10
src/app/shared/components/loading/loading.component.html
Normal file
10
src/app/shared/components/loading/loading.component.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!-- État de chargement amélioré -->
|
||||
<div class="flex flex-col items-center justify-center h-96 space-y-4">
|
||||
<div class="relative">
|
||||
<div class="w-16 h-16 border-4 border-indigo-200 dark:border-indigo-800 rounded-full animate-spin border-t-indigo-600 dark:border-t-indigo-400"></div>
|
||||
<div class="absolute inset-0 w-16 h-16 border-4 border-transparent border-t-purple-600 dark:border-t-purple-400 rounded-full animate-spin-slow"></div>
|
||||
</div>
|
||||
<p class="text-lg text-gray-600 dark:text-gray-300 font-medium animate-pulse">
|
||||
{{message}}
|
||||
</p>
|
||||
</div>
|
||||
23
src/app/shared/components/loading/loading.component.spec.ts
Normal file
23
src/app/shared/components/loading/loading.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoadingComponent } from './loading.component';
|
||||
|
||||
describe('LoadingComponent', () => {
|
||||
let component: LoadingComponent;
|
||||
let fixture: ComponentFixture<LoadingComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [LoadingComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LoadingComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
12
src/app/shared/components/loading/loading.component.ts
Normal file
12
src/app/shared/components/loading/loading.component.ts
Normal file
@@ -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...';
|
||||
}
|
||||
@@ -1,107 +1,116 @@
|
||||
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()" class="w-full space-y-4">
|
||||
<div class="">
|
||||
<div class="relative">
|
||||
<span class="absolute block w-3 h-3 -translate-y-1/2 top-1/2 left-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M256 64C150 64 64 150 64 256s86 192 192 192c17.7 0 32 14.3 32 32s-14.3 32-32 32C114.6 512 0 397.4 0 256S114.6 0 256 0S512 114.6 512 256v32c0 53-43 96-96 96c-29.3 0-55.6-13.2-73.2-33.9C320 371.1 289.5 384 256 384c-70.7 0-128-57.3-128-128s57.3-128 128-128c27.9 0 53.7 8.9 74.7 24.1c5.7-5 13.1-8.1 21.3-8.1c17.7 0 32 14.3 32 32v80 32c0 17.7 14.3 32 32 32s32-14.3 32-32V256c0-106-86-192-192-192zm64 192a64 64 0 1 0 -128 0 64 64 0 1 0 128 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()" class="space-y-4">
|
||||
|
||||
<!-- Champ Email -->
|
||||
<div class="space-y-2">
|
||||
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Adresse email
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"/>
|
||||
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
formControlName="email"
|
||||
placeholder="email"
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
class="w-full h-10 px-5 py-2 text-xs bg-transparent border rounded-md"
|
||||
placeholder="votre@email.com"
|
||||
class="w-full pl-10 pr-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all"
|
||||
/>
|
||||
</div>
|
||||
@if (loginForm.get('email')?.invalid && loginForm.get('email')?.touched) {
|
||||
<p class="text-xs text-red-500 mt-1">Veuillez entrer une adresse email valide</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<div class="text-right">
|
||||
<!-- Champ Mot de passe -->
|
||||
<div class="space-y-2">
|
||||
<label for="password" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Mot de passe
|
||||
</label>
|
||||
<div class="relative">
|
||||
<span class="absolute block w-3 h-3 -translate-y-1/2 top-1/2 left-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 0 448 512"
|
||||
>
|
||||
<path
|
||||
d="M144 144v48H304V144c0-44.2-35.8-80-80-80s-80 35.8-80 80zM80 192V144C80 64.5 144.5 0 224 0s144 64.5 144 144v48h16c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V256c0-35.3 28.7-64 64-64H80z"
|
||||
/>
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<input
|
||||
#pwdInput
|
||||
formControlName="password"
|
||||
autocomplete="on"
|
||||
placeholder="mot de passe"
|
||||
type="password"
|
||||
id="password"
|
||||
name=""
|
||||
class="w-full h-10 px-5 py-2 text-xs bg-transparent border rounded-md"
|
||||
name="password"
|
||||
placeholder="••••••••"
|
||||
autocomplete="current-password"
|
||||
class="w-full pl-10 pr-12 py-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all"
|
||||
/>
|
||||
<button
|
||||
class="absolute w-4 h-4 -translate-y-1/2 right-1 top-1/2 togglePasswordVisibility"
|
||||
type="button"
|
||||
(click)="pwdInput.type = pwdInput.type === 'password' ? 'text' : 'password'"
|
||||
class="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"
|
||||
>
|
||||
<span class="inline-block w-4 h-4">
|
||||
@if (pwdInput.type === 'password') {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 0 21 21"
|
||||
>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M2 10.5Q5.805 16 10.5 16t8.5-5.5M4.5 13.423l-2 2.077m14-2.077l2 2.077m-6 .5l1 2.5m-5-2.5l-1 2.5"
|
||||
/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z"/>
|
||||
<path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
} @else {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 -960 960 960"
|
||||
>
|
||||
<path
|
||||
d="M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Zm0-300Zm0 220q113 0 207.5-59.5T832-500q-50-101-144.5-160.5T480-720q-113 0-207.5 59.5T128-500q50 101 144.5 160.5T480-280Z"
|
||||
/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M3.707 2.293a1 1 0 00-1.414 1.414l14 14a1 1 0 001.414-1.414l-1.473-1.473A10.014 10.014 0 0019.542 10C18.268 5.943 14.478 3 10 3a9.958 9.958 0 00-4.512 1.074l-1.78-1.781zm4.261 4.26l1.514 1.515a2.003 2.003 0 012.45 2.45l1.514 1.514a4 4 0 00-5.478-5.478z" clip-rule="evenodd"/>
|
||||
<path d="M12.454 16.697L9.75 13.992a4 4 0 01-3.742-3.741L2.335 6.578A9.98 9.98 0 00.458 10c1.274 4.057 5.065 7 9.542 7 .847 0 1.669-.105 2.454-.303z"/>
|
||||
</svg>
|
||||
}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<a [routerLink]="['']" class="text-xs">Mot de passe oublié?</a>
|
||||
</div>
|
||||
@if (loginForm.get('password')?.invalid && loginForm.get('password')?.touched) {
|
||||
<p class="text-xs text-red-500 mt-1">Le mot de passe est requis</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<button class="text-xs py-2 px-4 border bg-[#002B2F] w-1/2 text-center text-gray-50 rounded-md">
|
||||
Se connecter
|
||||
</button>
|
||||
<!-- Mot de passe oublié -->
|
||||
<div class="flex justify-end">
|
||||
<a [routerLink]="['/forgot-password']" class="text-sm text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300 font-medium transition-colors">
|
||||
Mot de passe oublié ?
|
||||
</a>
|
||||
</div>
|
||||
<div class="">
|
||||
<p class="text-xs font-light text-center">
|
||||
|
||||
<!-- Bouton de connexion -->
|
||||
<button
|
||||
type="submit"
|
||||
[disabled]="loginForm.invalid || isLoading()"
|
||||
class="w-full py-3 px-4 bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-700 hover:to-purple-700 text-white font-medium rounded-lg shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none"
|
||||
>
|
||||
@if (isLoading()) {
|
||||
<span class="flex items-center justify-center gap-2">
|
||||
<svg class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Connexion en cours...
|
||||
</span>
|
||||
} @else {
|
||||
Se connecter
|
||||
}
|
||||
</button>
|
||||
|
||||
<!-- Lien vers l'inscription -->
|
||||
<div class="text-center">
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
Vous n'avez pas de compte ?
|
||||
<a [routerLink]="['register']" class="font-bold">Créez-en ici</a>
|
||||
<a [routerLink]="['register']" class="text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300 font-semibold transition-colors">
|
||||
Créez-en un ici
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<!-- Progress bar -->
|
||||
@if (isLoading()) {
|
||||
<ng-container>
|
||||
<p-progressBar mode="indeterminate" [style]="{ height: '6px' }" />
|
||||
</ng-container>
|
||||
<div class="mt-6">
|
||||
<p-progressBar mode="indeterminate" [style]="{ height: '4px' }" />
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,174 +1,180 @@
|
||||
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()" class="w-full space-y-4">
|
||||
<div class="">
|
||||
<div class="relative">
|
||||
<span class="absolute block w-3 h-3 -translate-y-1/2 top-1/2 left-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M256 64C150 64 64 150 64 256s86 192 192 192c17.7 0 32 14.3 32 32s-14.3 32-32 32C114.6 512 0 397.4 0 256S114.6 0 256 0S512 114.6 512 256v32c0 53-43 96-96 96c-29.3 0-55.6-13.2-73.2-33.9C320 371.1 289.5 384 256 384c-70.7 0-128-57.3-128-128s57.3-128 128-128c27.9 0 53.7 8.9 74.7 24.1c5.7-5 13.1-8.1 21.3-8.1c17.7 0 32 14.3 32 32v80 32c0 17.7 14.3 32 32 32s32-14.3 32-32V256c0-106-86-192-192-192zm64 192a64 64 0 1 0 -128 0 64 64 0 1 0 128 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()" class="space-y-4">
|
||||
|
||||
<!-- Champ Email -->
|
||||
<div class="space-y-2">
|
||||
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Adresse email
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z"/>
|
||||
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
formControlName="email"
|
||||
placeholder="email"
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
class="w-full h-10 px-5 py-2 text-xs bg-transparent border rounded-md"
|
||||
placeholder="votre@email.com"
|
||||
class="w-full pl-10 pr-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all"
|
||||
/>
|
||||
</div>
|
||||
@if (registerForm.get('email')?.invalid && registerForm.get('email')?.touched) {
|
||||
<p class="text-xs text-red-500 mt-1">Veuillez entrer une adresse email valide</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<div class="text-right">
|
||||
<!-- Champ Mot de passe -->
|
||||
<div class="space-y-2">
|
||||
<label for="password" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Mot de passe
|
||||
</label>
|
||||
<div class="relative">
|
||||
<span class="absolute block w-3 h-3 -translate-y-1/2 top-1/2 left-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 0 448 512"
|
||||
>
|
||||
<path
|
||||
d="M144 144v48H304V144c0-44.2-35.8-80-80-80s-80 35.8-80 80zM80 192V144C80 64.5 144.5 0 224 0s144 64.5 144 144v48h16c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V256c0-35.3 28.7-64 64-64H80z"
|
||||
/>
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<input
|
||||
#pwdInput
|
||||
formControlName="password"
|
||||
placeholder="mot de passe"
|
||||
autocomplete="on"
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
class="w-full h-10 px-5 py-2 text-xs bg-transparent border rounded-md"
|
||||
placeholder="••••••••"
|
||||
autocomplete="new-password"
|
||||
class="w-full pl-10 pr-12 py-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all"
|
||||
/>
|
||||
<button
|
||||
class="absolute w-4 h-4 -translate-y-1/2 right-1 top-1/2 togglePasswordVisibility"
|
||||
type="button"
|
||||
(click)="pwdInput.type = pwdInput.type === 'password' ? 'text' : 'password'"
|
||||
class="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"
|
||||
>
|
||||
<span class="inline-block w-4 h-4">
|
||||
@if (pwdInput.type === 'password') {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 0 21 21"
|
||||
>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M2 10.5Q5.805 16 10.5 16t8.5-5.5M4.5 13.423l-2 2.077m14-2.077l2 2.077m-6 .5l1 2.5m-5-2.5l-1 2.5"
|
||||
/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z"/>
|
||||
<path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
} @else {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 -960 960 960"
|
||||
>
|
||||
<path
|
||||
d="M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Zm0-300Zm0 220q113 0 207.5-59.5T832-500q-50-101-144.5-160.5T480-720q-113 0-207.5 59.5T128-500q50 101 144.5 160.5T480-280Z"
|
||||
/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M3.707 2.293a1 1 0 00-1.414 1.414l14 14a1 1 0 001.414-1.414l-1.473-1.473A10.014 10.014 0 0019.542 10C18.268 5.943 14.478 3 10 3a9.958 9.958 0 00-4.512 1.074l-1.78-1.781zm4.261 4.26l1.514 1.515a2.003 2.003 0 012.45 2.45l1.514 1.514a4 4 0 00-5.478-5.478z" clip-rule="evenodd"/>
|
||||
<path d="M12.454 16.697L9.75 13.992a4 4 0 01-3.742-3.741L2.335 6.578A9.98 9.98 0 00.458 10c1.274 4.057 5.065 7 9.542 7 .847 0 1.669-.105 2.454-.303z"/>
|
||||
</svg>
|
||||
}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@if (registerForm.get('password')?.invalid && registerForm.get('password')?.touched) {
|
||||
<p class="text-xs text-red-500 mt-1">Le mot de passe doit contenir au moins 8 caractères</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<div class="text-right">
|
||||
<!-- Champ Confirmation mot de passe -->
|
||||
<div class="space-y-2">
|
||||
<label for="passwordConfirm" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Confirmer le mot de passe
|
||||
</label>
|
||||
<div class="relative">
|
||||
<span class="absolute block w-3 h-3 -translate-y-1/2 top-1/2 left-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 0 448 512"
|
||||
>
|
||||
<path
|
||||
d="M144 144v48H304V144c0-44.2-35.8-80-80-80s-80 35.8-80 80zM80 192V144C80 64.5 144.5 0 224 0s144 64.5 144 144v48h16c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V256c0-35.3 28.7-64 64-64H80z"
|
||||
/>
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<input
|
||||
#pwdInputConfirmation
|
||||
formControlName="passwordConfirm"
|
||||
placeholder="confirme le mot de passe"
|
||||
autocomplete="on"
|
||||
type="password"
|
||||
id="passwordConfirm"
|
||||
name="password"
|
||||
class="w-full h-10 px-5 py-2 text-xs bg-transparent border rounded-md"
|
||||
name="passwordConfirm"
|
||||
placeholder="••••••••"
|
||||
autocomplete="new-password"
|
||||
class="w-full pl-10 pr-12 py-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all"
|
||||
/>
|
||||
<button
|
||||
class="absolute w-4 h-4 -translate-y-1/2 right-1 top-1/2 togglePasswordVisibility"
|
||||
type="button"
|
||||
(click)="
|
||||
pwdInputConfirmation.type =
|
||||
pwdInputConfirmation.type === 'password' ? 'text' : 'password'
|
||||
"
|
||||
(click)="pwdInputConfirmation.type = pwdInputConfirmation.type === 'password' ? 'text' : 'password'"
|
||||
class="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors"
|
||||
>
|
||||
<span class="inline-block w-4 h-4">
|
||||
@if (pwdInputConfirmation.type === 'password') {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 0 21 21"
|
||||
>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M2 10.5Q5.805 16 10.5 16t8.5-5.5M4.5 13.423l-2 2.077m14-2.077l2 2.077m-6 .5l1 2.5m-5-2.5l-1 2.5"
|
||||
/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z"/>
|
||||
<path fill-rule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
} @else {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 -960 960 960"
|
||||
>
|
||||
<path
|
||||
d="M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Zm0-300Zm0 220q113 0 207.5-59.5T832-500q-50-101-144.5-160.5T480-720q-113 0-207.5 59.5T128-500q50 101 144.5 160.5T480-280Z"
|
||||
/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fill-rule="evenodd" d="M3.707 2.293a1 1 0 00-1.414 1.414l14 14a1 1 0 001.414-1.414l-1.473-1.473A10.014 10.014 0 0019.542 10C18.268 5.943 14.478 3 10 3a9.958 9.958 0 00-4.512 1.074l-1.78-1.781zm4.261 4.26l1.514 1.515a2.003 2.003 0 012.45 2.45l1.514 1.514a4 4 0 00-5.478-5.478z" clip-rule="evenodd"/>
|
||||
<path d="M12.454 16.697L9.75 13.992a4 4 0 01-3.742-3.741L2.335 6.578A9.98 9.98 0 00.458 10c1.274 4.057 5.065 7 9.542 7 .847 0 1.669-.105 2.454-.303z"/>
|
||||
</svg>
|
||||
}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@if (registerForm.get('passwordConfirm')?.invalid && registerForm.get('passwordConfirm')?.touched) {
|
||||
<p class="text-xs text-red-500 mt-1">Les mots de passe ne correspondent pas</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="relative">
|
||||
<button
|
||||
type="submit"
|
||||
class="text-xs py-2 px-4 border bg-[#002B2F] w-1/2 text-center text-gray-50 rounded-md"
|
||||
>
|
||||
S'inscrire
|
||||
</button>
|
||||
<!-- Indicateur de force du mot de passe -->
|
||||
@if (registerForm.get('password')!.value; as pwd) {
|
||||
<div class="space-y-2">
|
||||
<div class="flex gap-1">
|
||||
<div class="h-1 flex-1 rounded-full bg-red-500"></div>
|
||||
<div class="h-1 flex-1 rounded-full" [class.bg-yellow-500]="pwd.length >= 8" [class.bg-gray-200]="pwd.length < 8"></div>
|
||||
<div class="h-1 flex-1 rounded-full" [class.bg-green-500]="pwd.length >= 10" [class.bg-gray-200]="pwd.length < 10"></div>
|
||||
</div>
|
||||
<div class="">
|
||||
<p class="text-xs font-light text-center">
|
||||
Vous avez un compte?
|
||||
<a [routerLink]="['/auth']" class="font-bold">Connectez vous ici</a>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
@if (pwd.length < 8) {
|
||||
Mot de passe faible
|
||||
} @else if (pwd.length < 10) {
|
||||
Mot de passe moyen
|
||||
} @else {
|
||||
Mot de passe fort
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Bouton d'inscription -->
|
||||
<button
|
||||
type="submit"
|
||||
[disabled]="registerForm.invalid || isLoading()"
|
||||
class="w-full py-3 px-4 bg-gradient-to-r from-indigo-600 to-purple-600 hover:from-indigo-700 hover:to-purple-700 text-white font-medium rounded-lg shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none"
|
||||
>
|
||||
@if (isLoading()) {
|
||||
<span class="flex items-center justify-center gap-2">
|
||||
<svg class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Inscription en cours...
|
||||
</span>
|
||||
} @else {
|
||||
S'inscrire
|
||||
}
|
||||
</button>
|
||||
|
||||
<!-- Lien vers la connexion -->
|
||||
<div class="text-center">
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">
|
||||
Vous avez déjà un compte ?
|
||||
<a [routerLink]="['/auth']" class="text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300 font-semibold transition-colors">
|
||||
Connectez-vous ici
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Conditions d'utilisation -->
|
||||
<p class="text-xs text-center text-gray-500 dark:text-gray-400">
|
||||
En créant un compte, vous acceptez nos
|
||||
<a href="#" class="text-indigo-600 hover:underline">Conditions d'utilisation</a>
|
||||
et notre
|
||||
<a href="#" class="text-indigo-600 hover:underline">Politique de confidentialité</a>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<!-- Progress bar -->
|
||||
@if (isLoading()) {
|
||||
<ng-container class="my-6">
|
||||
<p-progressBar mode="indeterminate" [style]="{ height: '6px' }" />
|
||||
</ng-container>
|
||||
<div class="mt-6">
|
||||
<p-progressBar mode="indeterminate" [style]="{ height: '4px' }" />
|
||||
</div>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user