Merge pull request 'refacto' (#2) from refacto into main
Reviewed-on: #2
This commit is contained in:
@@ -14,4 +14,11 @@ module.exports = {
|
||||
'^@app/(.*)$': '<rootDir>/src/app/$1',
|
||||
'^@env/(.*)$': '<rootDir>/src/environments/$1',
|
||||
},
|
||||
// Ajouter cette section pour gérer les warnings Node.js
|
||||
testEnvironmentOptions: {
|
||||
customExportConditions: ['node', 'node-addons'],
|
||||
},
|
||||
|
||||
// Supprimer les warnings de dépréciation
|
||||
setupFiles: ['<rootDir>/suppress-deprecation-warnings.js'],
|
||||
};
|
||||
|
||||
142
package-lock.json
generated
142
package-lock.json
generated
@@ -24,7 +24,6 @@
|
||||
"@fortawesome/free-regular-svg-icons": "^6.4.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||
"@ngneat/until-destroy": "^10.0.0",
|
||||
"angularx-qrcode": "^17.0.1",
|
||||
"express": "^4.18.2",
|
||||
"ng2-pdf-viewer": "^10.3.3",
|
||||
"ngx-toastr": "^17.0.2",
|
||||
@@ -8033,19 +8032,6 @@
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/angularx-qrcode": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/angularx-qrcode/-/angularx-qrcode-17.0.1.tgz",
|
||||
"integrity": "sha512-23ZkkaMVtQU2DvyNDj7kTaZRRwYP6R3Vkoa6NmlfkdO/JACv5ppPKYceq2mG3mxP+7N7FCcIO5gpM67Lspm1hQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"qrcode": "1.5.3",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-colors": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
||||
@@ -8089,6 +8075,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -8803,6 +8790,7 @@
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -9608,15 +9596,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/decimal.js": {
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
||||
@@ -9813,12 +9792,6 @@
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dijkstrajs": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dir-glob": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||
@@ -9972,6 +9945,7 @@
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emojis-list": {
|
||||
@@ -9984,12 +9958,6 @@
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/encode-utf8": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz",
|
||||
"integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
@@ -11001,6 +10969,7 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"locate-path": "^5.0.0",
|
||||
@@ -11235,6 +11204,7 @@
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
@@ -12137,6 +12107,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -14649,6 +14620,7 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"p-locate": "^4.1.0"
|
||||
@@ -15990,6 +15962,7 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"p-limit": "^2.2.0"
|
||||
@@ -16002,6 +15975,7 @@
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"p-try": "^2.0.0"
|
||||
@@ -16057,6 +16031,7 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -16218,6 +16193,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -16481,15 +16457,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/pngjs": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pocketbase": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.21.5.tgz",
|
||||
@@ -17087,76 +17054,6 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/qrcode": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz",
|
||||
"integrity": "sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dijkstrajs": "^1.0.1",
|
||||
"encode-utf8": "^1.0.3",
|
||||
"pngjs": "^5.0.0",
|
||||
"yargs": "^15.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"qrcode": "bin/qrcode"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode/node_modules/cliui": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"wrap-ansi": "^6.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode/node_modules/y18n": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/qrcode/node_modules/yargs": {
|
||||
"version": "15.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cliui": "^6.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^4.1.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^4.2.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^18.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode/node_modules/yargs-parser": {
|
||||
"version": "18.1.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
@@ -17460,6 +17357,7 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -17475,12 +17373,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
@@ -18064,6 +17956,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/set-function-length": {
|
||||
@@ -18577,6 +18470,7 @@
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
@@ -18607,6 +18501,7 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
@@ -20794,12 +20689,6 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/which-module": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
||||
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/wide-align": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
|
||||
@@ -20838,6 +20727,7 @@
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"tsc": "tsc --noEmit",
|
||||
"tsc:watch": "tsc --noEmit --watch",
|
||||
"format": "prettier --write \"src/**/*.{ts,html,scss,css,md,json}\"",
|
||||
"prettier": "prettier --write \"src/**/*.{ts,html,scss,css,md,json}\"",
|
||||
"check:all": "npm run format && npm run tsc && npm run lint && npm run test",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
@@ -36,7 +36,6 @@
|
||||
"@fortawesome/free-regular-svg-icons": "^6.4.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||
"@ngneat/until-destroy": "^10.0.0",
|
||||
"angularx-qrcode": "^17.0.1",
|
||||
"express": "^4.18.2",
|
||||
"ng2-pdf-viewer": "^10.3.3",
|
||||
"ngx-toastr": "^17.0.2",
|
||||
|
||||
@@ -1,34 +1,48 @@
|
||||
<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.
|
||||
</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>
|
||||
</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>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<router-outlet />
|
||||
<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>
|
||||
</div>
|
||||
</section>
|
||||
</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>
|
||||
|
||||
<!-- 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>
|
||||
</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,204 @@
|
||||
<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
|
||||
<br />
|
||||
<span class="text-gray-800 dark:text-white">pépites?</span>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<!-- 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>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 mx-auto w-full space-y-2">
|
||||
<app-search (onSearchChange)="showNewQuery($event)" />
|
||||
<!-- 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="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>
|
||||
|
||||
<!-- 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,226 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,266 +1,344 @@
|
||||
@if (profile != undefined) {
|
||||
<section class="text-gray-600">
|
||||
<div class="container px-5 py-5 mx-auto flex flex-col">
|
||||
<div class="w-full max-md:mx-5 mx-auto my-5 rounded-lg min-h-56 max-h-64 bg-cover bg-auth">
|
||||
<div class="w-max flex justify-between items-center">
|
||||
<a [routerLink]="['']" (click)="location.back()">
|
||||
<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"
|
||||
>
|
||||
<title>Retour</title>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-4.28 9.22a.75.75 0 0 0 0 1.06l3 3a.75.75 0 1 0 1.06-1.06l-1.72-1.72h5.69a.75.75 0 0 0 0-1.5h-5.69l1.72-1.72a.75.75 0 0 0-1.06-1.06l-3 3Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<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-7xl mx-auto">
|
||||
<!-- Header avec bannière -->
|
||||
<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 -->
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-transparent to-black/30"></div>
|
||||
|
||||
@if (profile().estVerifier) {
|
||||
<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"
|
||||
>
|
||||
<title>Profile verifier</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>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col justify-center sm:flex-row sm:justify-between">
|
||||
<div class="text-center sm:pr-8 sm:py-8">
|
||||
@if (!isEditMode()) {
|
||||
<div class="w-max" (click)="isEditMode.set(!isEditMode())">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-6 h-6 cursor-pointer hover:text-gray-800 dark:text-white"
|
||||
<!-- Navigation -->
|
||||
<div class="relative z-10 p-4 flex justify-between items-start">
|
||||
<!-- Bouton retour -->
|
||||
<button
|
||||
(click)="location.back()"
|
||||
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"
|
||||
>
|
||||
<title>Editer le profil</title>
|
||||
<path
|
||||
d="M21.731 2.269a2.625 2.625 0 0 0-3.712 0l-1.157 1.157 3.712 3.712 1.157-1.157a2.625 2.625 0 0 0 0-3.712ZM19.513 8.199l-3.712-3.712-8.4 8.4a5.25 5.25 0 0 0-1.32 2.214l-.8 2.685a.75.75 0 0 0 .933.933l2.685-.8a5.25 5.25 0 0 0 2.214-1.32l8.4-8.4Z"
|
||||
/>
|
||||
<path
|
||||
d="M5.25 5.25a3 3 0 0 0-3 3v10.5a3 3 0 0 0 3 3h10.5a3 3 0 0 0 3-3V13.5a.75.75 0 0 0-1.5 0v5.25a1.5 1.5 0 0 1-1.5 1.5H5.25a1.5 1.5 0 0 1-1.5-1.5V8.25a1.5 1.5 0 0 1 1.5-1.5h5.25a.75.75 0 0 0 0-1.5H5.25Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-6 h-6 text-white group-hover:-translate-x-1 transition-transform"
|
||||
>
|
||||
<title>Retour</title>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-4.28 9.22a.75.75 0 0 0 0 1.06l3 3a.75.75 0 1 0 1.06-1.06l-1.72-1.72h5.69a.75.75 0 0 0 0-1.5h-5.69l1.72-1.72a.75.75 0 0 0-1.06-1.06l-3 3Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
@if (!isEditMode()) {
|
||||
<!-- 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="w-5 h-5 md:w-6 md:h-6 text-purple-300"
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<!-- Avatar et info principale -->
|
||||
<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 bouton edit -->
|
||||
<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 w-full h-full"
|
||||
src="{{ environment.baseUrl }}/api/files/users/{{ user().id }}/{{
|
||||
user().avatar
|
||||
}}"
|
||||
/>
|
||||
} @else {
|
||||
<img
|
||||
alt="{{ user().username }}"
|
||||
class="object-cover w-full h-full"
|
||||
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{ user().username }}"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nom et titre -->
|
||||
<div
|
||||
class="flex-1 text-center md:text-left mb-4 md:mb-0 animate-slide-up animation-delay-100"
|
||||
>
|
||||
@if (user().name) {
|
||||
<h1 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white mb-1">
|
||||
{{ user().name }}
|
||||
</h1>
|
||||
} @else if (user().username) {
|
||||
<h1 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white mb-1">
|
||||
{{ user().username }}
|
||||
</h1>
|
||||
} @else {
|
||||
<h1 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white mb-1">
|
||||
{{ user().email }}
|
||||
</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-4 gap-6">
|
||||
<!-- Sidebar - Informations -->
|
||||
<aside class="lg:col-span-1 space-y-6 animate-slide-up animation-delay-200">
|
||||
<!-- Card Apropos -->
|
||||
<div
|
||||
class="w-28 h-28 rounded-full inline-flex items-center justify-center bg-gray-200 text-gray-400"
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 hover:shadow-xl transition-shadow duration-300"
|
||||
>
|
||||
@if (user().avatar) {
|
||||
<img
|
||||
alt="{{ user().username }}"
|
||||
class="object-cover object-center h-full w-full rounded-full"
|
||||
src="{{ environment.baseUrl }}/api/files/users/{{ user().id }}/{{
|
||||
user().avatar
|
||||
}}"
|
||||
/>
|
||||
} @else {
|
||||
<img
|
||||
alt="{{ user().username }}"
|
||||
class="object-cover object-center h-full w-full rounded-full"
|
||||
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{ user().username }}"
|
||||
/>
|
||||
}
|
||||
<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-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
|
||||
</h3>
|
||||
<p class="text-gray-700 dark:text-gray-300 leading-relaxed text-base">
|
||||
{{ profile().apropos }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col w-full items-center text-center justify-center">
|
||||
@if (user().name) {
|
||||
<h2
|
||||
class="font-medium title-font mt-4 text-gray-900 text-lg w-full dark:text-white"
|
||||
<!-- Card Bio -->
|
||||
<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"
|
||||
>
|
||||
{{ user().name }}
|
||||
</h2>
|
||||
} @else if (user().username) {
|
||||
<h2
|
||||
class="font-medium title-font mt-4 text-gray-900 text-lg w-full dark:text-white"
|
||||
>
|
||||
{{ user().username }}
|
||||
</h2>
|
||||
} @else {
|
||||
<h2
|
||||
class="font-medium title-font mt-4 text-gray-900 text-lg w-full dark:text-white"
|
||||
>
|
||||
{{ user().email }}
|
||||
</h2>
|
||||
}
|
||||
<div class="w-12 h-1 bg-indigo-500 rounded mt-2 mb-4"></div>
|
||||
<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-base dark:text-white w-full">{{ profile().bio }}</p>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">
|
||||
{{ profile().bio }}
|
||||
</p>
|
||||
} @else {
|
||||
<p class="text-base dark:text-white w-full">
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300 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.
|
||||
compétences.
|
||||
</p>
|
||||
}
|
||||
|
||||
@if (profile().secteur) {
|
||||
<div class="space-y-2 flex flex-col my-4">
|
||||
<p class="text-base dark:text-white">Secteur</p>
|
||||
<app-chips [sectorId]="profile().secteur" />
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (profile().reseaux) {
|
||||
<div class="space-y-2 flex flex-col my-4">
|
||||
<p class="text-base dark:text-white">Réseaux</p>
|
||||
<app-reseaux [reseaux]="profile().reseaux" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<a
|
||||
[href]="qrCodeDownloadLink"
|
||||
download="qrcode"
|
||||
class="w-full flex items-center justify-center sm:pr-8 sm:py-8"
|
||||
>
|
||||
<qrcode
|
||||
(qrCodeURL)="onChangeURL($event)"
|
||||
[qrdata]="myProfileQrCode"
|
||||
[width]="128"
|
||||
[elementType]="'url'"
|
||||
[errorCorrectionLevel]="'M'"
|
||||
class="mx-auto"
|
||||
></qrcode>
|
||||
</a>
|
||||
}
|
||||
|
||||
@if (isEditMode()) {
|
||||
<app-update-user [user]="user()" (isCancelEditMode)="onCancelEditMode($event)" />
|
||||
}
|
||||
</div>
|
||||
<!-- Card Secteur -->
|
||||
@if (profile().secteur) {
|
||||
<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>
|
||||
}
|
||||
|
||||
<div
|
||||
class="flex-1 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"
|
||||
>
|
||||
<div class="p-4">
|
||||
<ul
|
||||
class="w-full flex flex-wrap gap-x-2 gap-y-2 rounded-lg items-center max-sm:mx-auto"
|
||||
>
|
||||
<li
|
||||
id="homeTab"
|
||||
(click)="menu.set('home')"
|
||||
[ngClass]="{ 'border-blue-600 text-blue-600': menu() == 'home'.toLowerCase() }"
|
||||
class="tab flex flex-col justify-center items-center border-2 hover:border-blue-600 rounded-lg bg-gray-100 text-sm font-semibold hover:text-blue-600 py-2 px-2 min-w-[100px] cursor-pointer transition-all"
|
||||
<!-- Card Réseaux -->
|
||||
@if (profile().reseaux) {
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 hover:shadow-xl transition-shadow duration-300"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
class="w-5 mb-1.5"
|
||||
viewBox="0 0 511 511.999"
|
||||
<h3
|
||||
class="text-lg font-bold text-gray-900 dark:text-white mb-3 flex items-center gap-2"
|
||||
>
|
||||
<path
|
||||
d="M498.7 222.695c-.016-.011-.028-.027-.04-.039L289.805 13.81C280.902 4.902 269.066 0 256.477 0c-12.59 0-24.426 4.902-33.332 13.809L14.398 222.55c-.07.07-.144.144-.21.215-18.282 18.386-18.25 48.218.09 66.558 8.378 8.383 19.44 13.235 31.273 13.746.484.047.969.07 1.457.07h8.32v153.696c0 30.418 24.75 55.164 55.168 55.164h81.711c8.285 0 15-6.719 15-15V376.5c0-13.879 11.293-25.168 25.172-25.168h48.195c13.88 0 25.168 11.29 25.168 25.168V497c0 8.281 6.715 15 15 15h81.711c30.422 0 55.168-24.746 55.168-55.164V303.14h7.719c12.586 0 24.422-4.903 33.332-13.813 18.36-18.367 18.367-48.254.027-66.633zm-21.243 45.422a17.03 17.03 0 0 1-12.117 5.024h-22.72c-8.285 0-15 6.714-15 15v168.695c0 13.875-11.289 25.164-25.168 25.164h-66.71V376.5c0-30.418-24.747-55.168-55.169-55.168H232.38c-30.422 0-55.172 24.75-55.172 55.168V482h-66.71c-13.876 0-25.169-11.29-25.169-25.164V288.14c0-8.286-6.715-15-15-15H48a13.9 13.9 0 0 0-.703-.032c-4.469-.078-8.66-1.851-11.8-4.996-6.68-6.68-6.68-17.55 0-24.234.003 0 .003-.004.007-.008l.012-.012L244.363 35.02A17.003 17.003 0 0 1 256.477 30c4.574 0 8.875 1.781 12.113 5.02l208.8 208.796.098.094c6.645 6.692 6.633 17.54-.031 24.207zm0 0"
|
||||
data-original="#000000"
|
||||
></path>
|
||||
</svg>
|
||||
Mon profile
|
||||
</li>
|
||||
<li
|
||||
id="settingTab"
|
||||
(click)="menu.set('projects')"
|
||||
[ngClass]="{ 'border-blue-600 text-blue-600': menu() == 'projects'.toLowerCase() }"
|
||||
class="tab flex flex-col justify-center items-center border-2 hover:border-blue-600 rounded-lg bg-gray-100 text-sm font-semibold hover:text-blue-600 py-2 px-2 min-w-[100px] cursor-pointer transition-all"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M9.75 3.104v5.714a2.25 2.25 0 0 1-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 0 1 4.5 0m0 0v5.714c0 .597.237 1.17.659 1.591L19.8 15.3M14.25 3.104c.251.023.501.05.75.082M19.8 15.3l-1.57.393A9.065 9.065 0 0 1 12 15a9.065 9.065 0 0 0-6.23-.693L5 14.5m14.8.8 1.402 1.402c1.232 1.232.65 3.318-1.067 3.611A48.309 48.309 0 0 1 12 21c-2.773 0-5.491-.235-8.135-.687-1.718-.293-2.3-2.379-1.067-3.61L5 14.5"
|
||||
/>
|
||||
</svg>
|
||||
Mes projets
|
||||
</li>
|
||||
<li
|
||||
id="profileTab"
|
||||
(click)="menu.set('update')"
|
||||
[ngClass]="{ 'border-blue-600 text-blue-600': menu() == 'update'.toLowerCase() }"
|
||||
class="tab flex flex-col justify-center items-center border-2 hover:border-blue-600 rounded-lg bg-gray-100 text-sm font-semibold hover:text-blue-600 py-2 px-2 min-w-[100px] cursor-pointer transition-all"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
class="w-5 mb-1.5"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M437.02 74.98C388.668 26.63 324.379 0 256 0S123.332 26.629 74.98 74.98C26.63 123.332 0 187.621 0 256s26.629 132.668 74.98 181.02C123.332 485.37 187.621 512 256 512s132.668-26.629 181.02-74.98C485.37 388.668 512 324.379 512 256s-26.629-132.668-74.98-181.02zM111.105 429.297c8.454-72.735 70.989-128.89 144.895-128.89 38.96 0 75.598 15.179 103.156 42.734 23.281 23.285 37.965 53.687 41.742 86.152C361.641 462.172 311.094 482 256 482s-105.637-19.824-144.895-52.703zM256 269.507c-42.871 0-77.754-34.882-77.754-77.753C178.246 148.879 213.13 114 256 114s77.754 34.879 77.754 77.754c0 42.871-34.883 77.754-77.754 77.754zm170.719 134.427a175.9 175.9 0 0 0-46.352-82.004c-18.437-18.438-40.25-32.27-64.039-40.938 28.598-19.394 47.426-52.16 47.426-89.238C363.754 132.34 315.414 84 256 84s-107.754 48.34-107.754 107.754c0 37.098 18.844 69.875 47.465 89.266-21.887 7.976-42.14 20.308-59.566 36.542-25.235 23.5-42.758 53.465-50.883 86.348C50.852 364.242 30 312.512 30 256 30 131.383 131.383 30 256 30s226 101.383 226 226c0 56.523-20.86 108.266-55.281 147.934zm0 0"
|
||||
data-original="#000000"
|
||||
></path>
|
||||
</svg>
|
||||
Mes informations
|
||||
</li>
|
||||
<li
|
||||
id="cvTab"
|
||||
(click)="menu.set('cv')"
|
||||
[ngClass]="{ 'border-blue-600 text-blue-600': menu() == 'cv'.toLowerCase() }"
|
||||
class="tab flex flex-col justify-center items-center border-2 hover:border-blue-600 rounded-lg bg-gray-100 text-sm font-semibold hover:text-blue-600 py-2 px-2 min-w-[100px] cursor-pointer transition-all"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"
|
||||
/>
|
||||
</svg>
|
||||
<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>
|
||||
}
|
||||
</aside>
|
||||
|
||||
Lecteur de PDF
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Contenu principal avec onglets -->
|
||||
<main class="lg:col-span-3 animate-slide-up animation-delay-300">
|
||||
<!-- Navigation par onglets -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-4 mb-6">
|
||||
<nav class="flex flex-wrap gap-2">
|
||||
<button
|
||||
(click)="menu.set('home')"
|
||||
[class.active-tab]="menu() === 'home'"
|
||||
class="tab-button flex items-center gap-2 px-4 py-2.5 rounded-lg font-medium text-sm transition-all duration-200"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-5 h-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="hidden sm:inline">Mon profil</span>
|
||||
</button>
|
||||
|
||||
<div id="homeContent" class="tab-content max-w-2xl block mt-8">
|
||||
<button
|
||||
(click)="menu.set('projects')"
|
||||
[class.active-tab]="menu() === 'projects'"
|
||||
class="tab-button flex items-center gap-2 px-4 py-2.5 rounded-lg font-medium text-sm transition-all duration-200"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-5 h-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9.75 3.104v5.714a2.25 2.25 0 01-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 014.5 0m0 0v5.714c0 .597.237 1.17.659 1.591L19.8 15.3M14.25 3.104c.251.023.501.05.75.082M19.8 15.3l-1.57.393A9.065 9.065 0 0112 15a9.065 9.065 0 00-6.23-.693L5 14.5m14.8.8l1.402 1.402c1.232 1.232.65 3.318-1.067 3.611A48.309 48.309 0 0112 21c-2.773 0-5.491-.235-8.135-.687-1.718-.293-2.3-2.379-1.067-3.61L5 14.5"
|
||||
/>
|
||||
</svg>
|
||||
<span class="hidden sm:inline">Mes projets</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
(click)="menu.set('update')"
|
||||
[class.active-tab]="menu() === 'update'"
|
||||
class="tab-button flex items-center gap-2 px-4 py-2.5 rounded-lg font-medium text-sm transition-all duration-200"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-5 h-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<span class="hidden sm:inline">Mes informations</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
(click)="menu.set('cv')"
|
||||
[class.active-tab]="menu() === 'cv'"
|
||||
class="tab-button flex items-center gap-2 px-4 py-2.5 rounded-lg font-medium text-sm transition-all duration-200"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-5 h-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="hidden sm:inline">Lecteur PDF</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Contenu des onglets -->
|
||||
<div class="tab-content">
|
||||
@switch (menu().toLowerCase()) {
|
||||
@case ('home'.toLowerCase()) {
|
||||
<app-my-home-profile [profile]="profile()" />
|
||||
@case ('home') {
|
||||
<app-update-user [user]="user()" />
|
||||
}
|
||||
@case ('projects'.toLowerCase()) {
|
||||
@case ('projects') {
|
||||
<app-my-profile-project-list
|
||||
[projectIds]="profile().projets"
|
||||
[userId]="user().id"
|
||||
/>
|
||||
<router-outlet />
|
||||
}
|
||||
@case ('update'.toLowerCase()) {
|
||||
@case ('update') {
|
||||
<app-my-profile-update-form [profile]="profile()" />
|
||||
}
|
||||
@case ('cv'.toLowerCase()) {
|
||||
@case ('cv') {
|
||||
<app-pdf-viewer [profile]="profile()" />
|
||||
}
|
||||
@default {
|
||||
<app-my-home-profile [profile]="profile()" />
|
||||
<app-update-user [user]="user()" />
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
// ============================================
|
||||
// ANIMATIONS
|
||||
// ============================================
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
.animate-pulse-slow {
|
||||
animation: pulseSlow 3s ease-in-out 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;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// NAVIGATION PAR ONGLETS
|
||||
// ============================================
|
||||
|
||||
.tab-button {
|
||||
@apply bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300;
|
||||
@apply hover:bg-indigo-50 dark:hover:bg-gray-600 hover:text-indigo-600 dark:hover:text-indigo-400;
|
||||
|
||||
&.active-tab {
|
||||
@apply bg-gradient-to-r from-indigo-600 to-purple-600 text-white;
|
||||
@apply shadow-lg;
|
||||
|
||||
svg {
|
||||
@apply text-white;
|
||||
}
|
||||
}
|
||||
|
||||
// Animation au hover
|
||||
&:hover:not(.active-tab) {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
// Effet au clic
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// CARDS
|
||||
// ============================================
|
||||
|
||||
.card {
|
||||
@apply bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6;
|
||||
@apply transition-shadow duration-300;
|
||||
|
||||
&:hover {
|
||||
@apply shadow-xl;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// RESPONSIVE BREAKPOINTS
|
||||
// ============================================
|
||||
|
||||
// Mobile (par défaut)
|
||||
@media (max-width: 640px) {
|
||||
.tab-button {
|
||||
min-width: auto;
|
||||
padding: 0.625rem;
|
||||
|
||||
span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tablet
|
||||
@media (min-width: 768px) and (max-width: 1023px) {
|
||||
.tab-button {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Desktop
|
||||
@media (min-width: 1024px) {
|
||||
// Grille sidebar + contenu
|
||||
.sidebar-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 300px 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// BOUTON D'ÉDITION SUR L'AVATAR
|
||||
// ============================================
|
||||
|
||||
.edit-button {
|
||||
@apply absolute -top-2 -right-2 z-10;
|
||||
@apply w-8 h-8 bg-indigo-600 hover:bg-indigo-700;
|
||||
@apply rounded-full flex items-center justify-center;
|
||||
@apply shadow-lg transition-all duration-300;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
svg {
|
||||
@apply w-4 h-4 text-white;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// AVATAR
|
||||
// ============================================
|
||||
|
||||
.avatar-wrapper {
|
||||
position: relative;
|
||||
|
||||
.avatar-ring {
|
||||
@apply w-28 h-28 md:w-36 md:h-36;
|
||||
@apply rounded-full bg-gradient-to-br from-indigo-500 to-purple-600;
|
||||
@apply p-1 shadow-2xl;
|
||||
@apply transition-transform duration-300;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-image {
|
||||
@apply w-full h-full rounded-full overflow-hidden bg-white;
|
||||
|
||||
img {
|
||||
@apply object-cover w-full h-full;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// BANNIÈRE
|
||||
// ============================================
|
||||
|
||||
.banner {
|
||||
@apply relative rounded-2xl overflow-hidden shadow-xl;
|
||||
|
||||
.banner-image {
|
||||
@apply h-48 md:h-64 bg-cover bg-center;
|
||||
}
|
||||
|
||||
.banner-overlay {
|
||||
@apply absolute inset-0 bg-gradient-to-b from-transparent to-black/30;
|
||||
}
|
||||
|
||||
.banner-content {
|
||||
@apply relative z-10 p-4 flex justify-between items-start;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// BACK BUTTON
|
||||
// ============================================
|
||||
|
||||
.back-button {
|
||||
@apply flex items-center justify-center;
|
||||
@apply w-10 h-10 md:w-12 md:h-12;
|
||||
@apply bg-white/20 backdrop-blur-md rounded-full;
|
||||
@apply hover:bg-white/30 transition-all duration-300;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
|
||||
svg {
|
||||
transform: translateX(-4px);
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
@apply w-6 h-6 text-white transition-transform;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// BADGE VÉRIFIÉ
|
||||
// ============================================
|
||||
|
||||
.verified-badge {
|
||||
@apply flex items-center gap-2;
|
||||
@apply bg-purple-500/20 backdrop-blur-md;
|
||||
@apply px-3 py-2 rounded-full;
|
||||
|
||||
svg {
|
||||
@apply w-5 h-5 md:w-6 md:h-6 text-purple-300;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply text-white text-sm font-medium;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// SECTIONS AVEC ICÔNES
|
||||
// ============================================
|
||||
|
||||
.section-title {
|
||||
@apply text-lg font-bold text-gray-900 dark:text-white mb-3;
|
||||
@apply flex items-center gap-2;
|
||||
|
||||
svg {
|
||||
@apply h-5 w-5 text-indigo-500;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Focus visible pour l'accessibilité
|
||||
*:focus-visible {
|
||||
outline: 2px solid #4f46e5;
|
||||
outline-offset: 2px;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// MODE SOMBRE
|
||||
// ============================================
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.card {
|
||||
background-color: rgba(31, 41, 55, 0.8);
|
||||
border: 1px solid rgba(55, 65, 81, 0.3);
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
&:not(.active-tab) {
|
||||
background-color: rgba(55, 65, 81, 0.5);
|
||||
color: rgba(209, 213, 219, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// UTILITAIRES PERSONNALISÉS
|
||||
// ============================================
|
||||
|
||||
// Divider avec gradient
|
||||
.divider-gradient {
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg, transparent, #4f46e5, transparent);
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
// Card avec hover effect
|
||||
.hover-lift {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
// Glassmorphism effect
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// PRINT STYLES (optionnel)
|
||||
// ============================================
|
||||
|
||||
@media print {
|
||||
.banner,
|
||||
.back-button,
|
||||
.edit-button,
|
||||
.tab-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card {
|
||||
box-shadow: none;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ import { ProfileRepository } from '@app/domain/profiles/profile.repository';
|
||||
import { PROFILE_REPOSITORY_TOKEN } from '@app/infrastructure/profiles/profile-repository.token';
|
||||
import { of } from 'rxjs';
|
||||
import { Profile } from '@app/domain/profiles/profile.model';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
|
||||
describe('MyProfileComponent', () => {
|
||||
let component: MyProfileComponent;
|
||||
let fixture: ComponentFixture<MyProfileComponent>;
|
||||
|
||||
let mockProfileRepo: ProfileRepository;
|
||||
let mockToastrService: Partial<ToastrService>;
|
||||
|
||||
beforeEach(async () => {
|
||||
mockProfileRepo = {
|
||||
@@ -20,11 +22,18 @@ describe('MyProfileComponent', () => {
|
||||
update: jest.fn(),
|
||||
getByUserId: jest.fn().mockReturnValue(of({} as Profile)),
|
||||
};
|
||||
mockToastrService = {
|
||||
warning: jest.fn(),
|
||||
success: jest.fn(),
|
||||
info: jest.fn(),
|
||||
error: jest.fn(),
|
||||
};
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [MyProfileComponent],
|
||||
providers: [
|
||||
provideRouter([]),
|
||||
{ provide: PROFILE_REPOSITORY_TOKEN, useValue: mockProfileRepo },
|
||||
{ provide: ToastrService, useValue: mockToastrService },
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import { Component, computed, inject, OnInit, signal } from '@angular/core';
|
||||
import { ActivatedRoute, RouterLink, RouterOutlet } from '@angular/router';
|
||||
import { ActivatedRoute, RouterOutlet } from '@angular/router';
|
||||
import { User } from '@app/shared/models/user';
|
||||
import { Location, NgClass } from '@angular/common';
|
||||
import { Location, UpperCasePipe } from '@angular/common';
|
||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||
import { SafeUrl } from '@angular/platform-browser';
|
||||
import { QRCodeModule } from 'angularx-qrcode';
|
||||
import { environment } from '@env/environment';
|
||||
import { ChipsComponent } from '@app/shared/components/chips/chips.component';
|
||||
import { ReseauxComponent } from '@app/shared/components/reseaux/reseaux.component';
|
||||
import { UpdateUserComponent } from '@app/shared/features/update-user/update-user.component';
|
||||
import { MyProfileProjectListComponent } from '@app/shared/components/my-profile-project-list/my-profile-project-list.component';
|
||||
import { MyHomeProfileComponent } from '@app/shared/components/my-home-profile/my-home-profile.component';
|
||||
import { MyProfileUpdateFormComponent } from '@app/shared/components/my-profile-update-form/my-profile-update-form.component';
|
||||
import { PdfViewerComponent } from '@app/shared/features/pdf-viewer/pdf-viewer.component';
|
||||
import { ProfileFacade } from '@app/ui/profiles/profile.facade';
|
||||
@@ -19,17 +16,14 @@ import { ProfileFacade } from '@app/ui/profiles/profile.facade';
|
||||
selector: 'app-my-profile',
|
||||
standalone: true,
|
||||
imports: [
|
||||
RouterLink,
|
||||
QRCodeModule,
|
||||
ChipsComponent,
|
||||
ReseauxComponent,
|
||||
UpdateUserComponent,
|
||||
MyProfileProjectListComponent,
|
||||
RouterOutlet,
|
||||
MyHomeProfileComponent,
|
||||
MyProfileUpdateFormComponent,
|
||||
NgClass,
|
||||
PdfViewerComponent,
|
||||
UpperCasePipe,
|
||||
],
|
||||
templateUrl: './my-profile.component.html',
|
||||
styleUrl: './my-profile.component.scss',
|
||||
@@ -40,7 +34,6 @@ export class MyProfileComponent implements OnInit {
|
||||
protected menu = signal<string>('home');
|
||||
|
||||
protected myProfileQrCode = `${environment.production}`;
|
||||
protected qrCodeDownloadLink: SafeUrl = `${environment.production}`;
|
||||
|
||||
protected location = inject(Location);
|
||||
protected readonly route = inject(ActivatedRoute);
|
||||
@@ -52,16 +45,6 @@ export class MyProfileComponent implements OnInit {
|
||||
return {} as User;
|
||||
});
|
||||
|
||||
protected isEditMode = signal<boolean>(false);
|
||||
|
||||
onChangeURL(url: SafeUrl) {
|
||||
this.qrCodeDownloadLink = url;
|
||||
}
|
||||
|
||||
onCancelEditMode($event: boolean) {
|
||||
this.isEditMode.set(!$event);
|
||||
}
|
||||
|
||||
private readonly profileFacade = new ProfileFacade();
|
||||
protected profile = this.profileFacade.profile;
|
||||
protected readonly loading = this.profileFacade.loading;
|
||||
|
||||
@@ -1,114 +1,267 @@
|
||||
<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']">
|
||||
<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"
|
||||
>
|
||||
<title>Retour</title>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-4.28 9.22a.75.75 0 0 0 0 1.06l3 3a.75.75 0 1 0 1.06-1.06l-1.72-1.72h5.69a.75.75 0 0 0 0-1.5h-5.69l1.72-1.72a.75.75 0 0 0-1.06-1.06l-3 3Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<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>
|
||||
|
||||
@if (profile().estVerifier) {
|
||||
<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"
|
||||
<!-- 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"
|
||||
>
|
||||
<title>Profile verifier</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>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-6 h-6 text-white group-hover:text-white transition-transform group-hover:-translate-x-1"
|
||||
>
|
||||
<title>Retour</title>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-4.28 9.22a.75.75 0 0 0 0 1.06l3 3a.75.75 0 1 0 1.06-1.06l-1.72-1.72h5.69a.75.75 0 0 0 0-1.5h-5.69l1.72-1.72a.75.75 0 0 0-1.06-1.06l-3 3Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</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="w-5 h-5 md:w-6 md:h-6 text-purple-300"
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<!-- 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 w-full h-full"
|
||||
src="{{ environment.baseUrl }}/api/files/users/{{ user().id }}/{{
|
||||
user().avatar
|
||||
}}"
|
||||
loading="lazy"
|
||||
/>
|
||||
} @else {
|
||||
<img
|
||||
alt="{{ user().username }}"
|
||||
class="object-cover w-full h-full"
|
||||
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{ user().username }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</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) {
|
||||
<h1 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white mb-1">
|
||||
{{ user().name }}
|
||||
</h1>
|
||||
} @else if (user().username) {
|
||||
<h1 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white mb-1">
|
||||
{{ user().username }}
|
||||
</h1>
|
||||
} @else {
|
||||
<h1 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white mb-1">
|
||||
{{ user().email }}
|
||||
</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="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="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>
|
||||
|
||||
<!-- <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">
|
||||
<!-- Colonne droite - À propos et Projets -->
|
||||
<div class="lg:col-span-2 space-y-6 animate-slide-up animation-delay-300">
|
||||
<!-- Card À propos -->
|
||||
<div
|
||||
class="w-20 h-20 rounded-full inline-flex items-center justify-center bg-gray-200 text-gray-400"
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 md:p-8 hover:shadow-xl transition-shadow duration-300"
|
||||
>
|
||||
@if (user().avatar) {
|
||||
<img
|
||||
alt="{{ user().username }}"
|
||||
class="object-cover object-center h-full w-full rounded-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"
|
||||
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{ user().username }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
}
|
||||
<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>
|
||||
<div class="flex flex-col items-center text-center justify-center">
|
||||
@if (user().name) {
|
||||
<h2 class="font-medium title-font mt-4 text-gray-900 text-lg dark:text-white">
|
||||
{{ user().name }}
|
||||
</h2>
|
||||
} @else if (user().username) {
|
||||
<h2 class="font-medium title-font mt-4 text-gray-900 text-lg dark:text-white">
|
||||
{{ user().username }}
|
||||
</h2>
|
||||
} @else {
|
||||
<h2 class="font-medium title-font mt-4 text-gray-900 text-lg dark:text-white">
|
||||
{{ 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>
|
||||
}
|
||||
|
||||
@if (profile().secteur) {
|
||||
<div class="space-y-2 flex flex-col my-4">
|
||||
<p class="text-base dark:text-white">Secteur</p>
|
||||
<app-chips [sectorId]="profile().secteur" />
|
||||
<!-- 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"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="flex items-center gap-3 pb-6 mb-8 border-b-2 border-indigo-500">
|
||||
<div
|
||||
class="w-12 h-12 bg-indigo-100 dark:bg-indigo-900 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-7 h-7 text-indigo-600 dark:text-indigo-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>
|
||||
}
|
||||
|
||||
@if (profile().reseaux) {
|
||||
<div class="space-y-2 flex flex-col my-4">
|
||||
<p class="text-base dark:text-white">Réseaux</p>
|
||||
<app-reseaux [reseaux]="profile().reseaux" />
|
||||
<div class="flex-1">
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Explorer les projets
|
||||
</h2>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
Découvrez les réalisations de la communauté
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<app-project-list [userProjectId]="profile().utilisateur" />
|
||||
</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>
|
||||
|
||||
<app-project-list [userProjectId]="profile().utilisateur" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/* 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,5 @@
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
import { QRCodeModule } from 'angularx-qrcode';
|
||||
import { UpperCasePipe } from '@angular/common';
|
||||
import { User } from '@app/shared/models/user';
|
||||
import { ChipsComponent } from '@app/shared/components/chips/chips.component';
|
||||
@@ -13,14 +12,7 @@ import { Profile } from '@app/domain/profiles/profile.model';
|
||||
@Component({
|
||||
selector: 'app-profile-detail',
|
||||
standalone: true,
|
||||
imports: [
|
||||
QRCodeModule,
|
||||
ChipsComponent,
|
||||
ReseauxComponent,
|
||||
RouterLink,
|
||||
UpperCasePipe,
|
||||
ProjectListComponent,
|
||||
],
|
||||
imports: [ChipsComponent, ReseauxComponent, RouterLink, UpperCasePipe, ProjectListComponent],
|
||||
templateUrl: './profile-detail.component.html',
|
||||
styleUrl: './profile-detail.component.scss',
|
||||
})
|
||||
|
||||
@@ -1,19 +1,77 @@
|
||||
<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 {
|
||||
<app-vertical-profile-list [profiles]="profiles()" />
|
||||
<!-- 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,86 @@
|
||||
/* 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 } });
|
||||
}
|
||||
}
|
||||
|
||||
14
src/app/shared/components/loading/loading.component.html
Normal file
14
src/app/shared/components/loading/loading.component.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!-- É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>
|
||||
22
src/app/shared/components/loading/loading.component.spec.ts
Normal file
22
src/app/shared/components/loading/loading.component.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
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,6 +0,0 @@
|
||||
@if (profile != undefined) {
|
||||
<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>
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MyHomeProfileComponent } from './my-home-profile.component';
|
||||
|
||||
describe('MyHomeProfileComponent', () => {
|
||||
let component: MyHomeProfileComponent;
|
||||
let fixture: ComponentFixture<MyHomeProfileComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [MyHomeProfileComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(MyHomeProfileComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { UpperCasePipe } from '@angular/common';
|
||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||
import { ProfileViewModel } from '@app/ui/profiles/profile.presenter.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-my-home-profile',
|
||||
standalone: true,
|
||||
imports: [UpperCasePipe],
|
||||
templateUrl: './my-home-profile.component.html',
|
||||
styleUrl: './my-home-profile.component.scss',
|
||||
})
|
||||
@UntilDestroy()
|
||||
export class MyHomeProfileComponent {
|
||||
@Input({ required: true }) profile: ProfileViewModel | undefined = undefined;
|
||||
}
|
||||
@@ -1,33 +1,124 @@
|
||||
<div class="min-h-screen py-4 font-sans">
|
||||
<div class="max-w-4xl max-lg:max-w-2xl max-sm:max-w-sm mx-auto">
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-8">Mes projets</h2>
|
||||
|
||||
@if (projects()) {
|
||||
<div class="relative flex items-center">
|
||||
<select
|
||||
[(ngModel)]="projectIdSelected"
|
||||
class="pr-4 pl-14 py-3 text-sm text-black rounded bg-white border border-gray-400 w-full outline-[#333]"
|
||||
<div class="w-full space-y-6">
|
||||
@if (projects()) {
|
||||
<!-- Sélecteur de projet -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 animate-fade-in animation-delay-100"
|
||||
>
|
||||
<div class="space-y-3">
|
||||
<label
|
||||
for="project-select"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
<option [value]="null" disabled>Selectionner le projet à modifier</option>
|
||||
Sélectionnez un projet à modifier
|
||||
</label>
|
||||
|
||||
<option [value]="'add'.toLowerCase()">Ajouter un nouveau projet</option>
|
||||
<div class="relative">
|
||||
<!-- Icône à gauche -->
|
||||
<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 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@for (project of projects(); track project.id) {
|
||||
<option [value]="project.id">
|
||||
{{ project.nom }}
|
||||
<!-- Select -->
|
||||
<select
|
||||
id="project-select"
|
||||
[(ngModel)]="projectIdSelected"
|
||||
class="w-full pl-10 pr-10 py-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all appearance-none cursor-pointer"
|
||||
>
|
||||
<option [value]="null" disabled>Sélectionnez un projet</option>
|
||||
|
||||
<!-- Option Ajouter avec style spécial -->
|
||||
<option [value]="'add'.toLowerCase()" class="font-semibold">
|
||||
➕ Ajouter un nouveau projet
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="w-full my-8">
|
||||
@if (projectIdSelected() != null) {
|
||||
@for (project of projects(); track project.id) {
|
||||
<option [value]="project.id">
|
||||
{{ project.nom }}
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
|
||||
<!-- Chevron à droite -->
|
||||
<div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
||||
<svg
|
||||
class="h-5 w-5 text-gray-400"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info bulle -->
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 flex items-center gap-1">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
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>
|
||||
Vous avez {{ projects().length }} projet(s) enregistré(s)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Formulaire du projet sélectionné -->
|
||||
@if (projectIdSelected() != null) {
|
||||
<div class="animate-slide-up">
|
||||
<app-my-profile-update-project-form
|
||||
[projectId]="projectIdSelected()"
|
||||
(formIsUpdated)="onProjectFormSubmitted($event)"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<!-- Message d'invite -->
|
||||
<div
|
||||
class="bg-gradient-to-r from-indigo-50 to-purple-50 dark:from-gray-800 dark:to-gray-700 rounded-xl p-8 text-center animate-fade-in animation-delay-200"
|
||||
>
|
||||
<div
|
||||
class="inline-flex w-16 h-16 bg-white dark:bg-gray-600 rounded-full items-center justify-center mb-4 shadow-lg"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-8 h-8 text-indigo-600 dark:text-indigo-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 13h6m-3-3v6m5 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">
|
||||
Aucun projet sélectionné
|
||||
</h3>
|
||||
<p class="text-gray-600 dark:text-gray-300">
|
||||
Sélectionnez un projet existant ou créez-en un nouveau pour commencer
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,283 +1,450 @@
|
||||
<form
|
||||
class="space-y-6 font-[sans-serif] max-w-md"
|
||||
[formGroup]="profileForm"
|
||||
(ngSubmit)="onSubmit()"
|
||||
>
|
||||
<h3 class="font-ubuntu font-bold text-xl uppercase dark:text-white mb-4">
|
||||
Mon curriculum vitae (CV)
|
||||
</h3>
|
||||
<app-my-profile-update-cv-form [profile]="profile" />
|
||||
|
||||
<h3 class="font-ubuntu font-bold text-xl uppercase dark:text-white">
|
||||
Ce qu'il faut savoir de moi
|
||||
</h3>
|
||||
<div class="mx-8">
|
||||
<div>
|
||||
<label class="mb-2 text-sm text-black block dark:text-white">Biographie</label>
|
||||
<div class="relative flex items-center">
|
||||
<textarea
|
||||
placeholder="Type Message"
|
||||
formControlName="bio"
|
||||
class="p-4 bg-white max-w-md mx-auto w-full block text-sm border border-gray-300 outline-[#007bff] rounded"
|
||||
rows="4"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-2 text-sm text-black block dark:text-white">A propos de vous</label>
|
||||
<div class="relative flex items-center">
|
||||
<textarea
|
||||
placeholder="Type Message"
|
||||
formControlName="apropos"
|
||||
class="p-4 bg-white max-w-md mx-auto w-full block text-sm border border-gray-300 outline-[#007bff] rounded"
|
||||
rows="4"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="font-ubuntu font-bold text-xl uppercase dark:text-white">Mon domaine de competence</h3>
|
||||
|
||||
<div class="mx-8">
|
||||
<div>
|
||||
<label class="mb-2 text-sm text-black block dark:text-white">Profession</label>
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="votre metier"
|
||||
formControlName="profession"
|
||||
class="pr-4 pl-14 py-3 text-sm text-black rounded bg-white border border-gray-400 w-full outline-[#333]"
|
||||
/>
|
||||
|
||||
<div class="absolute left-4">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor"
|
||||
class="size-4"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M11 4V3a2 2 0 0 0-2-2H7a2 2 0 0 0-2 2v1H4a2 2 0 0 0-2 2v3a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2h-1ZM9 2.5H7a.5.5 0 0 0-.5.5v1h3V3a.5.5 0 0 0-.5-.5ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
d="M3 11.83V12a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-.17c-.313.11-.65.17-1 .17H4c-.35 0-.687-.06-1-.17Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-2 text-sm text-black block dark:text-white">Secteur</label>
|
||||
|
||||
<div class="relative flex items-center">
|
||||
<select
|
||||
formControlName="secteur"
|
||||
class="pr-4 pl-14 py-3 text-sm text-black rounded bg-white border border-gray-400 w-full outline-[#333]"
|
||||
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()" class="space-y-8">
|
||||
<!-- Section CV -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 animate-fade-in">
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div
|
||||
class="w-10 h-10 bg-indigo-100 dark:bg-indigo-900 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-indigo-600 dark:text-indigo-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<option [ngValue]="null" disabled>Selectionner votre secteur d'activité</option>
|
||||
@for (sector of sectors(); track sector.id) {
|
||||
<option [value]="sector.id">{{ sector.nom }}</option>
|
||||
}
|
||||
</select>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Mon curriculum vitae (CV)</h3>
|
||||
</div>
|
||||
<app-my-profile-update-cv-form [profile]="profile" />
|
||||
</div>
|
||||
|
||||
<h3 class="font-ubuntu font-bold text-xl uppercase dark:text-white">Mes réseaux</h3>
|
||||
<div formGroupName="reseaux" class="mx-8">
|
||||
<div>
|
||||
<label class="mb-2 text-sm text-black block dark:text-white uppercase">Facebook</label>
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="lien vers votre facebook"
|
||||
formControlName="facebook"
|
||||
class="pr-4 pl-14 py-3 text-sm text-black rounded bg-white border border-gray-400 w-full outline-[#333]"
|
||||
/>
|
||||
|
||||
<div class="absolute left-4">
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
fill="#0866FF"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Facebook</title>
|
||||
<path
|
||||
d="M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-2 text-sm text-black block dark:text-white uppercase">Github</label>
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Lien vers votre github"
|
||||
formControlName="github"
|
||||
class="pr-4 pl-14 py-3 text-sm text-black rounded bg-white border border-gray-400 w-full outline-[#333]"
|
||||
/>
|
||||
|
||||
<div class="absolute left-4">
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
fill="#181717"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>GitHub</title>
|
||||
<path
|
||||
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-2 text-sm text-black block dark:text-white uppercase">Instagram</label>
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Lien vers votre instagram"
|
||||
formControlName="instagram"
|
||||
class="pr-4 pl-14 py-3 text-sm text-black rounded bg-white border border-gray-400 w-full outline-[#333]"
|
||||
/>
|
||||
|
||||
<div class="absolute left-4">
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
fill="#E4405F"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Instagram</title>
|
||||
<path
|
||||
d="M7.0301.084c-1.2768.0602-2.1487.264-2.911.5634-.7888.3075-1.4575.72-2.1228 1.3877-.6652.6677-1.075 1.3368-1.3802 2.127-.2954.7638-.4956 1.6365-.552 2.914-.0564 1.2775-.0689 1.6882-.0626 4.947.0062 3.2586.0206 3.6671.0825 4.9473.061 1.2765.264 2.1482.5635 2.9107.308.7889.72 1.4573 1.388 2.1228.6679.6655 1.3365 1.0743 2.1285 1.38.7632.295 1.6361.4961 2.9134.552 1.2773.056 1.6884.069 4.9462.0627 3.2578-.0062 3.668-.0207 4.9478-.0814 1.28-.0607 2.147-.2652 2.9098-.5633.7889-.3086 1.4578-.72 2.1228-1.3881.665-.6682 1.0745-1.3378 1.3795-2.1284.2957-.7632.4966-1.636.552-2.9124.056-1.2809.0692-1.6898.063-4.948-.0063-3.2583-.021-3.6668-.0817-4.9465-.0607-1.2797-.264-2.1487-.5633-2.9117-.3084-.7889-.72-1.4568-1.3876-2.1228C21.2982 1.33 20.628.9208 19.8378.6165 19.074.321 18.2017.1197 16.9244.0645 15.6471.0093 15.236-.005 11.977.0014 8.718.0076 8.31.0215 7.0301.0839m.1402 21.6932c-1.17-.0509-1.8053-.2453-2.2287-.408-.5606-.216-.96-.4771-1.3819-.895-.422-.4178-.6811-.8186-.9-1.378-.1644-.4234-.3624-1.058-.4171-2.228-.0595-1.2645-.072-1.6442-.079-4.848-.007-3.2037.0053-3.583.0607-4.848.05-1.169.2456-1.805.408-2.2282.216-.5613.4762-.96.895-1.3816.4188-.4217.8184-.6814 1.3783-.9003.423-.1651 1.0575-.3614 2.227-.4171 1.2655-.06 1.6447-.072 4.848-.079 3.2033-.007 3.5835.005 4.8495.0608 1.169.0508 1.8053.2445 2.228.408.5608.216.96.4754 1.3816.895.4217.4194.6816.8176.9005 1.3787.1653.4217.3617 1.056.4169 2.2263.0602 1.2655.0739 1.645.0796 4.848.0058 3.203-.0055 3.5834-.061 4.848-.051 1.17-.245 1.8055-.408 2.2294-.216.5604-.4763.96-.8954 1.3814-.419.4215-.8181.6811-1.3783.9-.4224.1649-1.0577.3617-2.2262.4174-1.2656.0595-1.6448.072-4.8493.079-3.2045.007-3.5825-.006-4.848-.0608M16.953 5.5864A1.44 1.44 0 1 0 18.39 4.144a1.44 1.44 0 0 0-1.437 1.4424M5.8385 12.012c.0067 3.4032 2.7706 6.1557 6.173 6.1493 3.4026-.0065 6.157-2.7701 6.1506-6.1733-.0065-3.4032-2.771-6.1565-6.174-6.1498-3.403.0067-6.156 2.771-6.1496 6.1738M8 12.0077a4 4 0 1 1 4.008 3.9921A3.9996 3.9996 0 0 1 8 12.0077"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-2 text-sm text-black block dark:text-white uppercase">linkedIn</label>
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Lien vers votre linkedIn"
|
||||
formControlName="linkedIn"
|
||||
class="pr-4 pl-14 py-3 text-sm text-black rounded bg-white border border-gray-400 w-full outline-[#333]"
|
||||
/>
|
||||
|
||||
<div class="absolute left-4">
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
fill="#0A66C2"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>LinkedIn</title>
|
||||
<path
|
||||
d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-2 text-sm text-black block dark:text-white uppercase">web</label>
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Lien vers votre site web"
|
||||
formControlName="web"
|
||||
class="pr-4 pl-14 py-3 text-sm text-black rounded bg-white border border-gray-400 w-full outline-[#333]"
|
||||
/>
|
||||
|
||||
<div class="absolute left-4">
|
||||
<svg
|
||||
id="Layer_1"
|
||||
class="w-6 h-6"
|
||||
data-name="Layer 1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<title>world-globe-outline</title>
|
||||
<path
|
||||
d="M256,0Q362.11,0,436.9,75.1,512,149.89,512,256T436.9,436.9Q362.11,512,256,512T75.1,436.9Q0,362.11,0,256T75.1,75.1Q149.89,0,256,0ZM55.34,190.63a211.82,211.82,0,0,0,0,130.73h66a416,416,0,0,1,0-130.73Zm14.9,165.7q34.36,63.55,100.64,92.73a232.64,232.64,0,0,1-20.07-31.92,302.59,302.59,0,0,1-22.2-60.81ZM170.87,63.24Q104.59,92.43,70.54,155.37h58.07a304.36,304.36,0,0,1,22.2-60.51A198.45,198.45,0,0,1,170.87,63.24ZM151.72,190.63A390.59,390.59,0,0,0,145.94,256a390.48,390.48,0,0,0,5.78,65.37H241.1V190.63Zm82.7-143.51q-32.83,13.38-57.16,61.11-9.43,19.16-17.63,47.13H241.1V45.61a3.4,3.4,0,0,1-1.52.3h-1.82Zm3.34,419h1.82a5.12,5.12,0,0,0,1.52.3V356.33H159.62q8.21,28.28,17.63,47.43,24.32,47.74,57.16,61.11ZM274.24,45.91h-1.83a3.38,3.38,0,0,1-1.52-.3V155.37h81.48q-8.21-28-17.63-47.13-24.33-47.73-57.16-61.11Zm86,275.46A391.23,391.23,0,0,0,366.06,256a395,395,0,0,0-5.78-65.37H270.9V321.37Zm-82.7,143.51q32.84-13.39,57.16-61.11,9.73-19.16,17.63-47.43H270.9V466.39a5.1,5.1,0,0,0,1.52-.3h1.83ZM441.46,155.37q-34.06-62.94-100-92.12A212.61,212.61,0,0,1,361.2,94.86a295.22,295.22,0,0,1,22.2,60.51Zm-100,293.7q66-29.49,100-92.73H383.39q-8.52,33.74-22.2,60.81A226,226,0,0,1,341.43,449.06Zm49.25-258.43A412,412,0,0,1,395.86,256a415.71,415.71,0,0,1-5.17,65.37h66a211.89,211.89,0,0,0,0-130.73Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-2 text-sm text-black block dark:text-white uppercase">x</label>
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Lien vers votre compte X"
|
||||
formControlName="x"
|
||||
class="pr-4 pl-14 py-3 text-sm text-black rounded bg-white border border-gray-400 w-full outline-[#333]"
|
||||
/>
|
||||
|
||||
<div class="absolute left-4">
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
fill="#000000"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>X</title>
|
||||
<path
|
||||
d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-2 text-sm text-black block dark:text-white uppercase">YouTube</label>
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Lien vers votre compte youtube"
|
||||
formControlName="youTube"
|
||||
class="pr-4 pl-14 py-3 text-sm text-black rounded bg-white border border-gray-400 w-full outline-[#333]"
|
||||
/>
|
||||
|
||||
<div class="absolute left-4">
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
fill="#FF0000"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>YouTube</title>
|
||||
<path
|
||||
d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
[ngClass]="{ 'bg-purple-600': profileForm.valid }"
|
||||
class="!mt-8 px-6 py-2 w-full bg-[#333] hover:bg-[#444] text-sm text-white mx-auto block"
|
||||
<!-- Section À propos de moi -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 animate-fade-in animation-delay-100"
|
||||
>
|
||||
Sauvegarder
|
||||
</button>
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div
|
||||
class="w-10 h-10 bg-purple-100 dark:bg-purple-900 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-purple-600 dark:text-purple-400"
|
||||
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>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Ce qu'il faut savoir de moi</h3>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- Biographie -->
|
||||
<div>
|
||||
<label for="bio" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Biographie
|
||||
</label>
|
||||
<textarea
|
||||
id="bio"
|
||||
formControlName="bio"
|
||||
placeholder="Parlez de vous en quelques lignes..."
|
||||
rows="4"
|
||||
class="w-full px-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 resize-none"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<!-- À propos -->
|
||||
<div>
|
||||
<label
|
||||
for="apropos"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
|
||||
>
|
||||
À propos de vous
|
||||
</label>
|
||||
<textarea
|
||||
id="apropos"
|
||||
formControlName="apropos"
|
||||
placeholder="Décrivez votre parcours, vos objectifs..."
|
||||
rows="4"
|
||||
class="w-full px-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 resize-none"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section Compétences -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 animate-fade-in animation-delay-200"
|
||||
>
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div
|
||||
class="w-10 h-10 bg-blue-100 dark:bg-blue-900 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-blue-600 dark:text-blue-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>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Mon domaine de compétence</h3>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- Profession -->
|
||||
<div>
|
||||
<label
|
||||
for="profession"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
|
||||
>
|
||||
Profession
|
||||
</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
|
||||
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>
|
||||
<input
|
||||
id="profession"
|
||||
type="text"
|
||||
formControlName="profession"
|
||||
placeholder="Ex: Développeur Web, Designer..."
|
||||
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>
|
||||
</div>
|
||||
|
||||
<!-- Secteur -->
|
||||
<div>
|
||||
<label
|
||||
for="secteur"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
|
||||
>
|
||||
Secteur d'activité
|
||||
</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 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>
|
||||
</div>
|
||||
<select
|
||||
id="secteur"
|
||||
formControlName="secteur"
|
||||
class="w-full pl-10 pr-10 py-3 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all appearance-none cursor-pointer"
|
||||
>
|
||||
<option [ngValue]="null" disabled>Sélectionnez votre secteur</option>
|
||||
@for (sector of sectors(); track sector.id) {
|
||||
<option [value]="sector.id">{{ sector.nom }}</option>
|
||||
}
|
||||
</select>
|
||||
<div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
||||
<svg
|
||||
class="h-5 w-5 text-gray-400"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Section Réseaux sociaux -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 animate-fade-in animation-delay-300"
|
||||
>
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div
|
||||
class="w-10 h-10 bg-pink-100 dark:bg-pink-900 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-pink-600 dark:text-pink-400"
|
||||
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>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Mes réseaux sociaux</h3>
|
||||
</div>
|
||||
|
||||
<div formGroupName="reseaux" class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<!-- Facebook -->
|
||||
<div>
|
||||
<label
|
||||
for="facebook"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
|
||||
>
|
||||
Facebook
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
fill="#0866FF"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="facebook"
|
||||
type="url"
|
||||
formControlName="facebook"
|
||||
placeholder="https://facebook.com/votreprofil"
|
||||
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>
|
||||
</div>
|
||||
<!-- GitHub -->
|
||||
<div>
|
||||
<label for="github" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
GitHub
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
fill="#181717"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="github"
|
||||
type="url"
|
||||
formControlName="github"
|
||||
placeholder="https://github.com/votreprofil"
|
||||
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>
|
||||
</div>
|
||||
<!-- Instagram -->
|
||||
<div>
|
||||
<label
|
||||
for="instagram"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
|
||||
>
|
||||
Instagram
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
fill="#E4405F"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7.0301.084c-1.2768.0602-2.1487.264-2.911.5634-.7888.3075-1.4575.72-2.1228 1.3877-.6652.6677-1.075 1.3368-1.3802 2.127-.2954.7638-.4956 1.6365-.552 2.914-.0564 1.2775-.0689 1.6882-.0626 4.947.0062 3.2586.0206 3.6671.0825 4.9473.061 1.2765.264 2.1482.5635 2.9107.308.7889.72 1.4573 1.388 2.1228.6679.6655 1.3365 1.0743 2.1285 1.38.7632.295 1.6361.4961 2.9134.552 1.2773.056 1.6884.069 4.9462.0627 3.2578-.0062 3.668-.0207 4.9478-.0814 1.28-.0607 2.147-.2652 2.9098-.5633.7889-.3086 1.4578-.72 2.1228-1.3881.665-.6682 1.0745-1.3378 1.3795-2.1284.2957-.7632.4966-1.636.552-2.9124.056-1.2809.0692-1.6898.063-4.948-.0063-3.2583-.021-3.6668-.0817-4.9465-.0607-1.2797-.264-2.1487-.5633-2.9117-.3084-.7889-.72-1.4568-1.3876-2.1228C21.2982 1.33 20.628.9208 19.8378.6165 19.074.321 18.2017.1197 16.9244.0645 15.6471.0093 15.236-.005 11.977.0014 8.718.0076 8.31.0215 7.0301.0839m.1402 21.6932c-1.17-.0509-1.8053-.2453-2.2287-.408-.5606-.216-.96-.4771-1.3819-.895-.422-.4178-.6811-.8186-.9-1.378-.1644-.4234-.3624-1.058-.4171-2.228-.0595-1.2645-.072-1.6442-.079-4.848-.007-3.2037.0053-3.583.0607-4.848.05-1.169.2456-1.805.408-2.2282.216-.5613.4762-.96.895-1.3816.4188-.4217.8184-.6814 1.3783-.9003.423-.1651 1.0575-.3614 2.227-.4171 1.2655-.06 1.6447-.072 4.848-.079 3.2033-.007 3.5835.005 4.8495.0608 1.169.0508 1.8053.2445 2.228.408.5608.216.96.4754 1.3816.895.4217.4194.6816.8176.9005 1.3787.1653.4217.3617 1.056.4169 2.2263.0602 1.2655.0739 1.645.0796 4.848.0058 3.203-.0055 3.5834-.061 4.848-.051 1.17-.245 1.8055-.408 2.2294-.216.5604-.4763.96-.8954 1.3814-.419.4215-.8181.6811-1.3783.9-.4224.1649-1.0577.3617-2.2262.4174-1.2656.0595-1.6448.072-4.8493.079-3.2045.007-3.5825-.006-4.848-.0608M16.953 5.5864A1.44 1.44 0 1 0 18.39 4.144a1.44 1.44 0 0 0-1.437 1.4424M5.8385 12.012c.0067 3.4032 2.7706 6.1557 6.173 6.1493 3.4026-.0065 6.157-2.7701 6.1506-6.1733-.0065-3.4032-2.771-6.1565-6.174-6.1498-3.403.0067-6.156 2.771-6.1496 6.1738M8 12.0077a4 4 0 1 1 4.008 3.9921A3.9996 3.9996 0 0 1 8 12.0077"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="instagram"
|
||||
type="url"
|
||||
formControlName="instagram"
|
||||
placeholder="https://instagram.com/votreprofil"
|
||||
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>
|
||||
</div>
|
||||
<!-- LinkedIn -->
|
||||
<div>
|
||||
<label
|
||||
for="linkedIn"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
|
||||
>
|
||||
LinkedIn
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
fill="#0A66C2"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="linkedIn"
|
||||
type="url"
|
||||
formControlName="linkedIn"
|
||||
placeholder="https://linkedin.com/in/votreprofil"
|
||||
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>
|
||||
</div>
|
||||
<!-- Site Web -->
|
||||
<div>
|
||||
<label for="web" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Site Web
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg
|
||||
class="w-5 h-5 text-gray-400"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M4.083 9h1.946c.089-1.546.383-2.97.837-4.118A6.004 6.004 0 004.083 9zM10 2a8 8 0 100 16 8 8 0 000-16zm0 2c-.076 0-.232.032-.465.262-.238.234-.497.623-.737 1.182-.389.907-.673 2.142-.766 3.556h3.936c-.093-1.414-.377-2.649-.766-3.556-.24-.56-.5-.948-.737-1.182C10.232 4.032 10.076 4 10 4zm3.971 5c-.089-1.546-.383-2.97-.837-4.118A6.004 6.004 0 0115.917 9h-1.946zm-2.003 2H8.032c.093 1.414.377 2.649.766 3.556.24.56.5.948.737 1.182.233.23.389.262.465.262.076 0 .232-.032.465-.262.238-.234.498-.623.737-1.182.389-.907.673-2.142.766-3.556zm1.166 4.118c.454-1.147.748-2.572.837-4.118h1.946a6.004 6.004 0 01-2.783 4.118zm-6.268 0C6.412 13.97 6.118 12.546 6.03 11H4.083a6.004 6.004 0 002.783 4.118z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="web"
|
||||
type="url"
|
||||
formControlName="web"
|
||||
placeholder="https://votresite.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>
|
||||
</div>
|
||||
<!-- X (Twitter) -->
|
||||
<div>
|
||||
<label for="x" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
X (Twitter)
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
fill="#000000"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="x"
|
||||
type="url"
|
||||
formControlName="x"
|
||||
placeholder="https://x.com/votreprofil"
|
||||
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>
|
||||
</div>
|
||||
<!-- Youtube -->
|
||||
<div>
|
||||
<label for="x" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Youtube
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
fill="#FF0000"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>YouTube</title>
|
||||
<path
|
||||
d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="x"
|
||||
type="url"
|
||||
formControlName="youTube"
|
||||
placeholder="https://youtube.com/votreprofil"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end animate-fade-in animation-delay-400">
|
||||
<button
|
||||
type="submit"
|
||||
[disabled]="profileForm.invalid"
|
||||
class="px-8 py-3 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"
|
||||
>
|
||||
<span class="flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M7.707 10.293a1 1 0 10-1.414 1.414l3 3a1 1 0 001.414 0l3-3a1 1 0 00-1.414-1.414L11 11.586V6h5a2 2 0 012 2v7a2 2 0 01-2 2H4a2 2 0 01-2-2V8a2 2 0 012-2h5v5.586l-1.293-1.293zM9 4a1 1 0 012 0v2H9V4z"
|
||||
/>
|
||||
</svg>
|
||||
Sauvegarder les modifications
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 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;
|
||||
}
|
||||
|
||||
.animation-delay-400 {
|
||||
animation-delay: 0.4s;
|
||||
opacity: 0;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,98 +1,189 @@
|
||||
@if (projectId) {
|
||||
@if (projectId == 'add'.toLowerCase()) {
|
||||
<app-project-picture-form [project]="undefined" />
|
||||
} @else {
|
||||
<app-project-picture-form [project]="project()" />
|
||||
}
|
||||
<div class="space-y-6">
|
||||
<!-- Section Image du projet -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 animate-fade-in">
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div
|
||||
class="w-10 h-10 bg-indigo-100 dark:bg-indigo-900 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-indigo-600 dark:text-indigo-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Image du projet</h3>
|
||||
</div>
|
||||
|
||||
<h3 class="font-ubuntu w-full text-start font-bold text-xl uppercase dark:text-white my-5">
|
||||
Information du projet
|
||||
</h3>
|
||||
|
||||
@if (loading().isLoading) {
|
||||
@switch (loading().action) {
|
||||
@case (ActionType.NONE || ActionType.CREATE || ActionType.DELETE) {}
|
||||
@default {
|
||||
<p>Chargement...</p>
|
||||
@if (projectId == 'add'.toLowerCase()) {
|
||||
<app-project-picture-form [project]="undefined" />
|
||||
} @else {
|
||||
<app-project-picture-form [project]="project()" />
|
||||
}
|
||||
}
|
||||
} @else {
|
||||
<form class="mx-8" [formGroup]="projectForm" (ngSubmit)="onSubmit()">
|
||||
<label class="mb-2 text-sm text-black block dark:text-white">Nom</label>
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="nom du projet"
|
||||
formControlName="nom"
|
||||
class="pr-4 pl-14 py-3 text-sm text-black rounded bg-white border border-gray-400 w-full outline-[#333]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="absolute left-4">
|
||||
<!-- Section Informations du projet -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 animate-fade-in animation-delay-100"
|
||||
>
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div
|
||||
class="w-10 h-10 bg-purple-100 dark:bg-purple-900 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="22px"
|
||||
height="22px"
|
||||
fill="#bbb"
|
||||
viewBox="0 0 512 512"
|
||||
class="w-6 h-6 text-purple-600 dark:text-purple-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z" />
|
||||
<path
|
||||
d="M437.02 74.981C388.667 26.629 324.38 0 256 0S123.333 26.629 74.98 74.981C26.629 123.333 0 187.62 0 256s26.629 132.667 74.98 181.019C123.333 485.371 187.62 512 256 512s132.667-26.629 181.02-74.981C485.371 388.667 512 324.38 512 256s-26.629-132.667-74.98-181.019zM256 482c-66.869 0-127.037-29.202-168.452-75.511C113.223 338.422 178.948 290 256 290c-49.706 0-90-40.294-90-90s40.294-90 90-90 90 40.294 90 90-40.294 90-90 90c77.052 0 142.777 48.422 168.452 116.489C383.037 452.798 322.869 482 256 482z"
|
||||
data-original="#000000"
|
||||
></path>
|
||||
fill-rule="evenodd"
|
||||
d="M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm3 4a1 1 0 000 2h.01a1 1 0 100-2H7zm3 0a1 1 0 000 2h3a1 1 0 100-2h-3zm-3 4a1 1 0 100 2h.01a1 1 0 100-2H7zm3 0a1 1 0 100 2h3a1 1 0 100-2h-3z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Informations du projet</h3>
|
||||
</div>
|
||||
|
||||
<label class="mb-2 text-sm text-black block dark:text-white">Lien</label>
|
||||
<div class="relative flex items-center">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="lien vers votre projet ex : http://monprojet"
|
||||
formControlName="lien"
|
||||
class="pr-4 pl-14 py-3 text-sm text-black rounded bg-white border border-gray-400 w-full outline-[#333]"
|
||||
/>
|
||||
@if (loading().isLoading) {
|
||||
@switch (loading().action) {
|
||||
@case (ActionType.NONE || ActionType.CREATE || ActionType.DELETE) {}
|
||||
@default {
|
||||
<!-- Loader -->
|
||||
<app-loading message="Chargement du projet..." />
|
||||
}
|
||||
}
|
||||
} @else {
|
||||
<!-- Formulaire -->
|
||||
<form [formGroup]="projectForm" (ngSubmit)="onSubmit()" class="space-y-6">
|
||||
<!-- Champ Nom -->
|
||||
<div class="space-y-2">
|
||||
<label for="nom" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Nom du projet
|
||||
</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 6a2 2 0 012-2h5l2 2h5a2 2 0 012 2v6a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" />
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="nom"
|
||||
type="text"
|
||||
formControlName="nom"
|
||||
placeholder="Nom du projet"
|
||||
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>
|
||||
</div>
|
||||
|
||||
<div class="absolute left-4">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="22px"
|
||||
height="22px"
|
||||
fill="#bbb"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M437.02 74.981C388.667 26.629 324.38 0 256 0S123.333 26.629 74.98 74.981C26.629 123.333 0 187.62 0 256s26.629 132.667 74.98 181.019C123.333 485.371 187.62 512 256 512s132.667-26.629 181.02-74.981C485.371 388.667 512 324.38 512 256s-26.629-132.667-74.98-181.019zM256 482c-66.869 0-127.037-29.202-168.452-75.511C113.223 338.422 178.948 290 256 290c-49.706 0-90-40.294-90-90s40.294-90 90-90 90 40.294 90 90-40.294 90-90 90c77.052 0 142.777 48.422 168.452 116.489C383.037 452.798 322.869 482 256 482z"
|
||||
data-original="#000000"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Champ Lien -->
|
||||
<div class="space-y-2">
|
||||
<label for="lien" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Lien du projet
|
||||
</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
|
||||
fill-rule="evenodd"
|
||||
d="M12.586 4.586a2 2 0 112.828 2.828l-3 3a2 2 0 01-2.828 0 1 1 0 00-1.414 1.414 4 4 0 005.656 0l3-3a4 4 0 00-5.656-5.656l-1.5 1.5a1 1 0 101.414 1.414l1.5-1.5zm-5 5a2 2 0 012.828 0 1 1 0 101.414-1.414 4 4 0 00-5.656 0l-3 3a4 4 0 105.656 5.656l1.5-1.5a1 1 0 10-1.414-1.414l-1.5 1.5a2 2 0 11-2.828-2.828l3-3z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="lien"
|
||||
type="url"
|
||||
formControlName="lien"
|
||||
placeholder="https://monprojet.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>
|
||||
</div>
|
||||
|
||||
<label class="mb-2 text-sm text-black block dark:text-white">description</label>
|
||||
<div class="relative flex items-center">
|
||||
<textarea
|
||||
placeholder="Type Message"
|
||||
formControlName="description"
|
||||
class="p-4 bg-white w-full block text-sm border border-gray-300 outline-[#007bff] rounded"
|
||||
rows="4"
|
||||
></textarea>
|
||||
</div>
|
||||
<!-- Champ Description -->
|
||||
<div class="space-y-2">
|
||||
<label
|
||||
for="description"
|
||||
class="block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
id="description"
|
||||
formControlName="description"
|
||||
placeholder="Décrivez votre projet..."
|
||||
rows="4"
|
||||
class="w-full px-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 resize-none"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
[ngClass]="{ 'bg-purple-600': projectForm.valid }"
|
||||
class="!mt-8 px-6 py-2 w-full bg-[#333] hover:bg-[#444] text-sm text-white mx-auto block"
|
||||
>
|
||||
Sauvegarder
|
||||
</button>
|
||||
<!-- Boutons d'action -->
|
||||
<div class="flex flex-col sm:flex-row gap-3 pt-4">
|
||||
<button
|
||||
type="submit"
|
||||
[disabled]="projectForm.invalid"
|
||||
class="flex-1 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"
|
||||
>
|
||||
<span class="flex items-center justify-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M7.707 10.293a1 1 0 10-1.414 1.414l3 3a1 1 0 001.414 0l3-3a1 1 0 00-1.414-1.414L11 11.586V6h5a2 2 0 012 2v7a2 2 0 01-2 2H4a2 2 0 01-2-2V8a2 2 0 012-2h5v5.586l-1.293-1.293zM9 4a1 1 0 012 0v2H9V4z"
|
||||
/>
|
||||
</svg>
|
||||
Sauvegarder
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
(click)="formIsUpdated.emit(null)"
|
||||
class="!mt-8 px-6 py-2 w-full bg-[#333] hover:bg-[#444] text-sm text-white mx-auto block"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
</form>
|
||||
}
|
||||
<button
|
||||
type="button"
|
||||
(click)="formIsUpdated.emit(null)"
|
||||
class="flex-1 sm:flex-initial py-3 px-6 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-900 dark:text-white font-medium rounded-lg transition-all duration-200"
|
||||
>
|
||||
<span class="flex items-center justify-center gap-2">
|
||||
<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="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
Annuler
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
.animation-delay-100 {
|
||||
animation-delay: 0.1s;
|
||||
opacity: 0;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,18 @@ import { AuthService } from '@app/core/services/authentication/auth.service';
|
||||
import { ProjectFacade } from '@app/ui/projects/project.facade';
|
||||
import { CreateProjectDto } from '@app/domain/projects/dto/create-project.dto';
|
||||
import { ActionType } from '@app/domain/action-type.util';
|
||||
import { LoadingComponent } from '@app/shared/components/loading/loading.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-my-profile-update-project-form',
|
||||
standalone: true,
|
||||
imports: [PaginatorModule, ReactiveFormsModule, NgClass, ProjectPictureFormComponent],
|
||||
imports: [
|
||||
PaginatorModule,
|
||||
ReactiveFormsModule,
|
||||
NgClass,
|
||||
ProjectPictureFormComponent,
|
||||
LoadingComponent,
|
||||
],
|
||||
templateUrl: './my-profile-update-project-form.component.html',
|
||||
styleUrl: './my-profile-update-project-form.component.scss',
|
||||
})
|
||||
|
||||
@@ -1,32 +1,56 @@
|
||||
@if (project) {
|
||||
<div class="bg-white rounded-2xl border p-6">
|
||||
@if (project.fichier) {
|
||||
<img
|
||||
alt="{{ project.nom }}"
|
||||
class="object-cover object-center h-full w-full"
|
||||
src="{{ environment.baseUrl }}/api/files/projets/{{ project.id }}/{{ project.fichier }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
} @else {
|
||||
<img
|
||||
alt="nouveau-projet"
|
||||
class="object-cover object-center h-full w-full"
|
||||
src="https://api.dicebear.com/9.x/shapes/svg?seed={{ project.nom }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
}
|
||||
<div
|
||||
class="group bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden transition-all duration-300 hover:shadow-2xl hover:-translate-y-2 animate-fade-in"
|
||||
>
|
||||
<!-- Image du projet -->
|
||||
<div class="relative h-48 overflow-hidden bg-gray-100 dark:bg-gray-700">
|
||||
@if (project.fichier) {
|
||||
<img
|
||||
alt="{{ project.nom }}"
|
||||
class="w-full h-full object-cover object-center transition-transform duration-500 group-hover:scale-110"
|
||||
src="{{ environment.baseUrl }}/api/files/projets/{{ project.id }}/{{ project.fichier }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
} @else {
|
||||
<img
|
||||
alt="{{ project.nom }}"
|
||||
class="w-full h-full object-cover object-center transition-transform duration-500 group-hover:scale-110 opacity-60"
|
||||
src="https://api.dicebear.com/9.x/shapes/svg?seed={{ project.nom }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
}
|
||||
|
||||
<div class="mt-6">
|
||||
<h3 class="text-lg font-bold text-gray-800 mb-3">{{ project.nom }}</h3>
|
||||
<p class="text-gray-800 text-sm">{{ project.description }}</p>
|
||||
<div class="mt-6">
|
||||
<!-- Overlay gradient au hover -->
|
||||
<div
|
||||
class="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<!-- Contenu -->
|
||||
<div class="p-6 space-y-4">
|
||||
<!-- Titre -->
|
||||
<h3
|
||||
class="text-xl font-bold text-gray-900 dark:text-white line-clamp-2 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition-colors"
|
||||
>
|
||||
{{ project.nom }}
|
||||
</h3>
|
||||
|
||||
<!-- Description -->
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300 line-clamp-3">
|
||||
{{ project.description || 'Aucune description disponible.' }}
|
||||
</p>
|
||||
|
||||
<!-- Lien vers le projet -->
|
||||
@if (project.lien) {
|
||||
<a
|
||||
class="text-indigo-500 inline-flex items-center md:mb-2 lg:mb-0"
|
||||
href="{{ project.lien }}"
|
||||
[href]="project.lien"
|
||||
target="_blank"
|
||||
>Explore
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center gap-2 text-indigo-600 dark:text-indigo-400 hover:text-indigo-700 dark:hover:text-indigo-300 font-medium text-sm transition-all duration-200 group/link"
|
||||
>
|
||||
<span>Explorer le projet</span>
|
||||
<svg
|
||||
class="w-4 h-4 ml-2"
|
||||
class="w-4 h-4 transition-transform duration-200 group-hover/link:translate-x-1"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
@@ -38,7 +62,12 @@
|
||||
<path d="M12 5l7 7-7 7"></path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Bordure animée au hover -->
|
||||
<div
|
||||
class="absolute inset-0 border-2 border-transparent group-hover:border-indigo-500 rounded-xl transition-all duration-300 pointer-events-none"
|
||||
></div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
/* Line clamp pour limiter le texte */
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.line-clamp-3 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,41 @@
|
||||
<div class="min-h-screen py-4 font-sans">
|
||||
<div class="max-w-4xl max-lg:max-w-2xl max-sm:max-w-sm mx-auto">
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-8">Explorer les projets</h2>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
@for (project of projects(); track project) {
|
||||
<app-project-item [project]="project" />
|
||||
} @empty {
|
||||
<p>Aucun projet</p>
|
||||
}
|
||||
</div>
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<!-- Grid des projets -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@for (project of projects(); track project.id) {
|
||||
<app-project-item [project]="project" />
|
||||
} @empty {
|
||||
<!-- Message si aucun projet -->
|
||||
<div class="col-span-full">
|
||||
<div
|
||||
class="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-700 rounded-xl p-12 text-center"
|
||||
>
|
||||
<div
|
||||
class="inline-flex w-20 h-20 bg-white dark:bg-gray-600 rounded-full items-center justify-center mb-6 shadow-lg"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-10 h-10 text-gray-400 dark:text-gray-500"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-3">
|
||||
Aucun projet disponible
|
||||
</h3>
|
||||
<p class="text-gray-600 dark:text-gray-300 max-w-md mx-auto">
|
||||
Il n'y a pas encore de projets à explorer. Revenez plus tard pour découvrir de nouvelles
|
||||
réalisations !
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,107 @@
|
||||
<form
|
||||
class="space-y-6 px-4 max-w-sm mx-auto font-[sans-serif]"
|
||||
[formGroup]="userForm"
|
||||
(ngSubmit)="onUserFormSubmit()"
|
||||
class="w-full space-y-6 animate-fade-in"
|
||||
>
|
||||
<h3 class="font-ubuntu font-bold text-xl uppercase dark:text-white mb-4">Mon Identité</h3>
|
||||
|
||||
<div class="flex items-center">
|
||||
<label class="text-gray-400 w-36 text-xs">Nom</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter your name"
|
||||
formControlName="name"
|
||||
class="px-4 py-3 bg-gray-100 focus:bg-transparent w-full text-xs outline-[#333] rounded-sm transition-all"
|
||||
/>
|
||||
<!-- Titre -->
|
||||
<div class="flex items-center gap-3 pb-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div
|
||||
class="w-10 h-10 bg-indigo-100 dark:bg-indigo-900 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-indigo-600 dark:text-indigo-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Mon identité</h3>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<label class="text-gray-400 w-36 text-xs">Prénom</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="entre votre prénom"
|
||||
formControlName="firstname"
|
||||
class="px-4 py-3 bg-gray-100 focus:bg-transparent w-full text-xs outline-[#333] rounded-sm transition-all"
|
||||
/>
|
||||
<!-- Champ Nom -->
|
||||
<div class="space-y-2">
|
||||
<label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Nom
|
||||
</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
|
||||
fill-rule="evenodd"
|
||||
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="name"
|
||||
type="text"
|
||||
formControlName="name"
|
||||
placeholder="Votre nom"
|
||||
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>
|
||||
</div>
|
||||
|
||||
<!-- Champ Prénom -->
|
||||
<div class="space-y-2">
|
||||
<label for="firstname" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Prénom
|
||||
</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
|
||||
fill-rule="evenodd"
|
||||
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
id="firstname"
|
||||
type="text"
|
||||
formControlName="firstname"
|
||||
placeholder="Votre prénom"
|
||||
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>
|
||||
</div>
|
||||
|
||||
<!-- Bouton de soumission -->
|
||||
<button
|
||||
type="submit"
|
||||
[ngClass]="{ 'bg-purple-600': userForm.valid }"
|
||||
class="!mt-4 px-6 py-2 w-full bg-[#333] hover:bg-[#444] text-sm text-white mx-auto block"
|
||||
[disabled]="userForm.invalid"
|
||||
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"
|
||||
>
|
||||
Modifier mon identité
|
||||
<span class="flex items-center justify-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M7.707 10.293a1 1 0 10-1.414 1.414l3 3a1 1 0 001.414 0l3-3a1 1 0 00-1.414-1.414L11 11.586V6h5a2 2 0 012 2v7a2 2 0 01-2 2H4a2 2 0 01-2-2V8a2 2 0 012-2h5v5.586l-1.293-1.293zM9 4a1 1 0 012 0v2H9V4z"
|
||||
/>
|
||||
</svg>
|
||||
Modifier mon identité
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +35,10 @@ export class UserFormComponent implements OnInit {
|
||||
|
||||
ngOnInit(): void {
|
||||
this.userForm = this.fb.group({
|
||||
firstname: new FormControl(this.user?.name!.split(' ').slice(0, -1).join(' ') ?? '', [
|
||||
firstname: new FormControl(this.user?.name?.split(' ').slice(0, -1).join(' ') ?? '', [
|
||||
Validators.required,
|
||||
]),
|
||||
name: new FormControl(this.user?.name!.split(' ').slice(-1)[0] ?? '', [Validators.required]),
|
||||
name: new FormControl(this.user?.name?.split(' ').slice(-1)[0] ?? '', [Validators.required]),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,59 +2,97 @@
|
||||
<a
|
||||
[routerLink]="[user.username ? user.username : user.id]"
|
||||
[state]="{ user, profile }"
|
||||
class="cursor-pointer"
|
||||
class="block group"
|
||||
>
|
||||
<div class="group text-center text-gray-500 dark:text-gray-400">
|
||||
<!-- Card du profil -->
|
||||
<div
|
||||
class="relative bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden transition-all duration-300 hover:shadow-2xl hover:-translate-y-2"
|
||||
>
|
||||
<!-- Badge vérifié (position absolue en haut à droite) -->
|
||||
@if (profile.estVerifier) {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-6 text-purple-800"
|
||||
>
|
||||
<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>
|
||||
}
|
||||
@if (user.avatar) {
|
||||
<img
|
||||
class="mx-auto mb-4 w-36 h-36 rounded-full grayscale object-cover object-top ransition duration-500 group-hover:scale-105 group-hover:grayscale-0"
|
||||
src="{{ environment.baseUrl }}/api/files/users/{{ user.id }}/{{ user.avatar }}"
|
||||
alt="{{ user.username }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
} @else {
|
||||
<img
|
||||
class="mx-auto mb-4 w-36 h-36 rounded-full grayscale object-cover object-top ransition duration-500 group-hover:scale-105 group-hover:grayscale-0"
|
||||
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{ user.username }}"
|
||||
alt="{{ user.username }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div class="absolute top-3 right-3 z-10 animate-pulse-slow">
|
||||
<div class="bg-purple-500/20 backdrop-blur-md p-2 rounded-full">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="w-6 h-6 text-purple-500"
|
||||
>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div
|
||||
class="translate-y-2 transition duration-300 ease-in-out group-hover:translate-y-0 flex flex-col items-center space-y-2"
|
||||
>
|
||||
<!-- Partie supérieure avec avatar -->
|
||||
<div class="p-6 text-center">
|
||||
<!-- Avatar avec bordure gradient -->
|
||||
<div class="relative inline-block mb-4">
|
||||
<div class="w-32 h-32 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 p-1">
|
||||
@if (user.avatar) {
|
||||
<img
|
||||
class="w-full h-full rounded-full object-cover grayscale group-hover:grayscale-0 transition-all duration-500 group-hover:scale-105"
|
||||
src="{{ environment.baseUrl }}/api/files/users/{{ user.id }}/{{ user.avatar }}"
|
||||
alt="{{ user.username }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
} @else {
|
||||
<img
|
||||
class="w-full h-full rounded-full object-cover grayscale group-hover:grayscale-0 transition-all duration-500 group-hover:scale-105"
|
||||
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{ user.username }}"
|
||||
alt="{{ user.username }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nom -->
|
||||
@if (user.name) {
|
||||
<h3 class="mb-1 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
|
||||
<h3
|
||||
class="text-lg font-bold text-gray-900 dark:text-white mb-2 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition-colors"
|
||||
>
|
||||
{{ user.name }}
|
||||
</h3>
|
||||
} @else if (user.username) {
|
||||
<h3 class="mb-1 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
|
||||
<h3
|
||||
class="text-lg font-bold text-gray-900 dark:text-white mb-2 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition-colors"
|
||||
>
|
||||
{{ user.username }}
|
||||
</h3>
|
||||
} @else {
|
||||
<h3 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white">
|
||||
Non mentionné
|
||||
</h3>
|
||||
<h3 class="text-lg font-bold text-gray-500 dark:text-gray-400 mb-2">Non mentionné</h3>
|
||||
}
|
||||
<p class="font-semibold">{{ profile.profession }}</p>
|
||||
<app-chips [sectorId]="profile.secteur" />
|
||||
<app-reseaux [reseaux]="profile.reseaux" />
|
||||
|
||||
<!-- Profession -->
|
||||
<p class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-4">
|
||||
{{ profile.profession || 'Profession non renseignée' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Partie inférieure avec infos -->
|
||||
<div class="px-6 pb-6 space-y-3 border-t border-gray-100 dark:border-gray-700 pt-4">
|
||||
<!-- Secteur -->
|
||||
<div class="flex justify-center">
|
||||
<app-chips [sectorId]="profile.secteur" />
|
||||
</div>
|
||||
|
||||
<!-- Réseaux sociaux -->
|
||||
@if (profile.reseaux) {
|
||||
<div class="flex justify-center">
|
||||
<app-reseaux [reseaux]="profile.reseaux" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Indicateur de hover (bordure animée) -->
|
||||
<div
|
||||
class="absolute inset-0 border-2 border-transparent group-hover:border-indigo-500 rounded-xl transition-all duration-300 pointer-events-none"
|
||||
></div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
@keyframes pulseSlow {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.animate-pulse-slow {
|
||||
animation: pulseSlow 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,41 @@
|
||||
<section class="bg-white dark:bg-gray-900">
|
||||
<div class="py-8 px-4 mx-auto max-w-screen-xl text-center lg:py-16 lg:px-6">
|
||||
<div class="grid gap-8 lg:gap-16 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
|
||||
<section class="w-full animate-fade-in">
|
||||
<div class="py-8 px-4 mx-auto max-w-screen-xl">
|
||||
<!-- Grid des profils -->
|
||||
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
@for (profile of profiles; track profile.id) {
|
||||
<app-vertical-profile-item [profile]="profile" />
|
||||
} @empty {
|
||||
<p>Aucun profile trouvée</p>
|
||||
<!-- Message si aucun profil -->
|
||||
<div class="col-span-full">
|
||||
<div
|
||||
class="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-700 rounded-xl p-12 text-center"
|
||||
>
|
||||
<div
|
||||
class="inline-flex w-20 h-20 bg-white dark:bg-gray-600 rounded-full items-center justify-center mb-6 shadow-lg"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-10 h-10 text-gray-400 dark:text-gray-500"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-3">
|
||||
Aucun profil trouvé
|
||||
</h3>
|
||||
<p class="text-gray-600 dark:text-gray-300 max-w-md mx-auto">
|
||||
Aucun profil ne correspond à votre recherche. Essayez de modifier vos critères.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,107 +1,170 @@
|
||||
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()" class="w-full space-y-4">
|
||||
<div class="">
|
||||
<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">
|
||||
<span class="absolute block w-3 h-3 -translate-y-1/2 top-1/2 left-1">
|
||||
<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="w-full h-full fill-current"
|
||||
viewBox="0 0 512 512"
|
||||
class="h-5 w-5 text-gray-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
<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>
|
||||
</span>
|
||||
|
||||
</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">
|
||||
<div class="relative">
|
||||
<span class="absolute block w-3 h-3 -translate-y-1/2 top-1/2 left-1">
|
||||
<!-- 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">
|
||||
<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>
|
||||
</div>
|
||||
<input
|
||||
#pwdInput
|
||||
formControlName="password"
|
||||
type="password"
|
||||
id="password"
|
||||
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
|
||||
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"
|
||||
>
|
||||
@if (pwdInput.type === 'password') {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 0 448 512"
|
||||
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
|
||||
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"
|
||||
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>
|
||||
</span>
|
||||
|
||||
<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"
|
||||
/>
|
||||
<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'"
|
||||
>
|
||||
<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>
|
||||
} @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>
|
||||
}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<a [routerLink]="['']" class="text-xs">Mot de passe oublié?</a>
|
||||
} @else {
|
||||
<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>
|
||||
}
|
||||
</button>
|
||||
</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">
|
||||
Vous n'avez pas de compte?
|
||||
<a [routerLink]="['register']" class="font-bold">Créez-en ici</a>
|
||||
|
||||
<!-- 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="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,18 +1,101 @@
|
||||
@if (cv_link()) {
|
||||
<pdf-viewer
|
||||
[src]="cv_link()"
|
||||
[zoom]="1"
|
||||
[rotation]="0"
|
||||
[original-size]="false"
|
||||
[show-all]="true"
|
||||
[fit-to-page]="false"
|
||||
[zoom-scale]="'page-width'"
|
||||
[stick-to-page]="false"
|
||||
[render-text]="true"
|
||||
[external-link-target]="'blank'"
|
||||
[autoresize]="true"
|
||||
[show-borders]="false"
|
||||
/>
|
||||
<!-- Container du PDF viewer -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden animate-fade-in">
|
||||
<!-- Header -->
|
||||
<div
|
||||
class="bg-gradient-to-r from-indigo-600 to-purple-600 px-6 py-4 flex items-center justify-between"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<div
|
||||
class="w-10 h-10 bg-white/20 backdrop-blur-md rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-white"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-bold text-white">Mon CV</h3>
|
||||
</div>
|
||||
|
||||
<!-- Bouton télécharger -->
|
||||
<a
|
||||
[href]="cv_link()"
|
||||
download
|
||||
target="_blank"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-white/20 hover:bg-white/30 backdrop-blur-md rounded-lg text-white text-sm font-medium transition-all duration-200 hover:scale-105"
|
||||
>
|
||||
<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 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<span class="hidden sm:inline">Télécharger</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- PDF Viewer -->
|
||||
<div class="p-4 bg-gray-100 dark:bg-gray-900">
|
||||
<pdf-viewer
|
||||
[src]="cv_link()"
|
||||
[zoom]="1"
|
||||
[rotation]="0"
|
||||
[original-size]="false"
|
||||
[show-all]="true"
|
||||
[fit-to-page]="false"
|
||||
[zoom-scale]="'page-width'"
|
||||
[stick-to-page]="false"
|
||||
[render-text]="true"
|
||||
[external-link-target]="'blank'"
|
||||
[autoresize]="true"
|
||||
[show-borders]="true"
|
||||
class="rounded-lg overflow-hidden shadow-inner"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<p>Aucun curriculum vitae (CV) n'a été rajouté.</p>
|
||||
<!-- Message si aucun CV -->
|
||||
<div
|
||||
class="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-700 rounded-xl p-12 text-center animate-fade-in"
|
||||
>
|
||||
<div
|
||||
class="inline-flex w-20 h-20 bg-white dark:bg-gray-600 rounded-full items-center justify-center mb-6 shadow-lg"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-10 h-10 text-gray-400 dark:text-gray-500"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-3">Aucun CV disponible</h3>
|
||||
<p class="text-gray-600 dark:text-gray-300 max-w-md mx-auto">
|
||||
Aucun curriculum vitae n'a été ajouté pour le moment. Veuillez télécharger votre CV pour le
|
||||
visualiser ici.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,174 +1,273 @@
|
||||
<form [formGroup]="registerForm" (ngSubmit)="onSubmit()" class="w-full space-y-4">
|
||||
<div class="">
|
||||
<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">
|
||||
<span class="absolute block w-3 h-3 -translate-y-1/2 top-1/2 left-1">
|
||||
<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="w-full h-full fill-current"
|
||||
viewBox="0 0 512 512"
|
||||
class="h-5 w-5 text-gray-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<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"
|
||||
/>
|
||||
<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>
|
||||
</span>
|
||||
|
||||
</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">
|
||||
<div class="relative">
|
||||
<span class="absolute block w-3 h-3 -translate-y-1/2 top-1/2 left-1">
|
||||
<!-- 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">
|
||||
<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>
|
||||
</div>
|
||||
<input
|
||||
#pwdInput
|
||||
formControlName="password"
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
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
|
||||
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"
|
||||
>
|
||||
@if (pwdInput.type === 'password') {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 0 448 512"
|
||||
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
|
||||
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"
|
||||
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>
|
||||
</span>
|
||||
|
||||
<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"
|
||||
/>
|
||||
<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'"
|
||||
>
|
||||
<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>
|
||||
} @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>
|
||||
}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<div class="text-right">
|
||||
<div class="relative">
|
||||
<span class="absolute block w-3 h-3 -translate-y-1/2 top-1/2 left-1">
|
||||
} @else {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-full h-full fill-current"
|
||||
viewBox="0 0 448 512"
|
||||
class="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<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"
|
||||
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>
|
||||
|
||||
<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"
|
||||
/>
|
||||
<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'
|
||||
"
|
||||
>
|
||||
<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>
|
||||
} @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>
|
||||
}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</button>
|
||||
</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="relative">
|
||||
<button
|
||||
type="submit"
|
||||
class="text-xs py-2 px-4 border bg-[#002B2F] w-1/2 text-center text-gray-50 rounded-md"
|
||||
>
|
||||
<!-- 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">
|
||||
<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>
|
||||
</div>
|
||||
<input
|
||||
#pwdInputConfirmation
|
||||
formControlName="passwordConfirm"
|
||||
type="password"
|
||||
id="passwordConfirm"
|
||||
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
|
||||
type="button"
|
||||
(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"
|
||||
>
|
||||
@if (pwdInputConfirmation.type === 'password') {
|
||||
<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="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>
|
||||
}
|
||||
</button>
|
||||
</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>
|
||||
|
||||
<!-- 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>
|
||||
<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>
|
||||
</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>
|
||||
}
|
||||
</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>
|
||||
}
|
||||
|
||||
@@ -1,38 +1,34 @@
|
||||
<div class="flex w-full justify-between items-center mb-6">
|
||||
<span
|
||||
(click)="isCancelEditMode.emit(true)"
|
||||
class="cursor-pointer px-3 py-3 text-white bg-purple-600 w-max flex space-x-2 items-center rounded-full justify-center"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12.416 3.376a.75.75 0 0 1 .208 1.04l-5 7.5a.75.75 0 0 1-1.154.114l-3-3a.75.75 0 0 1 1.06-1.06l2.353 2.353 4.493-6.74a.75.75 0 0 1 1.04-.207Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<small>Terminer</small>
|
||||
</span>
|
||||
|
||||
<span
|
||||
(click)="isCancelEditMode.emit(true)"
|
||||
class="cursor-pointer px-3 py-3 text-white bg-red-600 w-max flex space-x-2 items-center rounded-full justify-center"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14Zm2.78-4.22a.75.75 0 0 1-1.06 0L8 9.06l-1.72 1.72a.75.75 0 1 1-1.06-1.06L6.94 8 5.22 6.28a.75.75 0 0 1 1.06-1.06L8 6.94l1.72-1.72a.75.75 0 1 1 1.06 1.06L9.06 8l1.72 1.72a.75.75 0 0 1 0 1.06Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<small>Annuler</small>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@if (user != undefined) {
|
||||
<div class="w-full flex flex-col space-y-4">
|
||||
<app-user-avatar-form [user]="user" />
|
||||
<app-user-form [user]="user" />
|
||||
<div class="space-y-6">
|
||||
<!-- Section Avatar -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 animate-fade-in">
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<div
|
||||
class="w-10 h-10 bg-indigo-100 dark:bg-indigo-900 rounded-lg flex items-center justify-center"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-6 h-6 text-indigo-600 dark:text-indigo-400"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V5a2 2 0 00-2-2H4zm12 12H4l4-8 3 6 2-4 3 6z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Photo de profil</h3>
|
||||
</div>
|
||||
<app-user-avatar-form [user]="user" />
|
||||
</div>
|
||||
|
||||
<!-- Section Informations utilisateur -->
|
||||
<div
|
||||
class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 animate-fade-in animation-delay-100"
|
||||
>
|
||||
<app-user-form [user]="user" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
.animation-delay-100 {
|
||||
animation-delay: 0.1s;
|
||||
opacity: 0;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, Input, output } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { User } from '@app/shared/models/user';
|
||||
import { UserFormComponent } from '@app/shared/components/user-form/user-form.component';
|
||||
import { UserAvatarFormComponent } from '@app/shared/components/user-avatar-form/user-avatar-form.component';
|
||||
@@ -12,6 +12,4 @@ import { UserAvatarFormComponent } from '@app/shared/components/user-avatar-form
|
||||
})
|
||||
export class UpdateUserComponent {
|
||||
@Input({ required: true }) user: User | undefined = undefined;
|
||||
onFormSubmitted = output<any>();
|
||||
isCancelEditMode = output<boolean>();
|
||||
}
|
||||
|
||||
@@ -32,23 +32,47 @@ global.fetch = jest.fn(() =>
|
||||
})
|
||||
) as jest.Mock;
|
||||
|
||||
// 🟡 Ignore le warning pdfjs-dist
|
||||
// 🟡 Ignore les warnings pdfjs-dist
|
||||
const originalWarn = console.warn;
|
||||
console.warn = (...args) => {
|
||||
if (
|
||||
typeof args[0] === 'string' &&
|
||||
args[0].includes('ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG')
|
||||
) {
|
||||
const message = typeof args[0] === 'string' ? args[0] : '';
|
||||
|
||||
// Liste des warnings à ignorer
|
||||
const ignoredWarnings = [
|
||||
'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG',
|
||||
'loadPackages',
|
||||
'pdfjs-dist',
|
||||
];
|
||||
|
||||
if (ignoredWarnings.some((warning) => message.includes(warning))) {
|
||||
return;
|
||||
}
|
||||
|
||||
originalWarn(...args);
|
||||
};
|
||||
|
||||
// 🔴 Ignore le log angularx-qrcode après les tests
|
||||
const originalError = console.error;
|
||||
console.error = (...args) => {
|
||||
if (typeof args[0] === 'string' && args[0].includes('[angularx-qrcode]')) {
|
||||
// 🟡 Ignore les logs angularx-qrcode
|
||||
const originalLog = console.log;
|
||||
console.log = (...args) => {
|
||||
const message = typeof args[0] === 'string' ? args[0] : '';
|
||||
|
||||
const ignoredLogs = ['Warning: loadPackages', 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG'];
|
||||
|
||||
if (ignoredLogs.some((log) => message.includes(log))) {
|
||||
return;
|
||||
}
|
||||
|
||||
originalLog(...args);
|
||||
};
|
||||
|
||||
// 🔴 Ignore les errors angularx-qrcode
|
||||
const originalError = console.error;
|
||||
console.error = (...args) => {
|
||||
const message = typeof args[0] === 'string' ? args[0] : '';
|
||||
|
||||
if (message.includes('[angularx-qrcode]')) {
|
||||
return;
|
||||
}
|
||||
|
||||
originalError(...args);
|
||||
};
|
||||
|
||||
9
suppress-deprecation-warnings.js
Normal file
9
suppress-deprecation-warnings.js
Normal file
@@ -0,0 +1,9 @@
|
||||
// suppress-deprecation-warnings.js
|
||||
const originalEmitWarning = process.emitWarning;
|
||||
|
||||
process.emitWarning = (warning, type, code, ...args) => {
|
||||
if (code === 'DEP0040') {
|
||||
return;
|
||||
}
|
||||
return originalEmitWarning.call(process, warning, type, code, ...args);
|
||||
};
|
||||
Reference in New Issue
Block a user