Merge pull request 'profiles => format clean archi' (#1) from clean-arch into main
Reviewed-on: #1
This commit is contained in:
4
.actrc
4
.actrc
@@ -1,3 +1,3 @@
|
|||||||
--container-architecture linux/arm64
|
--container-architecture linux/amd64
|
||||||
-W .gitea/workflows
|
-W .gitea/workflows
|
||||||
-P ubuntu-latest=node:20-bullseye
|
-P ubuntu-latest==ghcr.io/catthehacker/ubuntu:act-latest
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Docker Build Check
|
name: Build Check
|
||||||
|
|
||||||
# Déclencheur pour chaque pull request
|
# Déclencheur pour chaque pull request
|
||||||
on:
|
on:
|
||||||
|
|||||||
7
.prettierrc
Normal file
7
.prettierrc
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"semi": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
||||||
4
CMD.md
4
CMD.md
@@ -1,3 +1,7 @@
|
|||||||
|
# commande Tree
|
||||||
|
```bash
|
||||||
|
tree -a -I 'node_modules|coverage|logs|.vscode|.git|dist|.git|.venv|__pycache__'
|
||||||
|
```
|
||||||
# Utilisation de `act` avec des workflows Gitea
|
# Utilisation de `act` avec des workflows Gitea
|
||||||
```bash
|
```bash
|
||||||
# Lister les workflows trouvés sous .gitea/workflows
|
# Lister les workflows trouvés sous .gitea/workflows
|
||||||
|
|||||||
64
README.md
64
README.md
@@ -2,3 +2,67 @@
|
|||||||
|
|
||||||
## Description
|
## Description
|
||||||
Trouve Ton Profile est une plateforme innovante qui permet à chacun de trouver facilement des professionnels qualifiés dans tous les domaines de la vie quotidienne. Que tu cherches un électricien, un plombier, un développeur, ou tout autre expert, la plateforme te permet de parcourir rapidement des profils, de découvrir leurs compétences, et de sélectionner la personne idéale pour répondre à tes besoins. Conçue pour faciliter l'accès à des talents locaux et spécialisés, Trouve Ton Profile est l'outil incontournable pour simplifier les tâches du quotidien en connectant les personnes à des solutions fiables et adaptées.
|
Trouve Ton Profile est une plateforme innovante qui permet à chacun de trouver facilement des professionnels qualifiés dans tous les domaines de la vie quotidienne. Que tu cherches un électricien, un plombier, un développeur, ou tout autre expert, la plateforme te permet de parcourir rapidement des profils, de découvrir leurs compétences, et de sélectionner la personne idéale pour répondre à tes besoins. Conçue pour faciliter l'accès à des talents locaux et spécialisés, Trouve Ton Profile est l'outil incontournable pour simplifier les tâches du quotidien en connectant les personnes à des solutions fiables et adaptées.
|
||||||
|
|
||||||
|
# Architecture du Projet
|
||||||
|
|
||||||
|
- domain : modèles métiers + interfaces de dépôt (ports) + (optionnel) erreurs/valeurs.
|
||||||
|
- usecase : cas d’usage (use cases) qui orchestrent le métier (pur TS).
|
||||||
|
- infrastructure : implémentations techniques (HTTP/PocketBase, mapping DTO ⇄ domaine).
|
||||||
|
- ui (Angular) : composants/containers/facade → appellent les use cases via DI.
|
||||||
|
|
||||||
|
```mathematica
|
||||||
|
┌───────────────────────────────────────────────────┐
|
||||||
|
│ UI (Angular) │
|
||||||
|
│ Composants, Facades, Formulaires │
|
||||||
|
│ ➜ Appelle les Use Cases │
|
||||||
|
└───────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌───────────────────────────────────────────────────┐
|
||||||
|
│ Application / Use Cases │
|
||||||
|
│ Exemple : ListProfilesUseCase │
|
||||||
|
│ ➜ Appelle les interfaces du domaine │
|
||||||
|
└───────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌───────────────────────────────────────────────────┐
|
||||||
|
│ Domaine (Core) │
|
||||||
|
│ Interfaces (ProfileRepository), Entities │
|
||||||
|
│ ➜ Ne dépend de rien │
|
||||||
|
└───────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌───────────────────────────────────────────────────┐
|
||||||
|
│ Infrastructure (Technique) │
|
||||||
|
│ Implémentations : PbProfileRepository │
|
||||||
|
│ API HTTP, PocketBase, LocalStorage │
|
||||||
|
│ ➜ Implémente l’interface du domaine │
|
||||||
|
└───────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
```
|
||||||
|
# Le présenteurs
|
||||||
|
Un Presenter (ou ViewModel / Mapper) sert à :<br>
|
||||||
|
|
||||||
|
✔ Transformer les données métier (Domain Model) → en données prêtes pour l’affichage (UI Model)<br>
|
||||||
|
✔ Préparer les données : formatage de date, texte tronqué, fusion de champs…<br>
|
||||||
|
✔ Éviter que tes composants Angular fassent eux-mêmes du .toUpperCase(), .slice(), etc.<br>
|
||||||
|
✔ Laisser le domaine pur, sans logique de présentation<br>
|
||||||
|
|
||||||
|
```
|
||||||
|
UI (Component)
|
||||||
|
⬇
|
||||||
|
Facade (coordonne l’action)
|
||||||
|
⬇
|
||||||
|
UseCase (app logic)
|
||||||
|
⬇
|
||||||
|
ProfileRepository (interface domain)
|
||||||
|
⬇
|
||||||
|
Infrastructure (PocketBase / HTTP)
|
||||||
|
⬇
|
||||||
|
▶ DATA RAW (Profile from database)
|
||||||
|
⬆
|
||||||
|
Presenter (transforme en UI-friendly data)
|
||||||
|
⬆
|
||||||
|
UI displays ViewModel
|
||||||
|
|
||||||
|
```
|
||||||
|
|||||||
212
angular.json
212
angular.json
@@ -1,98 +1,114 @@
|
|||||||
{
|
{
|
||||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"newProjectRoot": "projects",
|
"newProjectRoot": "projects",
|
||||||
"projects": {
|
"projects": {
|
||||||
"TrouveTonProfile": {
|
"TrouveTonProfile": {
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"schematics": {
|
"schematics": {
|
||||||
"@schematics/angular:component": {
|
"@schematics/angular:component": {
|
||||||
"style": "scss"
|
"style": "scss"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "",
|
"root": "",
|
||||||
"sourceRoot": "src",
|
"sourceRoot": "src",
|
||||||
"prefix": "app",
|
"prefix": "app",
|
||||||
"architect": {
|
"architect": {
|
||||||
"build": {
|
"build": {
|
||||||
"builder": "@angular-devkit/build-angular:application",
|
"builder": "@angular-devkit/build-angular:application",
|
||||||
"options": {
|
"options": {
|
||||||
"outputPath": "dist/",
|
"allowedCommonJsDependencies": [
|
||||||
"index": "src/index.html",
|
"pocketbase"
|
||||||
"browser": "src/main.ts",
|
],
|
||||||
"polyfills": [
|
"outputPath": "dist/",
|
||||||
"zone.js"
|
"index": "src/index.html",
|
||||||
],
|
"browser": "src/main.ts",
|
||||||
"tsConfig": "tsconfig.app.json",
|
"polyfills": [
|
||||||
"inlineStyleLanguage": "scss",
|
"zone.js"
|
||||||
"assets": [
|
],
|
||||||
"src/favicon.ico",
|
"tsConfig": "tsconfig.app.json",
|
||||||
"src/assets"
|
"inlineStyleLanguage": "scss",
|
||||||
],
|
"assets": [
|
||||||
"styles": [
|
"src/favicon.ico",
|
||||||
"node_modules/primeng/resources/themes/lara-light-blue/theme.css",
|
"src/assets"
|
||||||
"node_modules/primeng/resources/primeng.min.css",
|
],
|
||||||
"node_modules/primeicons/primeicons.css",
|
"styles": [
|
||||||
"node_modules/ngx-toastr/toastr.css",
|
"node_modules/primeng/resources/themes/lara-light-blue/theme.css",
|
||||||
"src/styles.scss"
|
"node_modules/primeng/resources/primeng.min.css",
|
||||||
],
|
"node_modules/primeicons/primeicons.css",
|
||||||
"scripts": [],
|
"node_modules/ngx-toastr/toastr.css",
|
||||||
"server": "src/main.server.ts",
|
"src/styles.scss"
|
||||||
"prerender": false,
|
],
|
||||||
"ssr": false
|
"scripts": [],
|
||||||
},
|
"server": "src/main.server.ts",
|
||||||
"configurations": {
|
"prerender": false,
|
||||||
"production": {
|
"ssr": false
|
||||||
"budgets": [
|
},
|
||||||
{
|
"configurations": {
|
||||||
"type": "initial",
|
"production": {
|
||||||
"maximumWarning": "500kb",
|
"budgets": [
|
||||||
"maximumError": "1mb"
|
{
|
||||||
},
|
"type": "initial",
|
||||||
{
|
"maximumWarning": "500kb",
|
||||||
"type": "anyComponentStyle",
|
"maximumError": "1mb"
|
||||||
"maximumWarning": "2kb",
|
},
|
||||||
"maximumError": "4kb"
|
{
|
||||||
}
|
"type": "anyComponentStyle",
|
||||||
],
|
"maximumWarning": "2kb",
|
||||||
"outputHashing": "all"
|
"maximumError": "4kb"
|
||||||
},
|
}
|
||||||
"development": {
|
],
|
||||||
"optimization": false,
|
"outputHashing": "all"
|
||||||
"extractLicenses": false,
|
},
|
||||||
"sourceMap": true,
|
"development": {
|
||||||
"fileReplacements": [
|
"optimization": false,
|
||||||
{
|
"extractLicenses": false,
|
||||||
"replace": "src/environments/environment.ts",
|
"sourceMap": true,
|
||||||
"with": "src/environments/environment.development.ts"
|
"fileReplacements": [
|
||||||
}
|
{
|
||||||
]
|
"replace": "src/environments/environment.ts",
|
||||||
}
|
"with": "src/environments/environment.development.ts"
|
||||||
},
|
}
|
||||||
"defaultConfiguration": "production"
|
]
|
||||||
},
|
}
|
||||||
"serve": {
|
},
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
"defaultConfiguration": "production"
|
||||||
"configurations": {
|
},
|
||||||
"production": {
|
"serve": {
|
||||||
"buildTarget": "TrouveTonProfile:build:production"
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
},
|
"configurations": {
|
||||||
"development": {
|
"production": {
|
||||||
"buildTarget": "TrouveTonProfile:build:development"
|
"buildTarget": "TrouveTonProfile:build:production"
|
||||||
}
|
},
|
||||||
},
|
"development": {
|
||||||
"defaultConfiguration": "development"
|
"buildTarget": "TrouveTonProfile:build:development"
|
||||||
},
|
}
|
||||||
"extract-i18n": {
|
},
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
"defaultConfiguration": "development"
|
||||||
"options": {
|
},
|
||||||
"buildTarget": "TrouveTonProfile:build"
|
"extract-i18n": {
|
||||||
}
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
}
|
"options": {
|
||||||
}
|
"buildTarget": "TrouveTonProfile:build"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cli": {
|
"lint": {
|
||||||
"analytics": "dda3ec82-e13e-4042-ae63-71d138479518"
|
"builder": "@angular-eslint/builder:lint",
|
||||||
}
|
"options": {
|
||||||
}
|
"lintFilePatterns": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.html"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cli": {
|
||||||
|
"analytics": "dda3ec82-e13e-4042-ae63-71d138479518",
|
||||||
|
"schematicCollections": [
|
||||||
|
"angular-eslint",
|
||||||
|
"angular-eslint"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
43
eslint.config.js
Normal file
43
eslint.config.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// @ts-check
|
||||||
|
const eslint = require("@eslint/js");
|
||||||
|
const tseslint = require("typescript-eslint");
|
||||||
|
const angular = require("angular-eslint");
|
||||||
|
|
||||||
|
module.exports = tseslint.config(
|
||||||
|
{
|
||||||
|
files: ["**/*.ts"],
|
||||||
|
extends: [
|
||||||
|
eslint.configs.recommended,
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
...tseslint.configs.stylistic,
|
||||||
|
...angular.configs.tsRecommended,
|
||||||
|
],
|
||||||
|
processor: angular.processInlineTemplates,
|
||||||
|
rules: {
|
||||||
|
"@angular-eslint/directive-selector": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
type: "attribute",
|
||||||
|
prefix: "app",
|
||||||
|
style: "camelCase",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"@angular-eslint/component-selector": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
prefix: "app",
|
||||||
|
style: "kebab-case",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["**/*.html"],
|
||||||
|
extends: [
|
||||||
|
...angular.configs.templateRecommended,
|
||||||
|
...angular.configs.templateAccessibility,
|
||||||
|
],
|
||||||
|
rules: {},
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -3,15 +3,15 @@ module.exports = {
|
|||||||
rootDir: '.',
|
rootDir: '.',
|
||||||
setupFilesAfterEnv: ['<rootDir>/src/setup-jest.ts'],
|
setupFilesAfterEnv: ['<rootDir>/src/setup-jest.ts'],
|
||||||
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/dist/'],
|
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/dist/'],
|
||||||
|
testEnvironment: 'jsdom',
|
||||||
transform: {
|
transform: {
|
||||||
'^.+\\.ts$': 'ts-jest', // Only transform .ts files
|
'^.+\\.ts$': 'ts-jest', // Only transform .ts files
|
||||||
},
|
},
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
'/node_modules/(?!flat)/', // Exclude modules except 'flat' from transformation
|
'/node_modules/(?!flat)/',
|
||||||
],
|
],
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'^@app/(.*)$': '<rootDir>/src/app/$1',
|
'^@app/(.*)$': '<rootDir>/src/app/$1',
|
||||||
'^@env/(.*)$': '<rootDir>/src/environments/$1',
|
'^@env/(.*)$': '<rootDir>/src/environments/$1',
|
||||||
},
|
},
|
||||||
testTimeout: 30000,
|
|
||||||
};
|
};
|
||||||
|
|||||||
39345
package-lock.json
generated
39345
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
141
package.json
141
package.json
@@ -1,65 +1,76 @@
|
|||||||
{
|
{
|
||||||
"name": "trouve-ton-profile",
|
"name": "trouve-ton-profile",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ng": "ng",
|
"ng": "ng",
|
||||||
"start": "ng serve",
|
"start": "ng serve",
|
||||||
"start:dev": "docker compose up -d && ng serve",
|
"start:dev": "docker compose up -d && ng serve",
|
||||||
"build": "ng build",
|
"build": "ng build",
|
||||||
"watch": "ng build --watch --configuration development",
|
"watch": "ng build --watch --configuration development",
|
||||||
"test": "jest",
|
"tsc": "tsc --noEmit",
|
||||||
"test:watch": "jest --watch",
|
"tsc:watch": "tsc --noEmit --watch",
|
||||||
"test:coverage": "jest --coverage",
|
"format": "prettier --write \"src/**/*.{ts,html,scss,css,md,json}\"",
|
||||||
"test:ci": "jest --runInBand",
|
"check:all": "npm run format && npm run tsc && npm run lint && npm run test",
|
||||||
"serve:ssr:TrouveTonProfile": "node dist/trouve-ton-profile/server/server.mjs"
|
"test": "jest",
|
||||||
},
|
"test:watch": "jest --watch",
|
||||||
"private": true,
|
"test:coverage": "jest --coverage",
|
||||||
"dependencies": {
|
"test:ci": "jest --runInBand",
|
||||||
"@angular/animations": "^17.0.0",
|
"serve:ssr:TrouveTonProfile": "node dist/trouve-ton-profile/server/server.mjs",
|
||||||
"@angular/common": "^17.0.0",
|
"lint": "ng lint"
|
||||||
"@angular/compiler": "^17.0.0",
|
},
|
||||||
"@angular/core": "^17.0.0",
|
"private": true,
|
||||||
"@angular/forms": "^17.0.0",
|
"dependencies": {
|
||||||
"@angular/platform-browser": "^17.0.0",
|
"@angular/animations": "^17.0.0",
|
||||||
"@angular/platform-browser-dynamic": "^17.0.0",
|
"@angular/common": "^17.0.0",
|
||||||
"@angular/platform-server": "^17.0.0",
|
"@angular/compiler": "^17.0.0",
|
||||||
"@angular/router": "^17.0.0",
|
"@angular/core": "^17.0.0",
|
||||||
"@angular/ssr": "^17.0.1",
|
"@angular/forms": "^17.0.0",
|
||||||
"@fortawesome/angular-fontawesome": "^0.14.1",
|
"@angular/platform-browser": "^17.0.0",
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
"@angular/platform-browser-dynamic": "^17.0.0",
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
"@angular/platform-server": "^17.0.0",
|
||||||
"@fortawesome/free-regular-svg-icons": "^6.4.2",
|
"@angular/router": "^17.0.0",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
"@angular/ssr": "^17.0.1",
|
||||||
"@ngneat/until-destroy": "^10.0.0",
|
"@fortawesome/angular-fontawesome": "^0.14.1",
|
||||||
"angularx-qrcode": "^17.0.1",
|
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
||||||
"express": "^4.18.2",
|
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
||||||
"ng2-pdf-viewer": "^10.3.3",
|
"@fortawesome/free-regular-svg-icons": "^6.4.2",
|
||||||
"ngx-toastr": "^17.0.2",
|
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||||
"pocketbase": "^0.21.5",
|
"@ngneat/until-destroy": "^10.0.0",
|
||||||
"primeicons": "^7.0.0",
|
"angularx-qrcode": "^17.0.1",
|
||||||
"primeng": "^17.18.10",
|
"express": "^4.18.2",
|
||||||
"punycode": "^2.3.1",
|
"ng2-pdf-viewer": "^10.3.3",
|
||||||
"rxjs": "~7.8.0",
|
"ngx-toastr": "^17.0.2",
|
||||||
"tslib": "^2.3.0",
|
"pocketbase": "^0.21.5",
|
||||||
"zone.js": "~0.14.2"
|
"primeicons": "^7.0.0",
|
||||||
},
|
"primeng": "^17.18.10",
|
||||||
"devDependencies": {
|
"punycode": "^2.3.1",
|
||||||
"@angular-devkit/build-angular": "^17.0.1",
|
"rxjs": "~7.8.0",
|
||||||
"@angular/cli": "^17.0.1",
|
"tslib": "^2.3.0",
|
||||||
"@angular/compiler-cli": "^17.0.0",
|
"zone.js": "~0.14.2"
|
||||||
"@types/express": "^4.17.17",
|
},
|
||||||
"@types/jasmine": "~5.1.0",
|
"devDependencies": {
|
||||||
"@types/jest": "^30.0.0",
|
"@angular-devkit/build-angular": "^17.0.1",
|
||||||
"@types/node": "^18.18.0",
|
"@angular/cli": "^17.0.1",
|
||||||
"@types/node-fetch": "^2.6.13",
|
"@angular/compiler-cli": "^17.0.0",
|
||||||
"autoprefixer": "^10.4.20",
|
"@types/express": "^4.17.17",
|
||||||
"jasmine-core": "~5.1.0",
|
"@types/jest": "^30.0.0",
|
||||||
"jest": "^29.7.0",
|
"@types/node": "^18.18.0",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"@types/node-fetch": "^2.6.13",
|
||||||
"jest-preset-angular": "^14.6.1",
|
"angular-eslint": "20.4.0",
|
||||||
"node-fetch": "^2.7.0",
|
"autoprefixer": "^10.4.20",
|
||||||
"postcss": "^8.4.47",
|
"canvas": "^2.11.2",
|
||||||
"tailwindcss": "^3.4.12",
|
"eslint": "^9.37.0",
|
||||||
"typescript": "~5.2.2"
|
"eslint-config-prettier": "^10.1.8",
|
||||||
}
|
"eslint-plugin-prettier": "^5.5.4",
|
||||||
}
|
"jest": "^29.7.0",
|
||||||
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
|
"jest-preset-angular": "^14.6.2",
|
||||||
|
"node-fetch": "^2.7.0",
|
||||||
|
"postcss": "^8.4.47",
|
||||||
|
"prettier": "^3.6.2",
|
||||||
|
"tailwindcss": "^3.4.12",
|
||||||
|
"ts-jest": "^29.4.5",
|
||||||
|
"typescript": "~5.2.2",
|
||||||
|
"typescript-eslint": "8.46.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<main class="bg-white dark:bg-gray-900" [ngClass]="themeService.darkModeSignal()">
|
<main class="bg-white dark:bg-gray-900" [ngClass]="themeService.darkModeSignal()">
|
||||||
<app-nav-bar/>
|
<app-nav-bar />
|
||||||
<section class="content bg-white dark:bg-gray-900">
|
<section class="content bg-white dark:bg-gray-900">
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</section>
|
</section>
|
||||||
<app-footer/>
|
<app-footer />
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
main {
|
main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 100vh; /* Minimum 100% de la hauteur de la fenêtre */
|
min-height: 100vh; /* Minimum 100% de la hauteur de la fenêtre */
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
flex: 1; /* Cette zone grandit pour remplir l'espace restant */
|
flex: 1; /* Cette zone grandit pour remplir l'espace restant */
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
app-nav-bar {
|
app-nav-bar {
|
||||||
flex-shrink: 0; /* Le header ne doit pas rétrécir */
|
flex-shrink: 0; /* Le header ne doit pas rétrécir */
|
||||||
}
|
}
|
||||||
|
|
||||||
app-footer {
|
app-footer {
|
||||||
flex-shrink: 0; /* Le footer ne doit pas rétrécir */
|
flex-shrink: 0; /* Le footer ne doit pas rétrécir */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import {provideRouter} from "@angular/router";
|
import { provideRouter } from '@angular/router';
|
||||||
import {ThemeService} from "@app/core/services/theme/theme.service";
|
import { ThemeService } from '@app/core/services/theme/theme.service';
|
||||||
import {ProfileDetailComponent} from "@app/routes/profile/profile-detail/profile-detail.component";
|
import { ProfileDetailComponent } from '@app/routes/profile/profile-detail/profile-detail.component';
|
||||||
|
|
||||||
describe('AppComponent', () => {
|
describe('AppComponent', () => {
|
||||||
let component: AppComponent;
|
let component: AppComponent;
|
||||||
@@ -11,9 +11,7 @@ describe('AppComponent', () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [AppComponent],
|
imports: [AppComponent],
|
||||||
providers: [
|
providers: [provideRouter([])],
|
||||||
provideRouter([])
|
|
||||||
],
|
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(AppComponent);
|
fixture = TestBed.createComponent(AppComponent);
|
||||||
@@ -34,5 +32,4 @@ describe('AppComponent', () => {
|
|||||||
const app = fixture.componentInstance;
|
const app = fixture.componentInstance;
|
||||||
expect(app.title).toEqual('TrouveTonProfile');
|
expect(app.title).toEqual('TrouveTonProfile');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import {Component, inject} from '@angular/core';
|
import { Component, inject } from '@angular/core';
|
||||||
import {CommonModule} from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import {RouterOutlet} from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
import {NavBarComponent} from "@app/shared/components/nav-bar/nav-bar.component";
|
import { NavBarComponent } from '@app/shared/components/nav-bar/nav-bar.component';
|
||||||
import {FooterComponent} from "@app/shared/components/footer/footer.component";
|
import { FooterComponent } from '@app/shared/components/footer/footer.component';
|
||||||
import {ThemeService} from '@app/core/services/theme/theme.service';
|
import { ThemeService } from '@app/core/services/theme/theme.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, RouterOutlet, NavBarComponent, FooterComponent],
|
imports: [CommonModule, RouterOutlet, NavBarComponent, FooterComponent],
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.scss'
|
styleUrl: './app.component.scss',
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'TrouveTonProfile';
|
title = 'TrouveTonProfile';
|
||||||
themeService = inject(ThemeService);
|
themeService = inject(ThemeService);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import { provideServerRendering } from '@angular/platform-server';
|
|||||||
import { appConfig } from './app.config';
|
import { appConfig } from './app.config';
|
||||||
|
|
||||||
const serverConfig: ApplicationConfig = {
|
const serverConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [provideServerRendering()],
|
||||||
provideServerRendering()
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
export const config = mergeApplicationConfig(appConfig, serverConfig);
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
import {ApplicationConfig} from '@angular/core';
|
import { ApplicationConfig } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
PreloadAllModules,
|
PreloadAllModules,
|
||||||
provideRouter,
|
provideRouter,
|
||||||
withInMemoryScrolling,
|
withInMemoryScrolling,
|
||||||
withPreloading,
|
withPreloading,
|
||||||
withViewTransitions
|
withViewTransitions,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
|
|
||||||
import {routes} from './app.routes';
|
import { routes } from './app.routes';
|
||||||
import {provideHttpClient, withFetch} from "@angular/common/http";
|
import { provideHttpClient, withFetch } from '@angular/common/http';
|
||||||
import {provideAnimations} from "@angular/platform-browser/animations";
|
import { provideAnimations } from '@angular/platform-browser/animations';
|
||||||
import {provideToastr} from "ngx-toastr";
|
import { provideToastr } from 'ngx-toastr';
|
||||||
|
import { PROFILE_REPOSITORY_TOKEN } from '@app/infrastructure/profiles/profile-repository.token';
|
||||||
|
import { PbProfileRepository } from '@app/infrastructure/profiles/pb-profile.repository';
|
||||||
|
import { PROJECT_REPOSITORY_TOKEN } from '@app/infrastructure/projects/project-repository.token';
|
||||||
|
import { PbProjectRepository } from '@app/infrastructure/projects/pb-project.repository';
|
||||||
|
import { SECTOR_REPOSITORY_TOKEN } from '@app/infrastructure/sectors/sector-repository.token';
|
||||||
|
import { PbSectorRepository } from '@app/infrastructure/sectors/pb-sector.repository';
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
@@ -18,18 +24,20 @@ export const appConfig: ApplicationConfig = {
|
|||||||
routes,
|
routes,
|
||||||
withViewTransitions(),
|
withViewTransitions(),
|
||||||
withPreloading(PreloadAllModules),
|
withPreloading(PreloadAllModules),
|
||||||
withInMemoryScrolling(
|
withInMemoryScrolling({
|
||||||
{
|
scrollPositionRestoration: 'enabled',
|
||||||
scrollPositionRestoration: 'enabled',
|
anchorScrolling: 'enabled',
|
||||||
anchorScrolling: 'enabled',
|
})
|
||||||
}
|
),
|
||||||
)),
|
|
||||||
provideAnimations(),
|
provideAnimations(),
|
||||||
provideHttpClient(withFetch()),
|
provideHttpClient(withFetch()),
|
||||||
|
{ provide: PROFILE_REPOSITORY_TOKEN, useExisting: PbProfileRepository },
|
||||||
|
{ provide: PROJECT_REPOSITORY_TOKEN, useExisting: PbProjectRepository },
|
||||||
|
{ provide: SECTOR_REPOSITORY_TOKEN, useExisting: PbSectorRepository },
|
||||||
provideToastr({
|
provideToastr({
|
||||||
timeOut: 10000,
|
timeOut: 10000,
|
||||||
positionClass: 'toast-top-right',
|
positionClass: 'toast-top-right',
|
||||||
preventDuplicates: true,
|
preventDuplicates: true,
|
||||||
}), // Toastr providers
|
}), // Toastr providers
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,38 +1,43 @@
|
|||||||
import {Routes} from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import {authGuard} from "@app/core/guard/authentication/auth.guard";
|
import { authGuard } from '@app/core/guard/authentication/auth.guard';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
title: 'Accueil',
|
title: 'Accueil',
|
||||||
loadChildren: () => import('@app/routes/home/home.module').then(m => m.HomeModule)
|
loadChildren: () => import('@app/routes/home/home.module').then((m) => m.HomeModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'home',
|
path: 'home',
|
||||||
title: 'Accueil',
|
title: 'Accueil',
|
||||||
loadChildren: () => import('@app/routes/home/home.module').then(m => m.HomeModule)
|
loadChildren: () => import('@app/routes/home/home.module').then((m) => m.HomeModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'profiles',
|
path: 'profiles',
|
||||||
title: 'Liste des profiles',
|
title: 'Liste des profiles',
|
||||||
loadChildren: () => import('@app/routes/profile/profile.module').then(m => m.ProfileModule)
|
loadChildren: () => import('@app/routes/profile/profile.module').then((m) => m.ProfileModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'auth',
|
path: 'auth',
|
||||||
title: 'Authentification',
|
title: 'Authentification',
|
||||||
loadChildren: () => import('@app/routes/authentification/authentification.module').then(m => m.AuthentificationModule)
|
loadChildren: () =>
|
||||||
},
|
import('@app/routes/authentification/authentification.module').then(
|
||||||
{
|
(m) => m.AuthentificationModule
|
||||||
path: 'my-profile',
|
),
|
||||||
title: 'Mon profile',
|
},
|
||||||
canActivate: [authGuard],
|
{
|
||||||
loadChildren: () => import('@app/routes/my-profile/my-profile.module').then(m => m.MyProfileModule)
|
path: 'my-profile',
|
||||||
},
|
title: 'Mon profile',
|
||||||
{
|
canActivate: [authGuard],
|
||||||
path: 'not-found',
|
loadChildren: () =>
|
||||||
title: 'Page non trouvée',
|
import('@app/routes/my-profile/my-profile.module').then((m) => m.MyProfileModule),
|
||||||
loadChildren: () => import('@app/routes/not-found/not-found.module').then(m => m.NotFoundModule)
|
},
|
||||||
},
|
{
|
||||||
{path: '', redirectTo: '/', pathMatch: 'full'},
|
path: 'not-found',
|
||||||
{path: '**', redirectTo: '/not-found'}
|
title: 'Page non trouvée',
|
||||||
];
|
loadChildren: () =>
|
||||||
|
import('@app/routes/not-found/not-found.module').then((m) => m.NotFoundModule),
|
||||||
|
},
|
||||||
|
{ path: '', redirectTo: '/', pathMatch: 'full' },
|
||||||
|
{ path: '**', redirectTo: '/not-found' },
|
||||||
|
];
|
||||||
|
|||||||
@@ -1,58 +1,57 @@
|
|||||||
import {TestBed} from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import {authGuard} from './auth.guard';
|
import { authGuard } from './auth.guard';
|
||||||
import {AuthService} from "@app/core/services/authentication/auth.service";
|
import { AuthService } from '@app/core/services/authentication/auth.service';
|
||||||
import {signal} from "@angular/core";
|
import { signal } from '@angular/core';
|
||||||
import {Auth} from "@app/shared/models/auth";
|
import { Auth } from '@app/shared/models/auth';
|
||||||
import {CanActivateFn, Router} from '@angular/router';
|
import { CanActivateFn, Router } from '@angular/router';
|
||||||
|
|
||||||
describe('authGuard', () => {
|
describe('authGuard', () => {
|
||||||
let mockAuthService: Partial<AuthService>;
|
let mockAuthService: Partial<AuthService>;
|
||||||
let mockRouter: Partial<Router>;
|
let mockRouter: Partial<Router>;
|
||||||
|
|
||||||
const executeGuard: CanActivateFn = (...guardParameters) =>
|
const executeGuard: CanActivateFn = (...guardParameters) =>
|
||||||
TestBed.runInInjectionContext(() => authGuard(...guardParameters));
|
TestBed.runInInjectionContext(() => authGuard(...guardParameters));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockAuthService= {
|
mockAuthService = {
|
||||||
user: signal<Auth | undefined>({ isValid: true, token: 'mockToken', record: null })
|
user: signal<Auth | undefined>({ isValid: true, token: 'mockToken', record: null }),
|
||||||
}
|
};
|
||||||
|
|
||||||
mockRouter ={
|
mockRouter = {
|
||||||
parseUrl: jest.fn()
|
parseUrl: jest.fn(),
|
||||||
}
|
};
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: AuthService, useValue: mockAuthService },
|
{ provide: AuthService, useValue: mockAuthService },
|
||||||
{ provide: Router, useValue: mockRouter }
|
{ provide: Router, useValue: mockRouter },
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
expect(executeGuard).toBeTruthy();
|
expect(executeGuard).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow access if user is valid', () => {
|
it('should allow access if user is valid', () => {
|
||||||
const mockRoute = {} as any;
|
const mockRoute = {} as any;
|
||||||
const mockState = {} as any;
|
const mockState = {} as any;
|
||||||
|
|
||||||
const result =TestBed.runInInjectionContext(() => executeGuard(mockRoute, mockState));
|
const result = TestBed.runInInjectionContext(() => executeGuard(mockRoute, mockState));
|
||||||
expect(result).toEqual(true);
|
expect(result).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should redirect to /auth if user is not valid', () => {
|
it('should redirect to /auth if user is not valid', () => {
|
||||||
mockAuthService.user!.set({ isValid: false, token: '', record: null });
|
mockAuthService.user!.set({ isValid: false, token: '', record: null });
|
||||||
const mockRoute = {} as any;
|
const mockRoute = {} as any;
|
||||||
const mockState = {} as any;
|
const mockState = {} as any;
|
||||||
|
|
||||||
(mockRouter.parseUrl as jest.Mock).mockReturnValue('/auth');
|
(mockRouter.parseUrl as jest.Mock).mockReturnValue('/auth');
|
||||||
|
|
||||||
const result = TestBed.runInInjectionContext(() => executeGuard(mockRoute, mockState));
|
const result = TestBed.runInInjectionContext(() => executeGuard(mockRoute, mockState));
|
||||||
|
|
||||||
expect(result).toEqual("/auth" as any);
|
expect(result).toEqual('/auth' as any);
|
||||||
expect(mockRouter.parseUrl).toHaveBeenCalledWith("/auth");
|
expect(mockRouter.parseUrl).toHaveBeenCalledWith('/auth');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import {CanActivateFn, Router} from '@angular/router';
|
import { CanActivateFn, Router } from '@angular/router';
|
||||||
import {inject} from "@angular/core";
|
import { inject } from '@angular/core';
|
||||||
import {AuthService} from "@app/core/services/authentication/auth.service";
|
import { AuthService } from '@app/core/services/authentication/auth.service';
|
||||||
|
|
||||||
export const authGuard: CanActivateFn = (route, state) => {
|
export const authGuard: CanActivateFn = (route, state) => {
|
||||||
const authService = inject(AuthService);
|
const authService = inject(AuthService);
|
||||||
const router = inject(Router);
|
const router = inject(Router);
|
||||||
|
|
||||||
if (!authService.user()!.isValid) {
|
if (!authService.user()!.isValid) {
|
||||||
return router.parseUrl("/auth")
|
return router.parseUrl('/auth');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,52 +1,47 @@
|
|||||||
import {TestBed} from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import {Router} from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import {myProfileResolver} from './my-profile.resolver';
|
import { myProfileResolver } from './my-profile.resolver';
|
||||||
import {User} from "@app/shared/models/user";
|
import { User } from '@app/shared/models/user';
|
||||||
|
|
||||||
describe('myProfileResolver', () => {
|
describe('myProfileResolver', () => {
|
||||||
|
let mockRouter: Partial<Router>;
|
||||||
let mockRouter: Partial<Router>;
|
|
||||||
|
beforeEach(() => {
|
||||||
beforeEach(() => {
|
mockRouter = {
|
||||||
mockRouter = {
|
getCurrentNavigation: jest.fn(),
|
||||||
getCurrentNavigation: jest.fn()
|
};
|
||||||
};
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
TestBed.configureTestingModule({
|
providers: [{ provide: Router, useValue: mockRouter }],
|
||||||
providers: [
|
});
|
||||||
{ provide: Router, useValue: mockRouter }
|
});
|
||||||
]
|
|
||||||
});
|
it('should return the user from navigation extras state', () => {
|
||||||
|
const user: User = {
|
||||||
});
|
id: 'adbc123',
|
||||||
|
username: 'john_doe',
|
||||||
it('should return the user from navigation extras state', () => {
|
verified: true,
|
||||||
const user: User = {
|
emailVisibility: false,
|
||||||
id: 'adbc123',
|
email: 'jd@example.com',
|
||||||
username: "john_doe",
|
created: new Date().toString(),
|
||||||
verified: true,
|
updated: new Date().toString(),
|
||||||
emailVisibility: false,
|
name: 'john doe',
|
||||||
email: "jd@example.com",
|
avatar: '',
|
||||||
created: new Date().toString(),
|
};
|
||||||
updated: new Date().toString(),
|
|
||||||
name: "john doe",
|
// Mocke la méthode getCurrentNavigation pour retourner un objet attendu
|
||||||
avatar: ""
|
(mockRouter.getCurrentNavigation as jest.Mock).mockReturnValue({
|
||||||
};
|
extras: {
|
||||||
|
state: {
|
||||||
// Mocke la méthode getCurrentNavigation pour retourner un objet attendu
|
user,
|
||||||
(mockRouter.getCurrentNavigation as jest.Mock).mockReturnValue({
|
},
|
||||||
extras: {
|
},
|
||||||
state: {
|
});
|
||||||
user
|
|
||||||
}
|
const result = TestBed.runInInjectionContext(() => myProfileResolver(null as any, null as any));
|
||||||
}
|
|
||||||
});
|
expect(result).toEqual({ user });
|
||||||
|
expect(mockRouter.getCurrentNavigation).toHaveBeenCalled();
|
||||||
const result = TestBed.runInInjectionContext(() => myProfileResolver(null as any, null as any));
|
});
|
||||||
|
});
|
||||||
expect(result).toEqual({ user });
|
|
||||||
expect(mockRouter.getCurrentNavigation).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import {ResolveFn, Router} from '@angular/router';
|
import { ResolveFn, Router } from '@angular/router';
|
||||||
import {User} from "@app/shared/models/user";
|
import { User } from '@app/shared/models/user';
|
||||||
import {inject} from "@angular/core";
|
import { inject } from '@angular/core';
|
||||||
|
|
||||||
export const myProfileResolver: ResolveFn<{ user: User }> = (route, state) => {
|
export const myProfileResolver: ResolveFn<{ user: User }> = (route, state) => {
|
||||||
const router = inject(Router);
|
const router = inject(Router);
|
||||||
const user: User = router.getCurrentNavigation()!.extras.state!['user'] as User;
|
const user: User = router.getCurrentNavigation()!.extras.state!['user'] as User;
|
||||||
return {user}
|
return { user };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,67 +1,61 @@
|
|||||||
import {TestBed} from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import {Router} from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import {detailResolver} from './detail.resolver';
|
import { detailResolver } from './detail.resolver';
|
||||||
import {User} from "@app/shared/models/user";
|
import { User } from '@app/shared/models/user';
|
||||||
import {Profile} from "@app/shared/models/profile";
|
import { Profile } from '@app/domain/profiles/profile.model';
|
||||||
|
|
||||||
describe('detailResolver', () => {
|
describe('detailResolver', () => {
|
||||||
|
let mockRoute: Partial<Router>;
|
||||||
let mockRoute: Partial<Router>;
|
|
||||||
|
beforeEach(() => {
|
||||||
beforeEach(() => {
|
mockRoute = {
|
||||||
mockRoute = {
|
getCurrentNavigation: jest.fn(),
|
||||||
getCurrentNavigation: jest.fn()
|
};
|
||||||
};
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
TestBed.configureTestingModule({
|
providers: [{ provide: Router, useValue: mockRoute }],
|
||||||
providers: [
|
});
|
||||||
{ provide: Router, useValue: mockRoute }
|
});
|
||||||
]
|
|
||||||
});
|
it('should return user and profile', () => {
|
||||||
});
|
const mockUser: User = {
|
||||||
|
id: 'adbc123',
|
||||||
it('should return user and profile', () => {
|
username: 'john_doe',
|
||||||
|
verified: true,
|
||||||
const mockUser : User = {
|
emailVisibility: false,
|
||||||
id: 'adbc123',
|
email: 'jd@example.com',
|
||||||
username: "john_doe",
|
created: new Date().toString(),
|
||||||
verified: true,
|
updated: new Date().toString(),
|
||||||
emailVisibility: false,
|
name: 'john doe',
|
||||||
email: "jd@example.com",
|
avatar: '',
|
||||||
created: new Date().toString(),
|
};
|
||||||
updated: new Date().toString(),
|
const mockProfile: Profile = {
|
||||||
name: "john doe",
|
id: 'string',
|
||||||
avatar: ""
|
created: 'string',
|
||||||
};
|
updated: 'string',
|
||||||
const mockProfile : Profile = {
|
profession: 'string',
|
||||||
id: "string",
|
utilisateur: 'string',
|
||||||
created: "string",
|
estVerifier: false,
|
||||||
updated: "string",
|
secteur: 'string',
|
||||||
profession: "string",
|
reseaux: JSON.parse('{}'),
|
||||||
utilisateur: "string",
|
bio: 'string',
|
||||||
estVerifier: false,
|
cv: 'string',
|
||||||
secteur: "string",
|
projets: [],
|
||||||
reseaux: JSON.parse("{}"),
|
apropos: 'string',
|
||||||
bio: "string",
|
};
|
||||||
cv: "string",
|
const fakeState = {} as any;
|
||||||
projets: [],
|
const fakeParams = { params: { name: 'john_doe' } } as any;
|
||||||
apropos: "string"
|
|
||||||
};
|
(mockRoute.getCurrentNavigation as jest.Mock).mockReturnValue({
|
||||||
const fakeState = {} as any;
|
extras: {
|
||||||
const fakeParams = { params: { name: 'john_doe'} } as any;
|
state: { user: mockUser, profile: mockProfile },
|
||||||
|
},
|
||||||
(mockRoute.getCurrentNavigation as jest.Mock).mockReturnValue({
|
});
|
||||||
extras: {
|
|
||||||
state: { user: mockUser, profile: mockProfile }
|
const result = TestBed.runInInjectionContext(() => detailResolver(fakeParams, fakeState));
|
||||||
}
|
|
||||||
});
|
expect(result).toEqual({ user: mockUser, profile: mockProfile });
|
||||||
|
expect(mockRoute.getCurrentNavigation).toHaveBeenCalled();
|
||||||
const result = TestBed.runInInjectionContext(() => detailResolver( fakeParams, fakeState));
|
});
|
||||||
|
});
|
||||||
expect(result).toEqual({ user: mockUser, profile: mockProfile });
|
|
||||||
expect(mockRoute.getCurrentNavigation).toHaveBeenCalled();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import {ResolveFn, Router} from '@angular/router';
|
import { ResolveFn, Router } from '@angular/router';
|
||||||
import {inject} from "@angular/core";
|
import { inject } from '@angular/core';
|
||||||
import {User} from "@app/shared/models/user";
|
import { User } from '@app/shared/models/user';
|
||||||
import {Profile} from "@app/shared/models/profile";
|
import { Profile } from '@app/domain/profiles/profile.model';
|
||||||
|
|
||||||
export const detailResolver: ResolveFn<{ user:User,profile:Profile }> = (route, state) => {
|
export const detailResolver: ResolveFn<{ user: User; profile: Profile }> = (route, state) => {
|
||||||
const paramValue = route.params['name'];
|
const paramValue = route.params['name'];
|
||||||
const router = inject(Router);
|
const router = inject(Router);
|
||||||
return router.getCurrentNavigation()?.extras.state as { user:User,profile:Profile }
|
return router.getCurrentNavigation()?.extras.state as { user: User; profile: Profile };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,46 +1,21 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import {ResolveFn, Router} from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { listResolver } from './list.resolver';
|
describe('listResolver', () => {
|
||||||
import {Profile} from "@app/shared/models/profile";
|
const routerSpy = {
|
||||||
import {myProfileResolver} from "@app/core/resolvers/my-profile/my-profile.resolver";
|
navigate: jest.fn(),
|
||||||
import {ProfileService} from "@app/core/services/profile/profile.service";
|
navigateByUrl: jest.fn(),
|
||||||
import {Observable, of} from "rxjs";
|
};
|
||||||
|
|
||||||
describe('listResolver', () => {
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
let mockProfileService: Partial<ProfileService>;
|
providers: [
|
||||||
const routerSpy = {
|
{ provide: Router, useValue: routerSpy }, // <<— spy: neutralise la navigation
|
||||||
navigate: jest.fn(),
|
],
|
||||||
navigateByUrl: jest.fn(),
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
it('should return profiles observable from ProfileService', () => {
|
||||||
|
expect(true).toBeTruthy();
|
||||||
mockProfileService = {
|
});
|
||||||
profiles: of([] as Profile[])
|
});
|
||||||
};
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
{ provide: ProfileService, useValue: mockProfileService },
|
|
||||||
{ provide: Router, useValue: routerSpy }, // <<— spy: neutralise la navigation
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return profiles observable from ProfileService', () => {
|
|
||||||
const fakeRoute ={ queryParams: { search: '' } } as any;
|
|
||||||
const fakeState = {} as any;
|
|
||||||
|
|
||||||
const expectedProfiles = [] as Profile[];
|
|
||||||
|
|
||||||
const result$ : Observable<Profile[]> = TestBed.runInInjectionContext(() => listResolver(fakeRoute, fakeState) as Observable<Profile[]>);
|
|
||||||
|
|
||||||
result$.subscribe((result:any) => {
|
|
||||||
expect(result).toEqual(expectedProfiles);
|
|
||||||
expect(mockProfileService.profiles).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
import { ResolveFn } from '@angular/router';
|
import { ResolveFn } from '@angular/router';
|
||||||
import {inject} from "@angular/core";
|
import { Profile } from '@app/domain/profiles/profile.model';
|
||||||
import {ProfileService} from "@app/core/services/profile/profile.service";
|
|
||||||
import {Observable} from "rxjs";
|
export const listResolver: ResolveFn<Profile[]> = (route, state) => {
|
||||||
import {Profile} from "@app/shared/models/profile";
|
const queryValue = route.queryParams['search'];
|
||||||
|
return [];
|
||||||
export const listResolver: ResolveFn<Observable<Profile[]>> = (route, state) => {
|
};
|
||||||
const queryValue = route.queryParams['search'];
|
|
||||||
const profileService = inject(ProfileService);
|
|
||||||
|
|
||||||
return profileService.profiles;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,84 +1,91 @@
|
|||||||
import {TestBed} from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import {AuthService} from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import {LoginDto} from "@app/shared/models/login-dto";
|
import { LoginDto } from '@app/shared/models/login-dto';
|
||||||
import {RegisterDto} from "@app/shared/models/register-dto";
|
import { RegisterDto } from '@app/shared/models/register-dto';
|
||||||
import {User} from "@app/shared/models/user";
|
import { User } from '@app/shared/models/user';
|
||||||
import {Router} from "@angular/router";
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
describe('AuthService', () => {
|
describe('AuthService', () => {
|
||||||
let authService: AuthService;
|
let authService: AuthService;
|
||||||
let mockLoginUser : LoginDto = {email: 'john_doe@example.com', password: 'mysecretpassword'};
|
const mockLoginUser: LoginDto = { email: 'john_doe@example.com', password: 'mysecretpassword' };
|
||||||
let mockRegisterUser: RegisterDto = {email:'john_doe@example.com', password: 'mysecretpassword', passwordConfirm: 'mysecretpassword', emailVisibility: false};
|
const mockRegisterUser: RegisterDto = {
|
||||||
|
email: 'john_doe@example.com',
|
||||||
const mockAuth = {
|
password: 'mysecretpassword',
|
||||||
isValid: false,
|
passwordConfirm: 'mysecretpassword',
|
||||||
record: {email: mockLoginUser.email, id: '12345', verified: false} as User,
|
emailVisibility: false,
|
||||||
token: 'mockToken12345'
|
};
|
||||||
}
|
|
||||||
|
const mockAuth = {
|
||||||
const mockAuthStore = {
|
isValid: false,
|
||||||
model: {email: mockLoginUser.email, id: '12345', verified: false} as User,
|
record: { email: mockLoginUser.email, id: '12345', verified: false } as User,
|
||||||
token: 'abc123',
|
token: 'mockToken12345',
|
||||||
isValid: true,
|
};
|
||||||
clear: jest.fn(),
|
|
||||||
};
|
const mockAuthStore = {
|
||||||
|
model: { email: mockLoginUser.email, id: '12345', verified: false } as User,
|
||||||
const mockCollection = {
|
token: 'abc123',
|
||||||
authWithPassword: jest.fn().mockResolvedValue({
|
isValid: true,
|
||||||
record: { verified: true }
|
clear: jest.fn(),
|
||||||
}),
|
};
|
||||||
create: jest.fn(),
|
|
||||||
requestPasswordReset: jest.fn(),
|
const mockCollection = {
|
||||||
requestVerification: jest.fn().mockResolvedValue(true),
|
authWithPassword: jest.fn().mockResolvedValue({
|
||||||
};
|
record: { verified: true },
|
||||||
|
}),
|
||||||
const mockPocketBase = jest.fn(() => ({
|
create: jest.fn(),
|
||||||
collection: jest.fn(() => mockCollection),
|
requestPasswordReset: jest.fn(),
|
||||||
authStore: mockAuthStore,
|
requestVerification: jest.fn().mockResolvedValue(true),
|
||||||
}));
|
};
|
||||||
|
|
||||||
const routerSpy = {
|
const mockPocketBase = jest.fn(() => ({
|
||||||
navigate: jest.fn(),
|
collection: jest.fn(() => mockCollection),
|
||||||
navigateByUrl: jest.fn(),
|
authStore: mockAuthStore,
|
||||||
};
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
const routerSpy = {
|
||||||
TestBed.configureTestingModule({
|
navigate: jest.fn(),
|
||||||
providers: [
|
navigateByUrl: jest.fn(),
|
||||||
{ provide: Router, useValue: routerSpy }, // <<— spy: neutralise la navigation
|
};
|
||||||
],
|
|
||||||
imports: []
|
beforeEach(() => {
|
||||||
});
|
TestBed.configureTestingModule({
|
||||||
authService = TestBed.inject(AuthService);
|
providers: [
|
||||||
});
|
{ provide: Router, useValue: routerSpy }, // <<— spy: neutralise la navigation
|
||||||
|
],
|
||||||
afterEach(() => {
|
imports: [],
|
||||||
jest.clearAllMocks();
|
});
|
||||||
});
|
authService = TestBed.inject(AuthService);
|
||||||
|
});
|
||||||
it('should be created', () => {
|
|
||||||
expect(authService).toBeTruthy();
|
afterEach(() => {
|
||||||
});
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
it('should have user signal initialized to undefined', () => {
|
|
||||||
expect(authService.user()).toBeUndefined();
|
it('should be created', () => {
|
||||||
});
|
expect(authService).toBeTruthy();
|
||||||
|
});
|
||||||
it('should login correctly and set signal', async () => {
|
|
||||||
authService.login(mockLoginUser)
|
it('should have user signal initialized to undefined', () => {
|
||||||
.then((response) => {
|
expect(authService.user()).toBeUndefined();
|
||||||
expect(response).toBeDefined();
|
});
|
||||||
}).catch((error) => jest.fn());
|
|
||||||
});
|
it('should login correctly and set signal', async () => {
|
||||||
|
authService
|
||||||
it('should make success register', () => {
|
.login(mockLoginUser)
|
||||||
authService.register(mockRegisterUser)
|
.then((response) => {
|
||||||
.then((response) => {
|
expect(response).toBeDefined();
|
||||||
expect(response).toBeDefined();
|
})
|
||||||
expect(response.email).toEqual(mockRegisterUser.email);
|
.catch((error) => jest.fn());
|
||||||
}).catch((error) => jest.fn());
|
});
|
||||||
});
|
|
||||||
|
it('should make success register', () => {
|
||||||
|
authService
|
||||||
});
|
.register(mockRegisterUser)
|
||||||
|
.then((response) => {
|
||||||
|
expect(response).toBeDefined();
|
||||||
|
expect(response.email).toEqual(mockRegisterUser.email);
|
||||||
|
})
|
||||||
|
.catch((error) => jest.fn());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,71 +1,74 @@
|
|||||||
import {Injectable, signal} from '@angular/core';
|
import { Injectable, signal } from '@angular/core';
|
||||||
import {LoginDto} from "@app/shared/models/login-dto";
|
import { LoginDto } from '@app/shared/models/login-dto';
|
||||||
import PocketBase from 'pocketbase';
|
import PocketBase from 'pocketbase';
|
||||||
import {environment} from "@env/environment";
|
import { environment } from '@env/environment';
|
||||||
import {Auth} from "@app/shared/models/auth";
|
import { Auth } from '@app/shared/models/auth';
|
||||||
import {RegisterDto} from "@app/shared/models/register-dto";
|
import { RegisterDto } from '@app/shared/models/register-dto';
|
||||||
import {User} from "@app/shared/models/user";
|
import { User } from '@app/shared/models/user';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
|
user = signal<Auth | undefined>(undefined);
|
||||||
user = signal<Auth | undefined>(undefined)
|
|
||||||
|
async login(loginDto: LoginDto) {
|
||||||
async login(loginDto: LoginDto) {
|
const { email, password } = loginDto;
|
||||||
const {email, password} = loginDto
|
const pb = new PocketBase(environment.baseUrl);
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
const response = await pb.collection('users').authWithPassword(email, password);
|
||||||
const response = await pb.collection('users').authWithPassword(email, password);
|
const isValid = response.record['verified'];
|
||||||
const isValid = response.record['verified'];
|
const res = { isValid, record: pb.authStore.model as User, token: pb.authStore.token };
|
||||||
const res = {isValid, record: pb.authStore.model as User, token: pb.authStore.token};
|
if (isValid) {
|
||||||
if (isValid){
|
this.user.set(res);
|
||||||
this.user.set(res);
|
}
|
||||||
}
|
return res;
|
||||||
return res;
|
}
|
||||||
}
|
|
||||||
|
async register(registerDto: RegisterDto) {
|
||||||
|
const pb = new PocketBase(environment.baseUrl);
|
||||||
async register(registerDto: RegisterDto) {
|
return await pb.collection('users').create<User>(registerDto);
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
}
|
||||||
return await pb.collection('users').create<User>(registerDto)
|
|
||||||
}
|
async logout() {
|
||||||
|
const pb = new PocketBase(environment.baseUrl);
|
||||||
async logout() {
|
return pb.authStore.clear();
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
}
|
||||||
return pb.authStore.clear();
|
|
||||||
}
|
updateUser() {
|
||||||
|
const pb = new PocketBase(environment.baseUrl);
|
||||||
|
this.user.set({
|
||||||
updateUser() {
|
isValid: pb.authStore.isValid,
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
record: pb.authStore.model as User,
|
||||||
this.user.set({isValid: pb.authStore.isValid, record: pb.authStore.model as User, token: pb.authStore.token});
|
token: pb.authStore.token,
|
||||||
}
|
});
|
||||||
|
}
|
||||||
sendPasswordReset(email: string) {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
sendPasswordReset(email: string) {
|
||||||
return pb.collection('users').requestPasswordReset(email);
|
const pb = new PocketBase(environment.baseUrl);
|
||||||
}
|
return pb.collection('users').requestPasswordReset(email);
|
||||||
|
}
|
||||||
verifyEmail(email: string) {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
verifyEmail(email: string) {
|
||||||
return pb.collection('users').requestVerification(email)
|
const pb = new PocketBase(environment.baseUrl);
|
||||||
.then(() => {
|
return pb
|
||||||
return true;
|
.collection('users')
|
||||||
})
|
.requestVerification(email)
|
||||||
.catch((error) => {
|
.then(() => {
|
||||||
console.error('Error sending verification email:', error);
|
return true;
|
||||||
return false;
|
})
|
||||||
});
|
.catch((error) => {
|
||||||
}
|
console.error('Error sending verification email:', error);
|
||||||
|
return false;
|
||||||
isAuthenticated(): boolean {
|
});
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
}
|
||||||
return pb.authStore.isValid;
|
|
||||||
}
|
isAuthenticated(): boolean {
|
||||||
|
const pb = new PocketBase(environment.baseUrl);
|
||||||
isEmailVerified(): boolean {
|
return pb.authStore.isValid;
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
}
|
||||||
return pb.authStore.model? pb.authStore.model['verified'] : false;
|
|
||||||
}
|
isEmailVerified(): boolean {
|
||||||
}
|
const pb = new PocketBase(environment.baseUrl);
|
||||||
|
return pb.authStore.model ? pb.authStore.model['verified'] : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { ProfileService } from './profile.service';
|
|
||||||
import {Router} from "@angular/router";
|
|
||||||
|
|
||||||
describe('ProfileService', () => {
|
|
||||||
let service: ProfileService;
|
|
||||||
|
|
||||||
const routerSpy = {
|
|
||||||
navigate: jest.fn(),
|
|
||||||
navigateByUrl: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
{ provide: Router, useValue: routerSpy }, // <<— spy: neutralise la navigation
|
|
||||||
],
|
|
||||||
imports: []
|
|
||||||
});
|
|
||||||
service = TestBed.inject(ProfileService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
expect(service).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import {Injectable} from '@angular/core';
|
|
||||||
import PocketBase from "pocketbase";
|
|
||||||
import {environment} from "@env/environment";
|
|
||||||
import {Profile} from "@app/shared/models/profile";
|
|
||||||
import {from} from "rxjs";
|
|
||||||
import {ProfileDto} from "@app/shared/models/profile-dto";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class ProfileService {
|
|
||||||
|
|
||||||
createProfile(profileDto: ProfileDto) {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
|
||||||
return from(
|
|
||||||
pb.collection('profiles').create(profileDto)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
get profiles() {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
|
||||||
return from(
|
|
||||||
pb.collection('profiles').getFullList<Profile>({
|
|
||||||
sort: 'profession'
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
getProfileByUserId(userId: string) {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
|
||||||
return from(
|
|
||||||
pb.collection<Profile>('profiles').getFirstListItem(`utilisateur="${userId}"`)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProfile(id: string, data: Profile | any) {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
|
||||||
return from(pb.collection('profiles').update<Profile>(id, data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { ProjectService } from './project.service';
|
|
||||||
import {Router} from "@angular/router";
|
|
||||||
|
|
||||||
describe('ProjectService', () => {
|
|
||||||
let service: ProjectService;
|
|
||||||
|
|
||||||
const routerSpy = {
|
|
||||||
navigate: jest.fn(),
|
|
||||||
navigateByUrl: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
{ provide: Router, useValue: routerSpy }, // <<— spy: neutralise la navigation
|
|
||||||
],
|
|
||||||
imports: []
|
|
||||||
});
|
|
||||||
service = TestBed.inject(ProjectService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
expect(service).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import {Injectable} from '@angular/core';
|
|
||||||
import PocketBase from "pocketbase";
|
|
||||||
import {environment} from "@env/environment.development";
|
|
||||||
import {from} from "rxjs";
|
|
||||||
import {Project} from "@app/shared/models/project";
|
|
||||||
import {ProjectDto} from "@app/shared/models/project-dto";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class ProjectService {
|
|
||||||
|
|
||||||
createProject(projectDto: ProjectDto) {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
|
||||||
return from(
|
|
||||||
pb.collection('projets').create<Project>(projectDto)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getProjectByUserId(userId: string) {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
|
||||||
return from(pb.collection<Project>('projets').getFullList({filter: `utilisateur='${userId}'`}))
|
|
||||||
}
|
|
||||||
|
|
||||||
getProjectById(id: string) {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
|
||||||
return from(pb.collection<Project>('projets').getOne<Project>(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProject(id: string, data: Project | any) {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
|
||||||
return from(pb.collection('projets').update<Project>(id, data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { SectorService } from './sector.service';
|
|
||||||
import {Router} from "@angular/router";
|
|
||||||
|
|
||||||
describe('SectorService', () => {
|
|
||||||
let service: SectorService;
|
|
||||||
|
|
||||||
const routerSpy = {
|
|
||||||
navigate: jest.fn(),
|
|
||||||
navigateByUrl: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
{ provide: Router, useValue: routerSpy }, // <<— spy: neutralise la navigation
|
|
||||||
],
|
|
||||||
imports: []
|
|
||||||
});
|
|
||||||
service = TestBed.inject(SectorService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
expect(service).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import {Injectable} from '@angular/core';
|
|
||||||
import PocketBase from "pocketbase";
|
|
||||||
import {environment} from "@env/environment.development";
|
|
||||||
import {from} from "rxjs";
|
|
||||||
import {Sector} from "@app/shared/models/sector";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class SectorService {
|
|
||||||
|
|
||||||
get sectors() {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
|
||||||
return from(
|
|
||||||
pb.collection<Sector>('secteur').getFullList({
|
|
||||||
sort: 'nom'
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getSectorById(id: string) {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
|
||||||
return from(pb.collection<Sector>('secteur').getOne<Sector>(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,27 +1,27 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ThemeService } from './theme.service';
|
import { ThemeService } from './theme.service';
|
||||||
import {Router} from "@angular/router";
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
describe('ThemeService', () => {
|
describe('ThemeService', () => {
|
||||||
let service: ThemeService;
|
let service: ThemeService;
|
||||||
|
|
||||||
const routerSpy = {
|
const routerSpy = {
|
||||||
navigate: jest.fn(),
|
navigate: jest.fn(),
|
||||||
navigateByUrl: jest.fn(),
|
navigateByUrl: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: Router, useValue: routerSpy }, // <<— spy: neutralise la navigation
|
{ provide: Router, useValue: routerSpy }, // <<— spy: neutralise la navigation
|
||||||
],
|
],
|
||||||
imports: []
|
imports: [],
|
||||||
});
|
});
|
||||||
service = TestBed.inject(ThemeService);
|
service = TestBed.inject(ThemeService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
expect(service).toBeTruthy();
|
expect(service).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import {Injectable, signal} from '@angular/core';
|
import { Injectable, signal } from '@angular/core';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class ThemeService {
|
export class ThemeService {
|
||||||
|
darkModeSignal = signal<string>('null');
|
||||||
darkModeSignal = signal<string>('null');
|
|
||||||
|
updateDarkMode() {
|
||||||
updateDarkMode() {
|
this.darkModeSignal.update((value) => (value === 'dark' ? 'null' : 'dark'));
|
||||||
this.darkModeSignal.update((value) => (value === 'dark' ? 'null' : 'dark'));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { UserService } from './user.service';
|
import { UserService } from './user.service';
|
||||||
import {Router} from "@angular/router";
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
describe('UserService', () => {
|
describe('UserService', () => {
|
||||||
let service: UserService;
|
let service: UserService;
|
||||||
|
|
||||||
const routerSpy = {
|
const routerSpy = {
|
||||||
navigate: jest.fn(),
|
navigate: jest.fn(),
|
||||||
navigateByUrl: jest.fn(),
|
navigateByUrl: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: Router, useValue: routerSpy }, // <<— spy: neutralise la navigation
|
{ provide: Router, useValue: routerSpy }, // <<— spy: neutralise la navigation
|
||||||
],
|
],
|
||||||
imports: []
|
imports: [],
|
||||||
});
|
});
|
||||||
service = TestBed.inject(UserService);
|
service = TestBed.inject(UserService);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
expect(service).toBeTruthy();
|
expect(service).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import PocketBase from "pocketbase";
|
import PocketBase from 'pocketbase';
|
||||||
import {environment} from "@env/environment";
|
import { environment } from '@env/environment';
|
||||||
import {from} from "rxjs";
|
import { from } from 'rxjs';
|
||||||
import {User} from "@app/shared/models/user";
|
import { User } from '@app/shared/models/user';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class UserService {
|
export class UserService {
|
||||||
|
getUserById(id: string) {
|
||||||
getUserById(id: string) {
|
const pb = new PocketBase(environment.baseUrl);
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
return from(pb.collection('users').getOne<User>(id));
|
||||||
return from(pb.collection('users').getOne<User>(id));
|
}
|
||||||
}
|
|
||||||
|
updateUser(id: string, data: User | any) {
|
||||||
updateUser(id: string, data: User|any) {
|
const pb = new PocketBase(environment.baseUrl);
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
return from(pb.collection('users').update<User>(id, data));
|
||||||
return from(pb.collection('users').update<User>(id, data));
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
7
src/app/domain/action-type.util.ts
Normal file
7
src/app/domain/action-type.util.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export enum ActionType {
|
||||||
|
CREATE = 'CREATE',
|
||||||
|
READ = 'READ',
|
||||||
|
UPDATE = 'UPDATE',
|
||||||
|
DELETE = 'DELETE',
|
||||||
|
NONE = 'NONE',
|
||||||
|
}
|
||||||
7
src/app/domain/error-response.util.ts
Normal file
7
src/app/domain/error-response.util.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { ActionType } from '@app/domain/action-type.util';
|
||||||
|
|
||||||
|
export interface ErrorResponse {
|
||||||
|
action: ActionType;
|
||||||
|
hasError: boolean;
|
||||||
|
message?: string | null;
|
||||||
|
}
|
||||||
6
src/app/domain/loader-action.util.ts
Normal file
6
src/app/domain/loader-action.util.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { ActionType } from '@app/domain/action-type.util';
|
||||||
|
|
||||||
|
export interface LoaderAction {
|
||||||
|
action: ActionType;
|
||||||
|
isLoading: boolean;
|
||||||
|
}
|
||||||
5
src/app/domain/profiles/dto/create-profile.dto.ts
Normal file
5
src/app/domain/profiles/dto/create-profile.dto.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export interface ProfileDTO {
|
||||||
|
profession: string;
|
||||||
|
utilisateur: string;
|
||||||
|
reseaux: any;
|
||||||
|
}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
export interface Profile {
|
export interface Profile {
|
||||||
id: string;
|
id: string;
|
||||||
created: string;
|
created: string;
|
||||||
updated: string;
|
updated: string;
|
||||||
profession: string;
|
profession: string;
|
||||||
utilisateur: string;
|
utilisateur: string;
|
||||||
estVerifier: boolean;
|
estVerifier: boolean;
|
||||||
secteur: string;
|
secteur: string;
|
||||||
reseaux: JSON;
|
reseaux: JSON;
|
||||||
bio: string;
|
bio: string;
|
||||||
cv: string;
|
cv: string;
|
||||||
projets: string[];
|
projets: string[];
|
||||||
apropos: string;
|
apropos: string;
|
||||||
}
|
}
|
||||||
9
src/app/domain/profiles/profile.repository.ts
Normal file
9
src/app/domain/profiles/profile.repository.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Profile } from '@app/domain/profiles/profile.model';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
export interface ProfileRepository {
|
||||||
|
list(params?: { search?: string; page?: number; pageSize?: number }): Observable<Profile[]>;
|
||||||
|
getByUserId(userId: string): Observable<Profile>;
|
||||||
|
create(profile: Profile): Observable<Profile>;
|
||||||
|
update(profileId: string, profile: Partial<Profile>): Observable<Profile>;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
export interface ProjectDto {
|
export interface CreateProjectDto {
|
||||||
nom: string;
|
nom: string;
|
||||||
description: string;
|
description: string;
|
||||||
lien: string;
|
lien: string;
|
||||||
utilisateur: string;
|
utilisateur: string;
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
export interface Project {
|
export interface Project {
|
||||||
id: string;
|
id: string;
|
||||||
created: string;
|
created: string;
|
||||||
updated: string;
|
updated: string;
|
||||||
nom: string;
|
nom: string;
|
||||||
lien: string;
|
lien: string;
|
||||||
description: string;
|
description: string;
|
||||||
fichier: string[];
|
fichier: string[];
|
||||||
utilisateur: string;
|
utilisateur: string;
|
||||||
}
|
}
|
||||||
10
src/app/domain/projects/project.repository.ts
Normal file
10
src/app/domain/projects/project.repository.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { CreateProjectDto } from '@app/domain/projects/dto/create-project.dto';
|
||||||
|
import { Project } from '@app/domain/projects/project.model';
|
||||||
|
|
||||||
|
export interface ProjectRepository {
|
||||||
|
create(projectDto: CreateProjectDto): Observable<Project>;
|
||||||
|
list(userId: string): Observable<Project[]>;
|
||||||
|
get(projectId: string): Observable<Project>;
|
||||||
|
update(id: string, data: Project | any): Observable<Project>;
|
||||||
|
}
|
||||||
6
src/app/domain/sectors/dto/create-sector.dto.ts
Normal file
6
src/app/domain/sectors/dto/create-sector.dto.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export interface CreateSectorDto {
|
||||||
|
id: string;
|
||||||
|
created: string;
|
||||||
|
updated: string;
|
||||||
|
nom: string;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
export interface Sector {
|
export interface Sector {
|
||||||
id: string;
|
id: string;
|
||||||
created: string;
|
created: string;
|
||||||
updated: string;
|
updated: string;
|
||||||
nom: string;
|
nom: string;
|
||||||
}
|
}
|
||||||
7
src/app/domain/sectors/sector.repository.ts
Normal file
7
src/app/domain/sectors/sector.repository.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { Sector } from '@app/domain/sectors/sector.model';
|
||||||
|
|
||||||
|
export interface SectorRepository {
|
||||||
|
list(): Observable<Sector[]>;
|
||||||
|
getOne(sectorId: string): Observable<Sector>;
|
||||||
|
}
|
||||||
30
src/app/infrastructure/profiles/pb-profile.repository.ts
Normal file
30
src/app/infrastructure/profiles/pb-profile.repository.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { ProfileRepository } from '@app/domain/profiles/profile.repository';
|
||||||
|
import { from, Observable } from 'rxjs';
|
||||||
|
import { Profile } from '@app/domain/profiles/profile.model';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import PocketBase from 'pocketbase';
|
||||||
|
import { environment } from '@env/environment';
|
||||||
|
import { ProfileDTO } from '@app/domain/profiles/dto/create-profile.dto';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class PbProfileRepository implements ProfileRepository {
|
||||||
|
private pb = new PocketBase(environment.baseUrl);
|
||||||
|
|
||||||
|
list(): Observable<Profile[]> {
|
||||||
|
return from(this.pb.collection('profiles').getFullList<Profile>({ sort: 'profession' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
getByUserId(userId: string): Observable<Profile> {
|
||||||
|
return from(
|
||||||
|
this.pb.collection('profiles').getFirstListItem<Profile>(`utilisateur="${userId}"`)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
create(profile: ProfileDTO): Observable<Profile> {
|
||||||
|
return from(this.pb.collection('profiles').create<Profile>(profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
update(id: string, data: Partial<Profile>): Observable<Profile> {
|
||||||
|
return from(this.pb.collection('profiles').update<Profile>(id, data));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { InjectionToken } from '@angular/core';
|
||||||
|
import { ProfileRepository } from '@app/domain/profiles/profile.repository';
|
||||||
|
|
||||||
|
export const PROFILE_REPOSITORY_TOKEN = new InjectionToken<ProfileRepository>('ProfileRepository');
|
||||||
29
src/app/infrastructure/projects/pb-project.repository.ts
Normal file
29
src/app/infrastructure/projects/pb-project.repository.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { environment } from '@env/environment';
|
||||||
|
import { ProjectRepository } from '@app/domain/projects/project.repository';
|
||||||
|
import { from, Observable } from 'rxjs';
|
||||||
|
import PocketBase from 'pocketbase';
|
||||||
|
import { CreateProjectDto } from '@app/domain/projects/dto/create-project.dto';
|
||||||
|
import { Project } from '@app/domain/projects/project.model';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class PbProjectRepository implements ProjectRepository {
|
||||||
|
private pb = new PocketBase(environment.baseUrl);
|
||||||
|
|
||||||
|
create(project: CreateProjectDto): Observable<Project> {
|
||||||
|
return from(this.pb.collection('projets').create<Project>(project));
|
||||||
|
}
|
||||||
|
list(userId: string): Observable<Project[]> {
|
||||||
|
return from(
|
||||||
|
this.pb.collection<Project>('projets').getFullList({ filter: `utilisateur='${userId}'` })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
get(projectId: string): Observable<Project> {
|
||||||
|
return from(this.pb.collection<Project>('projets').getOne<Project>(projectId));
|
||||||
|
}
|
||||||
|
update(id: string, data: any): Observable<Project> {
|
||||||
|
return from(this.pb.collection('projets').update<Project>(id, data));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { InjectionToken } from '@angular/core';
|
||||||
|
import { ProjectRepository } from '@app/domain/projects/project.repository';
|
||||||
|
|
||||||
|
export const PROJECT_REPOSITORY_TOKEN = new InjectionToken<ProjectRepository>('ProjectRepository');
|
||||||
25
src/app/infrastructure/sectors/pb-sector.repository.ts
Normal file
25
src/app/infrastructure/sectors/pb-sector.repository.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { SectorRepository } from '@app/domain/sectors/sector.repository';
|
||||||
|
import { from, Observable } from 'rxjs';
|
||||||
|
import { environment } from '@env/environment';
|
||||||
|
import { Sector } from '@app/domain/sectors/sector.model';
|
||||||
|
import PocketBase from 'pocketbase';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class PbSectorRepository implements SectorRepository {
|
||||||
|
private pb = new PocketBase(environment.baseUrl);
|
||||||
|
|
||||||
|
getOne(sectorId: string): Observable<Sector> {
|
||||||
|
return from(this.pb.collection<Sector>('secteur').getOne<Sector>(sectorId));
|
||||||
|
}
|
||||||
|
|
||||||
|
list(): Observable<Sector[]> {
|
||||||
|
return from(
|
||||||
|
this.pb.collection<Sector>('secteur').getFullList({
|
||||||
|
sort: 'nom',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { InjectionToken } from '@angular/core';
|
||||||
|
import { SectorRepository } from '@app/domain/sectors/sector.repository';
|
||||||
|
|
||||||
|
export const SECTOR_REPOSITORY_TOKEN = new InjectionToken<SectorRepository>('SectorRepository');
|
||||||
@@ -1,42 +1,34 @@
|
|||||||
<section class="relative flex justify-center items-center">
|
<section class="relative flex justify-center items-center">
|
||||||
|
<div class="">
|
||||||
<div class="">
|
<div class="relative z-10 grid w-full min-h-full py-6 place-content-center">
|
||||||
<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">
|
||||||
<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">
|
||||||
<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
|
<div class="w-[300px] mx-auto space-y-4 p-6 text-gray-50">
|
||||||
class="relative flex items-center justify-center w-full h-full">
|
<h2 class="text-2xl font-extrabold text-center">Bon retour</h2>
|
||||||
<div class="w-[300px] mx-auto space-y-4 p-6 text-gray-50">
|
<p class="text-sm leading-tight text-center">
|
||||||
<h2 class="text-2xl font-extrabold text-center">
|
Les solutions à tes besoins sont à portée de main.
|
||||||
Bon retour
|
</p>
|
||||||
</h2>
|
<div class="absolute space-x-2 text-xs font-bold -translate-x-1/2 bottom-4 left-1/2">
|
||||||
<p class="text-sm leading-tight text-center">
|
<a href="">Politiques</a>
|
||||||
Les solutions à tes besoins sont à portée de main.
|
<a href="">Terms</a>
|
||||||
</p>
|
<a href="">Conditions</a>
|
||||||
<div
|
</div>
|
||||||
class="absolute space-x-2 text-xs font-bold -translate-x-1/2 bottom-4 left-1/2">
|
</div>
|
||||||
<a href="">Politiques</a>
|
</div>
|
||||||
<a href="">Terms</a>
|
</section>
|
||||||
<a href="">Conditions</a>
|
<section class="w-[300px] md:w-1/2 border bg-gray-50">
|
||||||
</div>
|
<div class="w-[300px] mx-auto space-y-6 p-6">
|
||||||
</div>
|
<div class="text-[#002B2F] text-center w-full mb-6">
|
||||||
</div>
|
<a [routerLink]="['']" class="inline-block h-[40px]">
|
||||||
</section>
|
<h2 class="text-3xl font-bold">Identifiez-vous</h2>
|
||||||
<section class="w-[300px] md:w-1/2 border bg-gray-50">
|
</a>
|
||||||
<div class="w-[300px] mx-auto space-y-6 p-6">
|
</div>
|
||||||
<div class="text-[#002B2F] text-center w-full mb-6">
|
|
||||||
<a [routerLink]="['']" class="inline-block h-[40px]">
|
<router-outlet />
|
||||||
<h2 class="text-3xl font-bold ">Identifiez-vous</h2>
|
</div>
|
||||||
</a>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<router-outlet/>
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,26 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { AuthComponent } from './auth.component';
|
import { AuthComponent } from './auth.component';
|
||||||
import {provideRouter} from "@angular/router";
|
import { provideRouter } from '@angular/router';
|
||||||
|
|
||||||
describe('AuthComponent', () => {
|
describe('AuthComponent', () => {
|
||||||
let component: AuthComponent;
|
let component: AuthComponent;
|
||||||
let fixture: ComponentFixture<AuthComponent>;
|
let fixture: ComponentFixture<AuthComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [AuthComponent],
|
imports: [AuthComponent],
|
||||||
providers:[
|
providers: [provideRouter([])],
|
||||||
provideRouter([])
|
}).compileComponents();
|
||||||
]
|
|
||||||
})
|
fixture = TestBed.createComponent(AuthComponent);
|
||||||
.compileComponents();
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
fixture = TestBed.createComponent(AuthComponent);
|
|
||||||
component = fixture.componentInstance;
|
await fixture.whenStable();
|
||||||
fixture.detectChanges();
|
});
|
||||||
|
|
||||||
await fixture.whenStable();
|
it('should create', () => {
|
||||||
});
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
it('should create', () => {
|
});
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import {RouterLink, RouterOutlet} from "@angular/router";
|
import { RouterLink, RouterOutlet } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-auth',
|
selector: 'app-auth',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [RouterOutlet, RouterLink],
|
||||||
RouterOutlet,
|
templateUrl: './auth.component.html',
|
||||||
RouterLink
|
styleUrl: './auth.component.scss',
|
||||||
],
|
})
|
||||||
templateUrl: './auth.component.html',
|
export class AuthComponent {}
|
||||||
styleUrl: './auth.component.scss'
|
|
||||||
})
|
|
||||||
export class AuthComponent {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,24 +1,28 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import {RouterModule, Routes} from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import {RegisterComponent} from "@app/shared/features/register/register.component";
|
import { RegisterComponent } from '@app/shared/features/register/register.component';
|
||||||
import {AuthComponent} from "@app/routes/authentification/auth/auth.component";
|
import { AuthComponent } from '@app/routes/authentification/auth/auth.component';
|
||||||
import {LoginComponent} from "@app/shared/features/login/login.component";
|
import { LoginComponent } from '@app/shared/features/login/login.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '', component: AuthComponent, children: [
|
path: '',
|
||||||
|
component: AuthComponent,
|
||||||
{path: '', component: LoginComponent},
|
children: [
|
||||||
{path: 'register', component: RegisterComponent},
|
{ path: '', component: LoginComponent },
|
||||||
]
|
{ path: 'register', component: RegisterComponent },
|
||||||
},
|
],
|
||||||
{path: 'not-found', loadChildren: () => import('@app/routes/not-found/not-found.module').then(m => m.NotFoundModule)},
|
},
|
||||||
{path: '**', redirectTo: 'not-found'}
|
{
|
||||||
];
|
path: 'not-found',
|
||||||
|
loadChildren: () =>
|
||||||
@NgModule({
|
import('@app/routes/not-found/not-found.module').then((m) => m.NotFoundModule),
|
||||||
imports: [RouterModule.forChild(routes)],
|
},
|
||||||
exports: [RouterModule]
|
{ path: '**', redirectTo: 'not-found' },
|
||||||
})
|
];
|
||||||
export class AuthentificationRoutingModule {
|
|
||||||
}
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class AuthentificationRoutingModule {}
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import {CommonModule} from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import {AuthentificationRoutingModule} from './authentification-routing.module';
|
import { AuthentificationRoutingModule } from './authentification-routing.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
@NgModule({
|
declarations: [],
|
||||||
declarations: [],
|
imports: [CommonModule, AuthentificationRoutingModule],
|
||||||
imports: [
|
})
|
||||||
CommonModule,
|
export class AuthentificationModule {}
|
||||||
AuthentificationRoutingModule
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class AuthentificationModule { }
|
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import {HomeComponent} from "@app/routes/home/home.component";
|
import { HomeComponent } from '@app/routes/home/home.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [{ path: '', component: HomeComponent, title: 'Accueil' }];
|
||||||
{path: '', component: HomeComponent, title: 'Accueil'}
|
|
||||||
];
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
@NgModule({
|
exports: [RouterModule],
|
||||||
imports: [RouterModule.forChild(routes)],
|
})
|
||||||
exports: [RouterModule]
|
export class HomeRoutingModule {}
|
||||||
})
|
|
||||||
export class HomeRoutingModule { }
|
|
||||||
|
|||||||
@@ -1,28 +1,23 @@
|
|||||||
<section class="">
|
<section class="">
|
||||||
|
<div class="w-full mx-auto px-4 sm:px-6 lg:px-8 pt-24 pb-10">
|
||||||
<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
|
||||||
<div class="mt-5 max-w-2xl text-center mx-auto">
|
class="block font-bold text-gray-800 gap-6 dark:text-white text-3xl md:text-5xl lg:text-6xl"
|
||||||
<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
|
||||||
Dans quel secteur se cache votre prochaine
|
<br />
|
||||||
<br>
|
<span class="text-gray-800 dark:text-white">pépites?</span>
|
||||||
<span class="text-gray-800 dark:text-white">pépites?</span>
|
|
||||||
|
<div class="word-animation max-sm:h-10 max-md:h-11 h-16">
|
||||||
<div class="word-animation max-sm:h-10 max-md:h-11 h-16">
|
<span class="red">Les finances</span>
|
||||||
<span class="red">Les finances</span>
|
<span class="orange">La Santé</span>
|
||||||
<span class="orange">La Santé</span>
|
<span class="blue">Les Etudes</span>
|
||||||
<span class="blue">Les Etudes</span>
|
</div>
|
||||||
</div>
|
</h1>
|
||||||
|
</div>
|
||||||
</h1>
|
|
||||||
</div>
|
<div class="mt-8 mx-auto w-full space-y-2">
|
||||||
|
<app-search (onSearchChange)="showNewQuery($event)" />
|
||||||
<div class="mt-8 mx-auto w-full space-y-2">
|
</div>
|
||||||
<app-search (onSearchChange)="showNewQuery($event)"/>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +1,66 @@
|
|||||||
h1 {
|
h1 {
|
||||||
.word-animation {
|
.word-animation {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
color: #4ec7f3;
|
color: #4ec7f3;
|
||||||
display: block;
|
display: block;
|
||||||
text-transform: capitalize;
|
text-transform: capitalize;
|
||||||
animation: rotateSpin 10s infinite;
|
animation: rotateSpin 10s infinite;
|
||||||
|
|
||||||
&.red {
|
&.red {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.orange {
|
&.orange {
|
||||||
color: orange;
|
color: orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.blue {
|
&.blue {
|
||||||
color: blue;
|
color: blue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes rotateSpin {
|
@keyframes rotateSpin {
|
||||||
10% {
|
10% {
|
||||||
-webkit-transform-style: translateY(-102%);
|
-webkit-transform-style: translateY(-102%);
|
||||||
transform: translateY(-102%);
|
transform: translateY(-102%);
|
||||||
}
|
}
|
||||||
|
|
||||||
25% {
|
25% {
|
||||||
-webkit-transform-style: translateY(-100%);
|
-webkit-transform-style: translateY(-100%);
|
||||||
transform: translateY(-100%);
|
transform: translateY(-100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
35% {
|
35% {
|
||||||
-webkit-transform-style: translateY(-202%);
|
-webkit-transform-style: translateY(-202%);
|
||||||
transform: translateY(-202%);
|
transform: translateY(-202%);
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
-webkit-transform-style: translateY(-200%);
|
-webkit-transform-style: translateY(-200%);
|
||||||
transform: translateY(-200%);
|
transform: translateY(-200%);
|
||||||
}
|
}
|
||||||
|
|
||||||
60% {
|
60% {
|
||||||
-webkit-transform-style: translateY(-302%);
|
-webkit-transform-style: translateY(-302%);
|
||||||
transform: translateY(-302%);
|
transform: translateY(-302%);
|
||||||
}
|
}
|
||||||
|
|
||||||
75% {
|
75% {
|
||||||
-webkit-transform-style: translateY(-300%);
|
-webkit-transform-style: translateY(-300%);
|
||||||
transform: translateY(-300%);
|
transform: translateY(-300%);
|
||||||
}
|
}
|
||||||
|
|
||||||
85% {
|
85% {
|
||||||
-webkit-transform-style: translateY(-402%);
|
-webkit-transform-style: translateY(-402%);
|
||||||
transform: translateY(-402%);
|
transform: translateY(-402%);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
-webkit-transform-style: translateY(-400%);
|
-webkit-transform-style: translateY(-400%);
|
||||||
transform: translateY(-400%);
|
transform: translateY(-400%);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { HomeComponent } from './home.component';
|
import { HomeComponent } from './home.component';
|
||||||
|
import { provideRouter } from '@angular/router';
|
||||||
describe('HomeComponent', () => {
|
|
||||||
let component: HomeComponent;
|
describe('HomeComponent', () => {
|
||||||
let fixture: ComponentFixture<HomeComponent>;
|
let component: HomeComponent;
|
||||||
|
let fixture: ComponentFixture<HomeComponent>;
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
beforeEach(async () => {
|
||||||
imports: [HomeComponent]
|
await TestBed.configureTestingModule({
|
||||||
})
|
imports: [HomeComponent],
|
||||||
.compileComponents();
|
providers: [provideRouter([])],
|
||||||
|
}).compileComponents();
|
||||||
fixture = TestBed.createComponent(HomeComponent);
|
|
||||||
component = fixture.componentInstance;
|
fixture = TestBed.createComponent(HomeComponent);
|
||||||
fixture.detectChanges();
|
component = fixture.componentInstance;
|
||||||
});
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
it('should create', () => {
|
||||||
});
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,23 +1,19 @@
|
|||||||
import {Component, inject} from '@angular/core';
|
import { Component, inject } from '@angular/core';
|
||||||
import {FaIconComponent} from "@fortawesome/angular-fontawesome";
|
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||||
import {SearchComponent} from "@app/shared/features/search/search.component";
|
import { SearchComponent } from '@app/shared/features/search/search.component';
|
||||||
import {Router} from "@angular/router";
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [FaIconComponent, SearchComponent],
|
||||||
FaIconComponent,
|
templateUrl: './home.component.html',
|
||||||
SearchComponent
|
styleUrl: './home.component.scss',
|
||||||
],
|
})
|
||||||
templateUrl: './home.component.html',
|
export class HomeComponent {
|
||||||
styleUrl: './home.component.scss'
|
private readonly router = inject(Router);
|
||||||
})
|
|
||||||
export class HomeComponent {
|
showNewQuery(newQuery: string) {
|
||||||
|
this.router.navigate(['/profiles'], { queryParams: { search: newQuery } });
|
||||||
private readonly router = inject(Router)
|
}
|
||||||
|
}
|
||||||
showNewQuery(newQuery: string) {
|
|
||||||
this.router.navigate(['/profiles'], {queryParams: {search: newQuery}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { HomeRoutingModule } from './home-routing.module';
|
import { HomeRoutingModule } from './home-routing.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
@NgModule({
|
declarations: [],
|
||||||
declarations: [],
|
imports: [CommonModule, HomeRoutingModule],
|
||||||
imports: [
|
})
|
||||||
CommonModule,
|
export class HomeModule {}
|
||||||
HomeRoutingModule
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class HomeModule { }
|
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import {RouterModule, Routes} from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import {MyProfileComponent} from "@app/routes/my-profile/my-profile.component";
|
import { MyProfileComponent } from '@app/routes/my-profile/my-profile.component';
|
||||||
import {myProfileResolver} from "@app/core/resolvers/my-profile/my-profile.resolver";
|
import { myProfileResolver } from '@app/core/resolvers/my-profile/my-profile.resolver';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '', component: MyProfileComponent, resolve: {user: myProfileResolver}
|
path: '',
|
||||||
}
|
component: MyProfileComponent,
|
||||||
];
|
resolve: { user: myProfileResolver },
|
||||||
|
},
|
||||||
@NgModule({
|
];
|
||||||
imports: [RouterModule.forChild(routes)],
|
|
||||||
exports: [RouterModule]
|
@NgModule({
|
||||||
})
|
imports: [RouterModule.forChild(routes)],
|
||||||
export class MyProfileRoutingModule {
|
exports: [RouterModule],
|
||||||
}
|
})
|
||||||
|
export class MyProfileRoutingModule {}
|
||||||
|
|||||||
@@ -1,192 +1,268 @@
|
|||||||
@if (profile != undefined) {
|
@if (profile != undefined) {
|
||||||
|
<section class="text-gray-600">
|
||||||
<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="container px-5 py-5 mx-auto flex flex-col">
|
<div class="w-max flex justify-between items-center">
|
||||||
|
<a [routerLink]="['']" (click)="location.back()">
|
||||||
<div class="w-full max-md:mx-5 mx-auto my-5 rounded-lg min-h-56 max-h-64 bg-cover bg-auth">
|
<svg
|
||||||
<div class="w-max flex justify-between items-center">
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<a [routerLink]="['']" (click)="location.back()">
|
viewBox="0 0 24 24"
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
fill="currentColor"
|
||||||
class="hover:text-gray-300 text-white size-6 w-5 h-5 sm:w-9 sm:h-9 mx-4 my-4 hover:w-10 hover:h-10 ">
|
class="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"
|
<title>Retour</title>
|
||||||
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"
|
<path
|
||||||
clip-rule="evenodd"/>
|
fill-rule="evenodd"
|
||||||
</svg>
|
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"
|
||||||
</a>
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
@if (profile.estVerifier) {
|
</svg>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"
|
</a>
|
||||||
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>
|
@if (profile().estVerifier) {
|
||||||
<path fill-rule="evenodd"
|
<svg
|
||||||
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"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
clip-rule="evenodd"/>
|
viewBox="0 0 24 24"
|
||||||
</svg>
|
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"
|
||||||
</div>
|
>
|
||||||
</div>
|
<title>Profile verifier</title>
|
||||||
|
<path
|
||||||
<div class="flex flex-col justify-center sm:flex-row sm:justify-between">
|
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"
|
||||||
<div class=" text-center sm:pr-8 sm:py-8">
|
clip-rule="evenodd"
|
||||||
@if (!isEditMode()) {
|
/>
|
||||||
<div class="w-max" (click)="isEditMode.set(!isEditMode())">
|
</svg>
|
||||||
<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">
|
</div>
|
||||||
<title>Editer le profil</title>
|
</div>
|
||||||
<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"/>
|
<div class="flex flex-col justify-center sm:flex-row sm:justify-between">
|
||||||
<path
|
<div class="text-center sm:pr-8 sm:py-8">
|
||||||
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"/>
|
@if (!isEditMode()) {
|
||||||
</svg>
|
<div class="w-max" (click)="isEditMode.set(!isEditMode())">
|
||||||
</div>
|
<svg
|
||||||
}
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
@if (!isEditMode()) {
|
class="w-6 h-6 cursor-pointer hover:text-gray-800 dark:text-white"
|
||||||
<div class="w-28 h-28 rounded-full inline-flex items-center justify-center bg-gray-200 text-gray-400">
|
>
|
||||||
|
<title>Editer le profil</title>
|
||||||
@if (user().avatar) {
|
<path
|
||||||
<img alt="{{user().username}}" class="object-cover object-center h-full w-full rounded-full"
|
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"
|
||||||
src="{{environment.baseUrl}}/api/files/users/{{user().id}}/{{user().avatar}}">
|
/>
|
||||||
|
<path
|
||||||
} @else {
|
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"
|
||||||
<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}}">
|
</svg>
|
||||||
}
|
</div>
|
||||||
</div>
|
}
|
||||||
|
|
||||||
<div class="flex flex-col w-full items-center text-center justify-center">
|
@if (!isEditMode()) {
|
||||||
@if (user().name) {
|
<div
|
||||||
<h2
|
class="w-28 h-28 rounded-full inline-flex items-center justify-center bg-gray-200 text-gray-400"
|
||||||
class="font-medium title-font mt-4 text-gray-900 text-lg w-full dark:text-white">{{ user().name }}</h2>
|
>
|
||||||
} @else if (user().username) {
|
@if (user().avatar) {
|
||||||
<h2
|
<img
|
||||||
class="font-medium title-font mt-4 text-gray-900 text-lg w-full dark:text-white">{{ user().username }}</h2>
|
alt="{{ user().username }}"
|
||||||
} @else {
|
class="object-cover object-center h-full w-full rounded-full"
|
||||||
<h2
|
src="{{ environment.baseUrl }}/api/files/users/{{ user().id }}/{{
|
||||||
class="font-medium title-font mt-4 text-gray-900 text-lg w-full dark:text-white">{{ user().email }}</h2>
|
user().avatar
|
||||||
}
|
}}"
|
||||||
<div class="w-12 h-1 bg-indigo-500 rounded mt-2 mb-4"></div>
|
/>
|
||||||
@if (profile.bio) {
|
} @else {
|
||||||
<p class="text-base dark:text-white w-full">{{ profile.bio }}</p>
|
<img
|
||||||
} @else {
|
alt="{{ user().username }}"
|
||||||
<p class="text-base dark:text-white w-full">Je suis sur la plateforme Trouve Ton Profile pour partager
|
class="object-cover object-center h-full w-full rounded-full"
|
||||||
mon
|
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{ user().username }}"
|
||||||
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>
|
||||||
|
|
||||||
}
|
<div class="flex flex-col w-full items-center text-center justify-center">
|
||||||
|
@if (user().name) {
|
||||||
@if (profile.secteur) {
|
<h2
|
||||||
<div class="space-y-2 flex flex-col my-4">
|
class="font-medium title-font mt-4 text-gray-900 text-lg w-full dark:text-white"
|
||||||
<p class="text-base dark:text-white">Secteur</p>
|
>
|
||||||
<app-chips [sectorId]="profile.secteur"/>
|
{{ user().name }}
|
||||||
</div>
|
</h2>
|
||||||
}
|
} @else if (user().username) {
|
||||||
|
<h2
|
||||||
@if (profile.reseaux) {
|
class="font-medium title-font mt-4 text-gray-900 text-lg w-full dark:text-white"
|
||||||
<div class="space-y-2 flex flex-col my-4">
|
>
|
||||||
<p class="text-base dark:text-white">Réseaux</p>
|
{{ user().username }}
|
||||||
<app-reseaux [reseaux]="profile.reseaux"/>
|
</h2>
|
||||||
</div>
|
} @else {
|
||||||
}
|
<h2
|
||||||
</div>
|
class="font-medium title-font mt-4 text-gray-900 text-lg w-full dark:text-white"
|
||||||
<a [href]="qrCodeDownloadLink" download="qrcode"
|
>
|
||||||
class="w-full flex items-center justify-center sm:pr-8 sm:py-8">
|
{{ user().email }}
|
||||||
<qrcode (qrCodeURL)="onChangeURL($event)" [qrdata]="myProfileQrCode" [width]="128" [elementType]="'url'"
|
</h2>
|
||||||
[errorCorrectionLevel]="'M'" class="mx-auto"></qrcode>
|
}
|
||||||
</a>
|
<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 w-full">{{ profile().bio }}</p>
|
||||||
@if (isEditMode()) {
|
} @else {
|
||||||
<app-update-user [user]="user()" (isCancelEditMode)="onCancelEditMode($event)"/>
|
<p class="text-base dark:text-white w-full">
|
||||||
}
|
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.
|
||||||
</div>
|
</p>
|
||||||
|
}
|
||||||
<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">
|
@if (profile().secteur) {
|
||||||
|
<div class="space-y-2 flex flex-col my-4">
|
||||||
<div class=" p-4">
|
<p class="text-base dark:text-white">Secteur</p>
|
||||||
<ul class="w-full flex flex-wrap gap-x-2 gap-y-2 rounded-lg items-center max-sm:mx-auto">
|
<app-chips [sectorId]="profile().secteur" />
|
||||||
<li id="homeTab" (click)="menu.set('home')"
|
</div>
|
||||||
[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">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="w-5 mb-1.5"
|
@if (profile().reseaux) {
|
||||||
viewBox="0 0 511 511.999">
|
<div class="space-y-2 flex flex-col my-4">
|
||||||
<path
|
<p class="text-base dark:text-white">Réseaux</p>
|
||||||
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"
|
<app-reseaux [reseaux]="profile().reseaux" />
|
||||||
data-original="#000000"></path>
|
</div>
|
||||||
</svg>
|
}
|
||||||
Mon profile
|
</div>
|
||||||
</li>
|
<a
|
||||||
<li id="settingTab" (click)="menu.set('projects')"
|
[href]="qrCodeDownloadLink"
|
||||||
[ngClass]="{'border-blue-600 text-blue-600': menu()=='projects'.toLowerCase()}"
|
download="qrcode"
|
||||||
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">
|
class="w-full flex items-center justify-center sm:pr-8 sm:py-8"
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
>
|
||||||
stroke="currentColor" class="size-6">
|
<qrcode
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
(qrCodeURL)="onChangeURL($event)"
|
||||||
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"/>
|
[qrdata]="myProfileQrCode"
|
||||||
</svg>
|
[width]="128"
|
||||||
Mes projets
|
[elementType]="'url'"
|
||||||
</li>
|
[errorCorrectionLevel]="'M'"
|
||||||
<li id="profileTab" (click)="menu.set('update')"
|
class="mx-auto"
|
||||||
[ngClass]="{'border-blue-600 text-blue-600': menu()=='update'.toLowerCase()}"
|
></qrcode>
|
||||||
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">
|
</a>
|
||||||
<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"
|
@if (isEditMode()) {
|
||||||
data-original="#000000"></path>
|
<app-update-user [user]="user()" (isCancelEditMode)="onCancelEditMode($event)" />
|
||||||
</svg>
|
}
|
||||||
Mes informations
|
</div>
|
||||||
</li>
|
|
||||||
<li id="cvTab" (click)="menu.set('cv')"
|
<div
|
||||||
[ngClass]="{'border-blue-600 text-blue-600': menu()=='cv'.toLowerCase()}"
|
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"
|
||||||
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"
|
<div class="p-4">
|
||||||
stroke="currentColor" class="size-6">
|
<ul
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
class="w-full flex flex-wrap gap-x-2 gap-y-2 rounded-lg items-center max-sm:mx-auto"
|
||||||
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>
|
<li
|
||||||
|
id="homeTab"
|
||||||
Lecteur de PDF
|
(click)="menu.set('home')"
|
||||||
</li>
|
[ngClass]="{ 'border-blue-600 text-blue-600': menu() == 'home'.toLowerCase() }"
|
||||||
</ul>
|
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"
|
||||||
|
>
|
||||||
<div id="homeContent" class="tab-content max-w-2xl block mt-8">
|
<svg
|
||||||
@switch (menu().toLowerCase()) {
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@case ('home'.toLowerCase()) {
|
fill="currentColor"
|
||||||
<app-my-home-profile [profile]="profile"/>
|
class="w-5 mb-1.5"
|
||||||
}
|
viewBox="0 0 511 511.999"
|
||||||
@case ('projects'.toLowerCase()) {
|
>
|
||||||
<app-my-profile-project-list [projectIds]="profile.projets" [userId]="user().id"/>
|
<path
|
||||||
<router-outlet/>
|
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"
|
||||||
@case ('update'.toLowerCase()) {
|
></path>
|
||||||
<app-my-profile-update-form [profile]="profile"/>
|
</svg>
|
||||||
}
|
Mon profile
|
||||||
@case ('cv'.toLowerCase()) {
|
</li>
|
||||||
<app-pdf-viewer [profile]="profile"/>
|
<li
|
||||||
}
|
id="settingTab"
|
||||||
@default {
|
(click)="menu.set('projects')"
|
||||||
<app-my-home-profile [profile]="profile"/>
|
[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"
|
||||||
}
|
>
|
||||||
</div>
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
</div>
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
</div>
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
</div>
|
class="size-6"
|
||||||
|
>
|
||||||
</div>
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
</section>
|
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>
|
||||||
|
|
||||||
|
Lecteur de PDF
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div id="homeContent" class="tab-content max-w-2xl block mt-8">
|
||||||
|
@switch (menu().toLowerCase()) {
|
||||||
|
@case ('home'.toLowerCase()) {
|
||||||
|
<app-my-home-profile [profile]="profile()" />
|
||||||
|
}
|
||||||
|
@case ('projects'.toLowerCase()) {
|
||||||
|
<app-my-profile-project-list
|
||||||
|
[projectIds]="profile().projets"
|
||||||
|
[userId]="user().id"
|
||||||
|
/>
|
||||||
|
<router-outlet />
|
||||||
|
}
|
||||||
|
@case ('update'.toLowerCase()) {
|
||||||
|
<app-my-profile-update-form [profile]="profile()" />
|
||||||
|
}
|
||||||
|
@case ('cv'.toLowerCase()) {
|
||||||
|
<app-pdf-viewer [profile]="profile()" />
|
||||||
|
}
|
||||||
|
@default {
|
||||||
|
<app-my-home-profile [profile]="profile()" />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,29 +1,41 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { MyProfileComponent } from './my-profile.component';
|
import { MyProfileComponent } from './my-profile.component';
|
||||||
import {provideRouter} from "@angular/router";
|
import { provideRouter } from '@angular/router';
|
||||||
|
import { ProfileRepository } from '@app/domain/profiles/profile.repository';
|
||||||
describe('MyProfileComponent', () => {
|
import { PROFILE_REPOSITORY_TOKEN } from '@app/infrastructure/profiles/profile-repository.token';
|
||||||
let component: MyProfileComponent;
|
import { of } from 'rxjs';
|
||||||
let fixture: ComponentFixture<MyProfileComponent>;
|
import { Profile } from '@app/domain/profiles/profile.model';
|
||||||
|
|
||||||
beforeEach(async () => {
|
describe('MyProfileComponent', () => {
|
||||||
await TestBed.configureTestingModule({
|
let component: MyProfileComponent;
|
||||||
imports: [MyProfileComponent],
|
let fixture: ComponentFixture<MyProfileComponent>;
|
||||||
providers: [
|
|
||||||
provideRouter([])
|
let mockProfileRepo: ProfileRepository;
|
||||||
]
|
|
||||||
})
|
beforeEach(async () => {
|
||||||
.compileComponents();
|
mockProfileRepo = {
|
||||||
|
create: jest.fn(),
|
||||||
fixture = TestBed.createComponent(MyProfileComponent);
|
list: jest.fn(),
|
||||||
component = fixture.componentInstance;
|
update: jest.fn(),
|
||||||
fixture.detectChanges();
|
getByUserId: jest.fn().mockReturnValue(of({} as Profile)),
|
||||||
|
};
|
||||||
await fixture.whenStable();
|
await TestBed.configureTestingModule({
|
||||||
});
|
imports: [MyProfileComponent],
|
||||||
|
providers: [
|
||||||
it('should create', () => {
|
provideRouter([]),
|
||||||
expect(component).toBeTruthy();
|
{ provide: PROFILE_REPOSITORY_TOKEN, useValue: mockProfileRepo },
|
||||||
});
|
],
|
||||||
});
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(MyProfileComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
await fixture.whenStable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,87 +1,74 @@
|
|||||||
import {Component, computed, inject, OnInit, signal} from '@angular/core';
|
import { Component, computed, inject, OnInit, signal } from '@angular/core';
|
||||||
import {ActivatedRoute, RouterLink, RouterOutlet} from "@angular/router";
|
import { ActivatedRoute, RouterLink, RouterOutlet } from '@angular/router';
|
||||||
import {User} from "@app/shared/models/user";
|
import { User } from '@app/shared/models/user';
|
||||||
import {AsyncPipe, JsonPipe, Location, NgClass, UpperCasePipe} from "@angular/common";
|
import { Location, NgClass } from '@angular/common';
|
||||||
import {UntilDestroy} from "@ngneat/until-destroy";
|
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||||
import {SafeUrl} from "@angular/platform-browser";
|
import { SafeUrl } from '@angular/platform-browser';
|
||||||
import {QRCodeModule} from "angularx-qrcode";
|
import { QRCodeModule } from 'angularx-qrcode';
|
||||||
import {environment} from "@env/environment";
|
import { environment } from '@env/environment';
|
||||||
import {ChipsComponent} from "@app/shared/components/chips/chips.component";
|
import { ChipsComponent } from '@app/shared/components/chips/chips.component';
|
||||||
import {ReseauxComponent} from "@app/shared/components/reseaux/reseaux.component";
|
import { ReseauxComponent } from '@app/shared/components/reseaux/reseaux.component';
|
||||||
import {UpdateUserComponent} from "@app/shared/features/update-user/update-user.component";
|
import { UpdateUserComponent } from '@app/shared/features/update-user/update-user.component';
|
||||||
import {
|
import { MyProfileProjectListComponent } from '@app/shared/components/my-profile-project-list/my-profile-project-list.component';
|
||||||
MyProfileProjectListComponent
|
import { MyHomeProfileComponent } from '@app/shared/components/my-home-profile/my-home-profile.component';
|
||||||
} from "@app/shared/components/my-profile-project-list/my-profile-project-list.component";
|
import { MyProfileUpdateFormComponent } from '@app/shared/components/my-profile-update-form/my-profile-update-form.component';
|
||||||
import {MyHomeProfileComponent} from "@app/shared/components/my-home-profile/my-home-profile.component";
|
import { PdfViewerComponent } from '@app/shared/features/pdf-viewer/pdf-viewer.component';
|
||||||
import {
|
import { ProfileFacade } from '@app/ui/profiles/profile.facade';
|
||||||
MyProfileUpdateFormComponent
|
|
||||||
} from "@app/shared/components/my-profile-update-form/my-profile-update-form.component";
|
@Component({
|
||||||
import {ProfileService} from "@app/core/services/profile/profile.service";
|
selector: 'app-my-profile',
|
||||||
import {Profile} from "@app/shared/models/profile";
|
standalone: true,
|
||||||
import {PdfViewerComponent} from "@app/shared/features/pdf-viewer/pdf-viewer.component";
|
imports: [
|
||||||
|
RouterLink,
|
||||||
@Component({
|
QRCodeModule,
|
||||||
selector: 'app-my-profile',
|
ChipsComponent,
|
||||||
standalone: true,
|
ReseauxComponent,
|
||||||
imports: [
|
UpdateUserComponent,
|
||||||
JsonPipe,
|
MyProfileProjectListComponent,
|
||||||
RouterLink,
|
RouterOutlet,
|
||||||
AsyncPipe,
|
MyHomeProfileComponent,
|
||||||
QRCodeModule,
|
MyProfileUpdateFormComponent,
|
||||||
ChipsComponent,
|
NgClass,
|
||||||
ReseauxComponent,
|
PdfViewerComponent,
|
||||||
UpdateUserComponent,
|
],
|
||||||
UpperCasePipe,
|
templateUrl: './my-profile.component.html',
|
||||||
MyProfileProjectListComponent,
|
styleUrl: './my-profile.component.scss',
|
||||||
RouterOutlet,
|
})
|
||||||
MyHomeProfileComponent,
|
@UntilDestroy()
|
||||||
MyProfileUpdateFormComponent,
|
export class MyProfileComponent implements OnInit {
|
||||||
NgClass,
|
protected readonly environment = environment;
|
||||||
PdfViewerComponent
|
protected menu = signal<string>('home');
|
||||||
],
|
|
||||||
templateUrl: './my-profile.component.html',
|
protected myProfileQrCode = `${environment.production}`;
|
||||||
styleUrl: './my-profile.component.scss'
|
protected qrCodeDownloadLink: SafeUrl = `${environment.production}`;
|
||||||
})
|
|
||||||
@UntilDestroy()
|
protected location = inject(Location);
|
||||||
export class MyProfileComponent implements OnInit {
|
protected readonly route = inject(ActivatedRoute);
|
||||||
|
|
||||||
private profileService = inject(ProfileService);
|
protected extraData: { user: User } = this.route.snapshot.data['user'];
|
||||||
|
|
||||||
protected readonly environment = environment;
|
protected user = computed(() => {
|
||||||
protected menu = signal<string>("home");
|
if (this.extraData != undefined) return this.extraData.user;
|
||||||
|
return {} as User;
|
||||||
protected myProfileQrCode: string = `${environment.production}`;
|
});
|
||||||
protected qrCodeDownloadLink: SafeUrl = `${environment.production}`;
|
|
||||||
|
protected isEditMode = signal<boolean>(false);
|
||||||
protected location = inject(Location);
|
|
||||||
protected readonly route = inject(ActivatedRoute);
|
onChangeURL(url: SafeUrl) {
|
||||||
|
this.qrCodeDownloadLink = url;
|
||||||
protected extraData: { user: User } = this.route.snapshot.data['user'];
|
}
|
||||||
|
|
||||||
protected user = computed(() => {
|
onCancelEditMode($event: boolean) {
|
||||||
if (this.extraData != undefined) return this.extraData.user;
|
this.isEditMode.set(!$event);
|
||||||
return {} as User;
|
}
|
||||||
});
|
|
||||||
|
private readonly profileFacade = new ProfileFacade();
|
||||||
protected profile: Profile = {} as Profile;
|
protected profile = this.profileFacade.profile;
|
||||||
|
protected readonly loading = this.profileFacade.loading;
|
||||||
protected isEditMode = signal<boolean>(false);
|
protected readonly error = this.profileFacade.error;
|
||||||
|
|
||||||
onChangeURL(url: SafeUrl) {
|
ngOnInit(): void {
|
||||||
this.qrCodeDownloadLink = url;
|
this.myProfileQrCode = `${this.myProfileQrCode}/profiles/${this.user().id}`;
|
||||||
}
|
this.profileFacade.loadOne(this.user().id);
|
||||||
|
}
|
||||||
onCancelEditMode($event: boolean) {
|
}
|
||||||
this.isEditMode.set(!$event);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.myProfileQrCode = `${this.myProfileQrCode}/profiles/${this.user().id}`;
|
|
||||||
|
|
||||||
this.profileService.getProfileByUserId(this.user().id).subscribe({
|
|
||||||
next: (value: Profile) => {
|
|
||||||
this.profile = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { MyProfileRoutingModule } from './my-profile-routing.module';
|
import { MyProfileRoutingModule } from './my-profile-routing.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
@NgModule({
|
declarations: [],
|
||||||
declarations: [],
|
imports: [CommonModule, MyProfileRoutingModule],
|
||||||
imports: [
|
})
|
||||||
CommonModule,
|
export class MyProfileModule {}
|
||||||
MyProfileRoutingModule
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class MyProfileModule { }
|
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import {NotFoundComponent} from "@app/routes/not-found/not-found.component";
|
import { NotFoundComponent } from '@app/routes/not-found/not-found.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [{ path: '', component: NotFoundComponent, title: 'Page non trouvée' }];
|
||||||
{path: '', component: NotFoundComponent, title: 'Page non trouvée'}
|
|
||||||
];
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
@NgModule({
|
exports: [RouterModule],
|
||||||
imports: [RouterModule.forChild(routes)],
|
})
|
||||||
exports: [RouterModule]
|
export class NotFoundRoutingModule {}
|
||||||
})
|
|
||||||
export class NotFoundRoutingModule { }
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<p>not-found works!</p>
|
<p>not-found works!</p>
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { NotFoundComponent } from './not-found.component';
|
import { NotFoundComponent } from './not-found.component';
|
||||||
|
|
||||||
describe('NotFoundComponent', () => {
|
describe('NotFoundComponent', () => {
|
||||||
let component: NotFoundComponent;
|
let component: NotFoundComponent;
|
||||||
let fixture: ComponentFixture<NotFoundComponent>;
|
let fixture: ComponentFixture<NotFoundComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [NotFoundComponent]
|
imports: [NotFoundComponent],
|
||||||
})
|
}).compileComponents();
|
||||||
.compileComponents();
|
|
||||||
|
fixture = TestBed.createComponent(NotFoundComponent);
|
||||||
fixture = TestBed.createComponent(NotFoundComponent);
|
component = fixture.componentInstance;
|
||||||
component = fixture.componentInstance;
|
fixture.detectChanges();
|
||||||
fixture.detectChanges();
|
});
|
||||||
});
|
|
||||||
|
it('should create', () => {
|
||||||
it('should create', () => {
|
expect(component).toBeTruthy();
|
||||||
expect(component).toBeTruthy();
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-not-found',
|
selector: 'app-not-found',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [],
|
imports: [],
|
||||||
templateUrl: './not-found.component.html',
|
templateUrl: './not-found.component.html',
|
||||||
styleUrl: './not-found.component.scss'
|
styleUrl: './not-found.component.scss',
|
||||||
})
|
})
|
||||||
export class NotFoundComponent {
|
export class NotFoundComponent {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { NotFoundRoutingModule } from './not-found-routing.module';
|
import { NotFoundRoutingModule } from './not-found-routing.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
@NgModule({
|
declarations: [],
|
||||||
declarations: [],
|
imports: [CommonModule, NotFoundRoutingModule],
|
||||||
imports: [
|
})
|
||||||
CommonModule,
|
export class NotFoundModule {}
|
||||||
NotFoundRoutingModule
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class NotFoundModule { }
|
|
||||||
|
|||||||
@@ -1,83 +1,115 @@
|
|||||||
<section class="text-gray-600">
|
<section class="text-gray-600">
|
||||||
<div class="container px-5 py-12 mx-auto flex flex-col">
|
<div class="container px-5 py-12 mx-auto flex flex-col">
|
||||||
<div class="lg:w-4/6 mx-auto">
|
<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="rounded-lg min-h-56 max-h-64 overflow-hidden bg-cover bg-auth">
|
||||||
<div class="w-full flex justify-between items-center">
|
<div class="w-full flex justify-between items-center">
|
||||||
<a [routerLink]="['/profiles']">
|
<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 ">
|
<svg
|
||||||
<title>Retour</title>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<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" />
|
viewBox="0 0 24 24"
|
||||||
</svg>
|
fill="currentColor"
|
||||||
</a>
|
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"
|
||||||
|
>
|
||||||
@if (profile().estVerifier) {
|
<title>Retour</title>
|
||||||
<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">
|
<path
|
||||||
<title>Profile verifier</title>
|
fill-rule="evenodd"
|
||||||
<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"
|
||||||
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"
|
||||||
clip-rule="evenodd"/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
}
|
</a>
|
||||||
</div>
|
|
||||||
|
@if (profile().estVerifier) {
|
||||||
<!-- <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}}">-->
|
<svg
|
||||||
</div>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<div class="flex flex-col sm:flex-row mt-10">
|
viewBox="0 0 24 24"
|
||||||
<div class="sm:w-1/3 text-center sm:pr-8 sm:py-8">
|
fill="currentColor"
|
||||||
<div class="w-20 h-20 rounded-full inline-flex items-center justify-center bg-gray-200 text-gray-400">
|
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"
|
||||||
@if (user().avatar) {
|
>
|
||||||
<img alt="{{user().username}}" class="object-cover object-center h-full w-full rounded-full"
|
<title>Profile verifier</title>
|
||||||
src="{{environment.baseUrl}}/api/files/users/{{user().id}}/{{user().avatar}}" loading="lazy">
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
} @else {
|
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"
|
||||||
<img alt="{{user().username}}" class="object-cover object-center h-full w-full rounded-full"
|
clip-rule="evenodd"
|
||||||
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{user().username}}" loading="lazy">
|
/>
|
||||||
}
|
</svg>
|
||||||
</div>
|
}
|
||||||
<div class="flex flex-col items-center text-center justify-center">
|
</div>
|
||||||
@if (user().name){
|
|
||||||
<h2 class="font-medium title-font mt-4 text-gray-900 text-lg dark:text-white">{{user().name}}</h2>
|
<!-- <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}}">-->
|
||||||
} @else if (user().username){
|
</div>
|
||||||
<h2 class="font-medium title-font mt-4 text-gray-900 text-lg dark:text-white">{{user().username}}</h2>
|
<div class="flex flex-col sm:flex-row mt-10">
|
||||||
} @else {
|
<div class="sm:w-1/3 text-center sm:pr-8 sm:py-8">
|
||||||
<h2 class="font-medium title-font mt-4 text-gray-900 text-lg dark:text-white">{{user().email}}</h2>
|
<div
|
||||||
}
|
class="w-20 h-20 rounded-full inline-flex items-center justify-center bg-gray-200 text-gray-400"
|
||||||
<div class="w-12 h-1 bg-indigo-500 rounded mt-2 mb-4"></div>
|
>
|
||||||
@if (profile().bio) {
|
@if (user().avatar) {
|
||||||
<p class="text-base dark:text-white">{{ profile().bio }}</p>
|
<img
|
||||||
} @else {
|
alt="{{ user().username }}"
|
||||||
<p class="text-base dark:text-white">Je suis sur la plateforme Trouve Ton Profile pour partager mon expertise et mes
|
class="object-cover object-center h-full w-full rounded-full"
|
||||||
compétences. N’hésitez pas à me contacter pour en savoir plus sur mon parcours et mes domaines
|
src="{{ environment.baseUrl }}/api/files/users/{{ user().id }}/{{ user().avatar }}"
|
||||||
d’intervention.</p>
|
loading="lazy"
|
||||||
|
/>
|
||||||
}
|
} @else {
|
||||||
|
<img
|
||||||
@if(profile().secteur){
|
alt="{{ user().username }}"
|
||||||
<div class="space-y-2 flex flex-col my-4">
|
class="object-cover object-center h-full w-full rounded-full"
|
||||||
<p class="text-base dark:text-white">Secteur</p>
|
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{ user().username }}"
|
||||||
<app-chips [sectorId]="profile().secteur"/>
|
loading="lazy"
|
||||||
</div>
|
/>
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
@if (profile().reseaux){
|
<div class="flex flex-col items-center text-center justify-center">
|
||||||
<div class="space-y-2 flex flex-col my-4">
|
@if (user().name) {
|
||||||
<p class="text-base dark:text-white">Réseaux</p>
|
<h2 class="font-medium title-font mt-4 text-gray-900 text-lg dark:text-white">
|
||||||
<app-reseaux [reseaux]="profile().reseaux"/>
|
{{ user().name }}
|
||||||
</div>
|
</h2>
|
||||||
}
|
} @else if (user().username) {
|
||||||
|
<h2 class="font-medium title-font mt-4 text-gray-900 text-lg dark:text-white">
|
||||||
</div>
|
{{ user().username }}
|
||||||
</div>
|
</h2>
|
||||||
<div
|
} @else {
|
||||||
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="font-medium title-font mt-4 text-gray-900 text-lg dark:text-white">
|
||||||
|
{{ user().email }}
|
||||||
<h2 class="text-3xl font-extrabold text-black dark:text-white">{{profile().profession | uppercase}}</h2>
|
</h2>
|
||||||
<p class="leading-relaxed text-lg mb-4 dark:text-white">{{profile().apropos}}</p>
|
}
|
||||||
|
<div class="w-12 h-1 bg-indigo-500 rounded mt-2 mb-4"></div>
|
||||||
<app-project-list [userProjectId]="profile().utilisateur"/>
|
@if (profile().bio) {
|
||||||
|
<p class="text-base dark:text-white">{{ profile().bio }}</p>
|
||||||
</div>
|
} @else {
|
||||||
</div>
|
<p class="text-base dark:text-white">
|
||||||
</div>
|
Je suis sur la plateforme Trouve Ton Profile pour partager mon expertise et mes
|
||||||
</div>
|
compétences. N’hésitez pas à me contacter pour en savoir plus sur mon parcours et
|
||||||
</section>
|
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" />
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
</section>
|
||||||
|
|||||||
@@ -1,29 +1,51 @@
|
|||||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import {ProfileDetailComponent} from './profile-detail.component';
|
import { ProfileDetailComponent } from './profile-detail.component';
|
||||||
import {provideRouter} from "@angular/router";
|
import { provideRouter } from '@angular/router';
|
||||||
|
import { PROJECT_REPOSITORY_TOKEN } from '@app/infrastructure/projects/project-repository.token';
|
||||||
describe('ProfileDetailComponent', () => {
|
import { ProjectRepository } from '@app/domain/projects/project.repository';
|
||||||
let component: ProfileDetailComponent;
|
import { of } from 'rxjs';
|
||||||
let fixture: ComponentFixture<ProfileDetailComponent>;
|
import { Project } from '@app/domain/projects/project.model';
|
||||||
|
import { Sector } from '@app/domain/sectors/sector.model';
|
||||||
beforeEach(async () => {
|
import { SectorRepository } from '@app/domain/sectors/sector.repository';
|
||||||
await TestBed.configureTestingModule({
|
import { SECTOR_REPOSITORY_TOKEN } from '@app/infrastructure/sectors/sector-repository.token';
|
||||||
imports: [ProfileDetailComponent],
|
|
||||||
providers:[
|
describe('ProfileDetailComponent', () => {
|
||||||
provideRouter([])
|
let component: ProfileDetailComponent;
|
||||||
]
|
let fixture: ComponentFixture<ProfileDetailComponent>;
|
||||||
})
|
let mockProjectRepository: jest.Mocked<ProjectRepository>;
|
||||||
.compileComponents();
|
let mockSectorRepo: SectorRepository;
|
||||||
|
|
||||||
fixture = TestBed.createComponent(ProfileDetailComponent);
|
beforeEach(async () => {
|
||||||
component = fixture.componentInstance;
|
mockProjectRepository = {
|
||||||
fixture.detectChanges();
|
create: jest.fn().mockReturnValue(of({} as Project)),
|
||||||
|
list: jest.fn().mockReturnValue(of([])),
|
||||||
await fixture.whenStable();
|
get: jest.fn().mockReturnValue(of({} as Project)),
|
||||||
});
|
update: jest.fn().mockReturnValue(of({} as Project)),
|
||||||
|
};
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
mockSectorRepo = {
|
||||||
});
|
list: jest.fn(),
|
||||||
});
|
getOne: jest.fn().mockReturnValue(of({} as Sector)),
|
||||||
|
};
|
||||||
|
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ProfileDetailComponent],
|
||||||
|
providers: [
|
||||||
|
provideRouter([]),
|
||||||
|
{ provide: PROJECT_REPOSITORY_TOKEN, useValue: mockProjectRepository },
|
||||||
|
{ provide: SECTOR_REPOSITORY_TOKEN, useValue: mockSectorRepo },
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ProfileDetailComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
await fixture.whenStable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,46 +1,43 @@
|
|||||||
import {Component, computed, inject} from '@angular/core';
|
import { Component, computed, inject } from '@angular/core';
|
||||||
import {ActivatedRoute, RouterLink} from "@angular/router";
|
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||||
import {QRCodeModule} from "angularx-qrcode";
|
import { QRCodeModule } from 'angularx-qrcode';
|
||||||
import {UpperCasePipe} from "@angular/common";
|
import { UpperCasePipe } from '@angular/common';
|
||||||
import {User} from "@app/shared/models/user";
|
import { User } from '@app/shared/models/user';
|
||||||
import {Profile} from "@app/shared/models/profile";
|
import { ChipsComponent } from '@app/shared/components/chips/chips.component';
|
||||||
import {ChipsComponent} from "@app/shared/components/chips/chips.component";
|
import { ReseauxComponent } from '@app/shared/components/reseaux/reseaux.component';
|
||||||
import {ReseauxComponent} from "@app/shared/components/reseaux/reseaux.component";
|
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||||
import {UntilDestroy} from "@ngneat/until-destroy";
|
import { ProjectListComponent } from '@app/shared/components/project-list/project-list.component';
|
||||||
import {ProjectListComponent} from "@app/shared/components/project-list/project-list.component";
|
import { environment } from '@env/environment';
|
||||||
import {environment} from "@env/environment";
|
import { Profile } from '@app/domain/profiles/profile.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-profile-detail',
|
selector: 'app-profile-detail',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
QRCodeModule,
|
QRCodeModule,
|
||||||
ChipsComponent,
|
ChipsComponent,
|
||||||
ReseauxComponent,
|
ReseauxComponent,
|
||||||
RouterLink,
|
RouterLink,
|
||||||
UpperCasePipe,
|
UpperCasePipe,
|
||||||
ProjectListComponent
|
ProjectListComponent,
|
||||||
],
|
],
|
||||||
templateUrl: './profile-detail.component.html',
|
templateUrl: './profile-detail.component.html',
|
||||||
styleUrl: './profile-detail.component.scss'
|
styleUrl: './profile-detail.component.scss',
|
||||||
})
|
})
|
||||||
@UntilDestroy()
|
@UntilDestroy()
|
||||||
export class ProfileDetailComponent {
|
export class ProfileDetailComponent {
|
||||||
|
protected readonly environment = environment;
|
||||||
protected readonly environment = environment;
|
private readonly route = inject(ActivatedRoute);
|
||||||
private readonly route = inject(ActivatedRoute);
|
|
||||||
|
protected extraData: { user: User; profile: Profile } = this.route.snapshot.data['profile'];
|
||||||
|
|
||||||
protected extraData: { user: User, profile: Profile } = this.route.snapshot.data['profile'];
|
protected user = computed(() => {
|
||||||
|
if (this.extraData != undefined) return this.extraData.user;
|
||||||
protected user = computed(() => {
|
return {} as User;
|
||||||
if (this.extraData != undefined) return this.extraData.user;
|
});
|
||||||
return {} as User;
|
|
||||||
});
|
protected profile = computed(() => {
|
||||||
|
if (this.extraData != undefined) return this.extraData.profile;
|
||||||
|
return {} as Profile;
|
||||||
protected profile = computed(() => {
|
});
|
||||||
if (this.extraData != undefined) return this.extraData.profile;
|
}
|
||||||
return {} as Profile;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,28 +1,19 @@
|
|||||||
<section class="pb-10 relative">
|
<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
|
||||||
<div class="flex-1">
|
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"
|
||||||
<app-search/>
|
>
|
||||||
</div>
|
<div class="flex-1">
|
||||||
<div class="">
|
<app-search />
|
||||||
<app-display-profile-card (onDisplayChange)="showNewDisplay($event)"/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="max-w-6xl mx-auto px-4">
|
||||||
|
@if (loading().isLoading) {
|
||||||
<div class="max-w-6xl mx-auto px-4">
|
<div class="flex justify-center items-center h-96">
|
||||||
|
<p>Chargement...</p>
|
||||||
@switch (display()) {
|
</div>
|
||||||
@case ('list'.toUpperCase()) {
|
} @else {
|
||||||
<app-horizental-profile-list [profiles]="profiles"/>
|
<app-vertical-profile-list [profiles]="profiles()" />
|
||||||
}
|
}
|
||||||
@case ('grid'.toUpperCase()) {
|
</div>
|
||||||
<app-vertical-profile-list [profiles]="profiles"/>
|
</section>
|
||||||
}
|
|
||||||
@default {
|
|
||||||
<app-vertical-profile-list [profiles]="profiles"/>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,39 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ProfileListComponent } from './profile-list.component';
|
import { ProfileListComponent } from './profile-list.component';
|
||||||
import {provideRouter} from "@angular/router";
|
import { provideRouter } from '@angular/router';
|
||||||
|
import { of } from 'rxjs';
|
||||||
describe('ProfileListComponent', () => {
|
import { ProfileRepository } from '@app/domain/profiles/profile.repository';
|
||||||
let component: ProfileListComponent;
|
import { PROFILE_REPOSITORY_TOKEN } from '@app/infrastructure/profiles/profile-repository.token';
|
||||||
let fixture: ComponentFixture<ProfileListComponent>;
|
import { Profile } from '@app/domain/profiles/profile.model';
|
||||||
|
|
||||||
beforeEach(async () => {
|
describe('ProfileListComponent', () => {
|
||||||
await TestBed.configureTestingModule({
|
let component: ProfileListComponent;
|
||||||
imports: [ProfileListComponent],
|
let fixture: ComponentFixture<ProfileListComponent>;
|
||||||
providers:[
|
let mockProfileRepository: jest.Mocked<ProfileRepository>;
|
||||||
provideRouter([])
|
|
||||||
]
|
beforeEach(async () => {
|
||||||
})
|
mockProfileRepository = {
|
||||||
.compileComponents();
|
create: jest.fn().mockReturnValue(of({} as Profile)),
|
||||||
|
list: jest.fn().mockReturnValue(of([])),
|
||||||
fixture = TestBed.createComponent(ProfileListComponent);
|
getByUserId: jest.fn().mockReturnValue(of({} as Profile)),
|
||||||
component = fixture.componentInstance;
|
update: jest.fn().mockReturnValue(of({} as Profile)),
|
||||||
fixture.detectChanges();
|
};
|
||||||
});
|
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
it('should create', () => {
|
imports: [ProfileListComponent],
|
||||||
expect(component).toBeTruthy();
|
providers: [
|
||||||
});
|
provideRouter([]),
|
||||||
});
|
{ provide: PROFILE_REPOSITORY_TOKEN, useValue: mockProfileRepository },
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ProfileListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,46 +1,24 @@
|
|||||||
import {Component, inject, signal} from '@angular/core';
|
import { Component, inject, OnInit } from '@angular/core';
|
||||||
import {ActivatedRoute} from "@angular/router";
|
import { SearchComponent } from '@app/shared/features/search/search.component';
|
||||||
import {SearchComponent} from "@app/shared/features/search/search.component";
|
import { VerticalProfileListComponent } from '@app/shared/components/vertical-profile-list/vertical-profile-list.component';
|
||||||
import {
|
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||||
HorizentalProfileItemComponent
|
import { ProfileFacade } from '@app/ui/profiles/profile.facade';
|
||||||
} from "@app/shared/components/horizental-profile-item/horizental-profile-item.component";
|
|
||||||
import {
|
@Component({
|
||||||
VerticalProfileItemComponent
|
selector: 'app-profile-list',
|
||||||
} from "@app/shared/components/vertical-profile-item/vertical-profile-item.component";
|
standalone: true,
|
||||||
import {DisplayProfileCardComponent} from "@app/shared/features/display-profile-card/display-profile-card.component";
|
imports: [SearchComponent, VerticalProfileListComponent],
|
||||||
import {JsonPipe} from "@angular/common";
|
templateUrl: './profile-list.component.html',
|
||||||
import {
|
styleUrl: './profile-list.component.scss',
|
||||||
HorizentalProfileListComponent
|
})
|
||||||
} from "@app/shared/components/horizental-profile-list/horizental-profile-list.component";
|
@UntilDestroy()
|
||||||
import {
|
export class ProfileListComponent implements OnInit {
|
||||||
VerticalProfileListComponent
|
private readonly facade = inject(ProfileFacade);
|
||||||
} from "@app/shared/components/vertical-profile-list/vertical-profile-list.component";
|
protected readonly profiles = this.facade.profiles;
|
||||||
import {UntilDestroy} from "@ngneat/until-destroy";
|
protected readonly loading = this.facade.loading;
|
||||||
import {Profile} from "@app/shared/models/profile";
|
protected readonly error = this.facade.error;
|
||||||
|
|
||||||
@Component({
|
ngOnInit() {
|
||||||
selector: 'app-profile-list',
|
this.facade.load();
|
||||||
standalone: true,
|
}
|
||||||
imports: [
|
}
|
||||||
SearchComponent,
|
|
||||||
HorizentalProfileItemComponent,
|
|
||||||
VerticalProfileItemComponent,
|
|
||||||
DisplayProfileCardComponent,
|
|
||||||
JsonPipe,
|
|
||||||
HorizentalProfileListComponent,
|
|
||||||
VerticalProfileListComponent
|
|
||||||
],
|
|
||||||
templateUrl: './profile-list.component.html',
|
|
||||||
styleUrl: './profile-list.component.scss'
|
|
||||||
})
|
|
||||||
@UntilDestroy()
|
|
||||||
export class ProfileListComponent {
|
|
||||||
|
|
||||||
private readonly route = inject(ActivatedRoute);
|
|
||||||
protected profiles : Profile[] = this.route.snapshot.data['profiles'] as Profile[];
|
|
||||||
protected display = signal<string>('grid'.toUpperCase());
|
|
||||||
|
|
||||||
showNewDisplay($event: string) {
|
|
||||||
this.display.set($event.toUpperCase())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,18 +1,27 @@
|
|||||||
import {NgModule} from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import {RouterModule, Routes} from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import {ProfileListComponent} from "@app/routes/profile/profile-list/profile-list.component";
|
import { ProfileListComponent } from '@app/routes/profile/profile-list/profile-list.component';
|
||||||
import {ProfileDetailComponent} from "@app/routes/profile/profile-detail/profile-detail.component";
|
import { ProfileDetailComponent } from '@app/routes/profile/profile-detail/profile-detail.component';
|
||||||
import {listResolver} from "@app/core/resolvers/profile/list/list.resolver";
|
import { listResolver } from '@app/core/resolvers/profile/list/list.resolver';
|
||||||
import {detailResolver} from "@app/core/resolvers/profile/detail/detail.resolver";
|
import { detailResolver } from '@app/core/resolvers/profile/detail/detail.resolver';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: '', component: ProfileListComponent, title: 'Liste des profiles', resolve: {profiles: listResolver}},
|
{
|
||||||
{path: ':name', component: ProfileDetailComponent, title: 'Detail du profile', resolve: {profile: detailResolver}},
|
path: '',
|
||||||
];
|
component: ProfileListComponent,
|
||||||
|
title: 'Liste des profiles',
|
||||||
@NgModule({
|
resolve: { profiles: listResolver },
|
||||||
imports: [RouterModule.forChild(routes)],
|
},
|
||||||
exports: [RouterModule]
|
{
|
||||||
})
|
path: ':name',
|
||||||
export class ProfileRoutingModule {
|
component: ProfileDetailComponent,
|
||||||
}
|
title: 'Detail du profile',
|
||||||
|
resolve: { profile: detailResolver },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class ProfileRoutingModule {}
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { ProfileRoutingModule } from './profile-routing.module';
|
import { ProfileRoutingModule } from './profile-routing.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
@NgModule({
|
declarations: [],
|
||||||
declarations: [],
|
imports: [CommonModule, ProfileRoutingModule],
|
||||||
imports: [
|
})
|
||||||
CommonModule,
|
export class ProfileModule {}
|
||||||
ProfileRoutingModule
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class ProfileModule { }
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
@if (sector != undefined) {
|
@if (sector() != undefined) {
|
||||||
<div class="flex flex-wrap space-x-2 items-center space-y-1">
|
<div class="flex flex-wrap space-x-2 items-center space-y-1">
|
||||||
@for (chip of sector.nom.split('-'); track chip) {
|
@for (chip of sector().noms; track chip) {
|
||||||
<small
|
<small
|
||||||
class="rounded-full bg-indigo-400 hover:bg-indigo-700 text-xs text-white py-1.5 px-3 font-semibold ">{{ chip | titlecase }}</small>
|
class="rounded-full bg-indigo-400 hover:bg-indigo-700 text-xs text-white py-1.5 px-3 font-semibold"
|
||||||
}
|
>{{ chip | titlecase }}</small
|
||||||
</div>
|
>
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,23 +1,38 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ChipsComponent } from './chips.component';
|
import { ChipsComponent } from './chips.component';
|
||||||
|
import { provideRouter } from '@angular/router';
|
||||||
describe('ChipsComponent', () => {
|
import { SECTOR_REPOSITORY_TOKEN } from '@app/infrastructure/sectors/sector-repository.token';
|
||||||
let component: ChipsComponent;
|
import { of } from 'rxjs';
|
||||||
let fixture: ComponentFixture<ChipsComponent>;
|
import { Sector } from '@app/domain/sectors/sector.model';
|
||||||
|
import { SectorRepository } from '@app/domain/sectors/sector.repository';
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
describe('ChipsComponent', () => {
|
||||||
imports: [ChipsComponent]
|
let component: ChipsComponent;
|
||||||
})
|
let fixture: ComponentFixture<ChipsComponent>;
|
||||||
.compileComponents();
|
|
||||||
|
let mockSectorRepo: SectorRepository;
|
||||||
fixture = TestBed.createComponent(ChipsComponent);
|
|
||||||
component = fixture.componentInstance;
|
beforeEach(async () => {
|
||||||
fixture.detectChanges();
|
mockSectorRepo = {
|
||||||
});
|
list: jest.fn(),
|
||||||
|
getOne: jest.fn().mockReturnValue(of({} as Sector)),
|
||||||
it('should create', () => {
|
};
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
await TestBed.configureTestingModule({
|
||||||
});
|
imports: [ChipsComponent],
|
||||||
|
providers: [
|
||||||
|
provideRouter([]),
|
||||||
|
{ provide: SECTOR_REPOSITORY_TOKEN, useValue: mockSectorRepo },
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ChipsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,30 +1,25 @@
|
|||||||
import {Component, inject, Input, OnInit} from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import {Sector} from "@app/shared/models/sector";
|
import { TitleCasePipe } from '@angular/common';
|
||||||
import {TitleCasePipe} from "@angular/common";
|
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||||
import {SectorService} from "@app/core/services/sector/sector.service";
|
import { SectorFacade } from '@app/ui/sectors/sector.facade';
|
||||||
import {UntilDestroy} from "@ngneat/until-destroy";
|
|
||||||
|
@Component({
|
||||||
@Component({
|
selector: 'app-chips',
|
||||||
selector: 'app-chips',
|
standalone: true,
|
||||||
standalone: true,
|
imports: [TitleCasePipe],
|
||||||
imports: [
|
templateUrl: './chips.component.html',
|
||||||
TitleCasePipe
|
styleUrl: './chips.component.scss',
|
||||||
],
|
})
|
||||||
templateUrl: './chips.component.html',
|
@UntilDestroy()
|
||||||
styleUrl: './chips.component.scss'
|
export class ChipsComponent implements OnInit {
|
||||||
})
|
@Input({ required: true }) sectorId: string | null = null;
|
||||||
@UntilDestroy()
|
|
||||||
export class ChipsComponent implements OnInit {
|
private readonly sectorFacade = new SectorFacade();
|
||||||
@Input({required: true}) sectorId: string | null = null;
|
protected sector = this.sectorFacade.sector;
|
||||||
|
|
||||||
protected sectorService = inject(SectorService);
|
ngOnInit(): void {
|
||||||
|
if (this.sectorId) {
|
||||||
protected sector: Sector | undefined = undefined;
|
this.sectorFacade.loadOne(this.sectorId);
|
||||||
|
}
|
||||||
ngOnInit(): void {
|
}
|
||||||
if (this.sectorId)
|
}
|
||||||
this.sectorService.getSectorById(this.sectorId).subscribe(value => this.sector = value)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,78 +1,86 @@
|
|||||||
<footer class=" py-4 w-full min-h-full bg-white dark:bg-gray-900 ">
|
<footer class="py-4 w-full min-h-full bg-white dark:bg-gray-900">
|
||||||
<div class="max-w-[80rem] mx-auto space-y-6 px-2 md:px-4 lg:px-6 pt-4">
|
<div class="max-w-[80rem] mx-auto space-y-6 px-2 md:px-4 lg:px-6 pt-4">
|
||||||
<div class="h-px w-full bg-gray-900/10 bg-gray-800 dark:bg-white"></div>
|
<div class="h-px w-full bg-gray-900/10 bg-gray-800 dark:bg-white"></div>
|
||||||
<div
|
<div
|
||||||
class="flex flex-col sm:flex-row justify-between items-center text-sm text-gray-600 space-y-4 sm:space-y-0 ">
|
class="flex flex-col sm:flex-row justify-between items-center text-sm text-gray-600 space-y-4 sm:space-y-0"
|
||||||
<a
|
>
|
||||||
[routerLink]="['/']"
|
<a [routerLink]="['/']" class="inline-flex items-center gap-1">
|
||||||
class="inline-flex items-center gap-1">
|
<span class="inline-block text-xl text-gray-800 dark:text-white">TrouveTonProfile</span>
|
||||||
<span class="inline-block text-xl text-gray-800 dark:text-white">TrouveTonProfile</span>
|
</a>
|
||||||
</a>
|
|
||||||
|
<ul class="flex items-center gap-4">
|
||||||
<ul class="flex items-center gap-4">
|
<li>
|
||||||
<li>
|
<a [routerLink]="['/conditions']" class="inline-block text-gray-800 dark:text-white"
|
||||||
<a [routerLink]="['/conditions']" class="inline-block text-gray-800 dark:text-white">Conditions</a>
|
>Conditions</a
|
||||||
</li>
|
>
|
||||||
<li>
|
</li>
|
||||||
<a [routerLink]="['/terms']" class="inline-block text-gray-800 dark:text-white">Terms</a>
|
<li>
|
||||||
</li>
|
<a [routerLink]="['/terms']" class="inline-block text-gray-800 dark:text-white">Terms</a>
|
||||||
<li>
|
</li>
|
||||||
<a [routerLink]="['/politiques']" class="inline-block text-gray-800 dark:text-white">Politiques</a>
|
<li>
|
||||||
</li>
|
<a [routerLink]="['/politiques']" class="inline-block text-gray-800 dark:text-white"
|
||||||
</ul>
|
>Politiques</a
|
||||||
|
>
|
||||||
<ul class="flex items-center gap-4">
|
</li>
|
||||||
<li>
|
</ul>
|
||||||
<a
|
|
||||||
target="_blank"
|
<ul class="flex items-center gap-4">
|
||||||
href="#"
|
<li>
|
||||||
class="inline-block rounded-full h-8 w-8 p-2 border text-gray-800 dark:text-white">
|
<a
|
||||||
<svg
|
target="_blank"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
href="#"
|
||||||
class="w-full h-full fill-current"
|
class="inline-block rounded-full h-8 w-8 p-2 border text-gray-800 dark:text-white"
|
||||||
viewBox="0 0 448 512">
|
>
|
||||||
<path
|
<svg
|
||||||
d="M100.28 448H7.4V148.9h92.88zM53.79 108.1C24.09 108.1 0 83.5 0 53.8a53.79 53.79 0 0 1 107.58 0c0 29.7-24.1 54.3-53.79 54.3zM447.9 448h-92.68V302.4c0-34.7-.7-79.2-48.29-79.2-48.29 0-55.69 37.7-55.69 76.7V448h-92.78V148.9h89.08v40.8h1.3c12.4-23.5 42.69-48.3 87.88-48.3 94 0 111.28 61.9 111.28 142.3V448z"/>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
</svg>
|
class="w-full h-full fill-current"
|
||||||
</a>
|
viewBox="0 0 448 512"
|
||||||
</li>
|
>
|
||||||
<li>
|
<path
|
||||||
<a
|
d="M100.28 448H7.4V148.9h92.88zM53.79 108.1C24.09 108.1 0 83.5 0 53.8a53.79 53.79 0 0 1 107.58 0c0 29.7-24.1 54.3-53.79 54.3zM447.9 448h-92.68V302.4c0-34.7-.7-79.2-48.29-79.2-48.29 0-55.69 37.7-55.69 76.7V448h-92.78V148.9h89.08v40.8h1.3c12.4-23.5 42.69-48.3 87.88-48.3 94 0 111.28 61.9 111.28 142.3V448z"
|
||||||
target="_blank"
|
/>
|
||||||
href="#"
|
</svg>
|
||||||
class="inline-block rounded-full h-8 w-8 p-2 border text-gray-800 dark:text-white">
|
</a>
|
||||||
<svg
|
</li>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<li>
|
||||||
class="w-full h-full fill-current"
|
<a
|
||||||
viewBox="0 0 24 24">
|
target="_blank"
|
||||||
<g
|
href="#"
|
||||||
fill="none"
|
class="inline-block rounded-full h-8 w-8 p-2 border text-gray-800 dark:text-white"
|
||||||
stroke-linecap="round"
|
>
|
||||||
stroke-linejoin="round"
|
<svg
|
||||||
stroke-width="2">
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<path d="M0 0h24v24H0z"/>
|
class="w-full h-full fill-current"
|
||||||
<path
|
viewBox="0 0 24 24"
|
||||||
fill="currentColor"
|
>
|
||||||
d="M18 2a1 1 0 0 1 .993.883L19 3v4a1 1 0 0 1-.883.993L18 8h-3v1h3a1 1 0 0 1 .991 1.131l-.02.112l-1 4a1 1 0 0 1-.858.75L17 15h-2v6a1 1 0 0 1-.883.993L14 22h-4a1 1 0 0 1-.993-.883L9 21v-6H7a1 1 0 0 1-.993-.883L6 14v-4a1 1 0 0 1 .883-.993L7 9h2V8a6 6 0 0 1 5.775-5.996L15 2z"/>
|
<g fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||||
</g>
|
<path d="M0 0h24v24H0z" />
|
||||||
</svg>
|
<path
|
||||||
</a>
|
fill="currentColor"
|
||||||
</li>
|
d="M18 2a1 1 0 0 1 .993.883L19 3v4a1 1 0 0 1-.883.993L18 8h-3v1h3a1 1 0 0 1 .991 1.131l-.02.112l-1 4a1 1 0 0 1-.858.75L17 15h-2v6a1 1 0 0 1-.883.993L14 22h-4a1 1 0 0 1-.993-.883L9 21v-6H7a1 1 0 0 1-.993-.883L6 14v-4a1 1 0 0 1 .883-.993L7 9h2V8a6 6 0 0 1 5.775-5.996L15 2z"
|
||||||
<li>
|
/>
|
||||||
<a
|
</g>
|
||||||
target="_blank"
|
</svg>
|
||||||
href="#"
|
</a>
|
||||||
class="inline-block rounded-full h-8 w-8 p-2 border text-gray-800 dark:text-white">
|
</li>
|
||||||
<svg
|
<li>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<a
|
||||||
class="w-full h-full fill-current"
|
target="_blank"
|
||||||
viewBox="0 0 512 512">
|
href="#"
|
||||||
<path
|
class="inline-block rounded-full h-8 w-8 p-2 border text-gray-800 dark:text-white"
|
||||||
d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"/>
|
>
|
||||||
</svg>
|
<svg
|
||||||
</a>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
</li>
|
class="w-full h-full fill-current"
|
||||||
</ul>
|
viewBox="0 0 512 512"
|
||||||
</div>
|
>
|
||||||
</div>
|
<path
|
||||||
</footer>
|
d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|||||||
@@ -1,27 +1,24 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { provideRouter } from '@angular/router';
|
import { provideRouter } from '@angular/router';
|
||||||
|
|
||||||
import { FooterComponent } from './footer.component';
|
import { FooterComponent } from './footer.component';
|
||||||
|
|
||||||
describe('FooterComponent', () => {
|
describe('FooterComponent', () => {
|
||||||
let component: FooterComponent;
|
let component: FooterComponent;
|
||||||
let fixture: ComponentFixture<FooterComponent>;
|
let fixture: ComponentFixture<FooterComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [FooterComponent],
|
imports: [FooterComponent],
|
||||||
providers:[
|
providers: [provideRouter([])],
|
||||||
provideRouter([]),
|
}).compileComponents();
|
||||||
]
|
|
||||||
})
|
fixture = TestBed.createComponent(FooterComponent);
|
||||||
.compileComponents();
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
fixture = TestBed.createComponent(FooterComponent);
|
});
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
it('should create', () => {
|
||||||
});
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
it('should create', () => {
|
});
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import {RouterLink} from "@angular/router";
|
import { RouterLink } from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-footer',
|
selector: 'app-footer',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [RouterLink],
|
||||||
RouterLink
|
templateUrl: './footer.component.html',
|
||||||
],
|
styleUrl: './footer.component.scss',
|
||||||
templateUrl: './footer.component.html',
|
})
|
||||||
styleUrl: './footer.component.scss'
|
export class FooterComponent {}
|
||||||
})
|
|
||||||
export class FooterComponent {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
@if (user != undefined) {
|
|
||||||
<a [routerLink]="[user.username?user.username:user.id]" [state]="{user,profile}" class="cursor-pointer">
|
|
||||||
<div class="items-center bg-gray-50 rounded-lg shadow sm:flex dark:bg-gray-800 dark:border-gray-700 cursor-pointer">
|
|
||||||
|
|
||||||
<div class="sm:w-max w-full flex items-center justify-center ">
|
|
||||||
@if (user.avatar) {
|
|
||||||
<img class="max-w-xl rounded-lg max-h-64 object-cover sm:rounded-none sm:rounded-l-lg"
|
|
||||||
src="{{environment.baseUrl}}/api/files/users/{{user.id}}/{{user.avatar}}" alt="{{user.username}}"
|
|
||||||
loading="lazy">
|
|
||||||
} @else {
|
|
||||||
<img class="max-w-xl rounded-lg max-h-64 sm:rounded-none sm:rounded-l-lg"
|
|
||||||
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{user.username}}" alt="{{user.username}}"
|
|
||||||
loading="lazy">
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="p-5 flex flex-col items-center space-y-2">
|
|
||||||
@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.name) {
|
|
||||||
<h3 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white">{{ user.name }}</h3>
|
|
||||||
} @else if (user.username) {
|
|
||||||
<h3 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white">{{ user.username }}</h3>
|
|
||||||
} @else {
|
|
||||||
<h3 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white">Non mentionné</h3>
|
|
||||||
}
|
|
||||||
<span class="text-gray-500 dark:text-gray-400">{{ profile.profession }}</span>
|
|
||||||
|
|
||||||
<app-chips [sectorId]="profile.secteur"/>
|
|
||||||
|
|
||||||
<app-reseaux [reseaux]="profile.reseaux"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</a>
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { HorizentalProfileItemComponent } from './horizental-profile-item.component';
|
|
||||||
|
|
||||||
describe('HorizentalProfileItemComponent', () => {
|
|
||||||
let component: HorizentalProfileItemComponent;
|
|
||||||
let fixture: ComponentFixture<HorizentalProfileItemComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [HorizentalProfileItemComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(HorizentalProfileItemComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import {Component, inject, Input, OnInit} from '@angular/core';
|
|
||||||
import {Router, RouterLink} from "@angular/router";
|
|
||||||
import {Profile} from "@app/shared/models/profile";
|
|
||||||
import {UserService} from "@app/core/services/user/user.service";
|
|
||||||
import {User} from "@app/shared/models/user";
|
|
||||||
import {ChipsComponent} from "@app/shared/components/chips/chips.component";
|
|
||||||
import {ReseauxComponent} from "@app/shared/components/reseaux/reseaux.component";
|
|
||||||
import {environment} from "@env/environment";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-horizental-profile-item',
|
|
||||||
standalone: true,
|
|
||||||
imports: [
|
|
||||||
RouterLink,
|
|
||||||
ChipsComponent,
|
|
||||||
ReseauxComponent
|
|
||||||
],
|
|
||||||
templateUrl: './horizental-profile-item.component.html',
|
|
||||||
styleUrl: './horizental-profile-item.component.scss'
|
|
||||||
})
|
|
||||||
export class HorizentalProfileItemComponent implements OnInit {
|
|
||||||
|
|
||||||
@Input({required: true}) profile: Profile = {} as Profile;
|
|
||||||
protected router = inject(Router)
|
|
||||||
protected userService = inject(UserService);
|
|
||||||
|
|
||||||
protected user: User | undefined = undefined;
|
|
||||||
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.userService.getUserById(this.profile.utilisateur).subscribe(
|
|
||||||
value => this.user = value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected readonly environment = environment;
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<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 mb-6 lg:mb-16 md:grid-cols-2">
|
|
||||||
|
|
||||||
@for (profile of profiles; track profile.id) {
|
|
||||||
<app-horizental-profile-item [profile]="profile"/>
|
|
||||||
} @empty {
|
|
||||||
<p>Aucun profile trouvée</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { HorizentalProfileListComponent } from './horizental-profile-list.component';
|
|
||||||
|
|
||||||
describe('HorizentalProfileListComponent', () => {
|
|
||||||
let component: HorizentalProfileListComponent;
|
|
||||||
let fixture: ComponentFixture<HorizentalProfileListComponent>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await TestBed.configureTestingModule({
|
|
||||||
imports: [HorizentalProfileListComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(HorizentalProfileListComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import {Component, Input} from '@angular/core';
|
|
||||||
import {
|
|
||||||
HorizentalProfileItemComponent
|
|
||||||
} from "@app/shared/components/horizental-profile-item/horizental-profile-item.component";
|
|
||||||
import {Profile} from "@app/shared/models/profile";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-horizental-profile-list',
|
|
||||||
standalone: true,
|
|
||||||
imports: [
|
|
||||||
HorizentalProfileItemComponent
|
|
||||||
],
|
|
||||||
templateUrl: './horizental-profile-list.component.html',
|
|
||||||
styleUrl: './horizental-profile-list.component.scss'
|
|
||||||
})
|
|
||||||
export class HorizentalProfileListComponent {
|
|
||||||
@Input({required:true}) profiles: Profile[] = []
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
@if (profile != undefined) {
|
@if (profile != undefined) {
|
||||||
<h2 class="text-3xl font-extrabold text-black dark:text-white">{{ profile.profession | uppercase }}</h2>
|
<h2 class="text-3xl font-extrabold text-black dark:text-white">
|
||||||
<p class="leading-relaxed text-lg mb-4 dark:text-white">{{ profile.apropos }}</p>
|
{{ profile.profession | uppercase }}
|
||||||
}
|
</h2>
|
||||||
|
<p class="leading-relaxed text-lg mb-4 dark:text-white">{{ profile.apropos }}</p>
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { MyHomeProfileComponent } from './my-home-profile.component';
|
import { MyHomeProfileComponent } from './my-home-profile.component';
|
||||||
|
|
||||||
describe('MyHomeProfileComponent', () => {
|
describe('MyHomeProfileComponent', () => {
|
||||||
let component: MyHomeProfileComponent;
|
let component: MyHomeProfileComponent;
|
||||||
let fixture: ComponentFixture<MyHomeProfileComponent>;
|
let fixture: ComponentFixture<MyHomeProfileComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [MyHomeProfileComponent]
|
imports: [MyHomeProfileComponent],
|
||||||
})
|
}).compileComponents();
|
||||||
.compileComponents();
|
|
||||||
|
fixture = TestBed.createComponent(MyHomeProfileComponent);
|
||||||
fixture = TestBed.createComponent(MyHomeProfileComponent);
|
component = fixture.componentInstance;
|
||||||
component = fixture.componentInstance;
|
fixture.detectChanges();
|
||||||
fixture.detectChanges();
|
|
||||||
|
await fixture.whenStable();
|
||||||
await fixture.whenStable();
|
});
|
||||||
});
|
|
||||||
|
it('should create', () => {
|
||||||
it('should create', () => {
|
expect(component).toBeTruthy();
|
||||||
expect(component).toBeTruthy();
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user