Compare commits
32 commits
main
...
front-admi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d797abe90a | ||
|
|
7b7f934245 | ||
|
|
d8063a02dd | ||
|
|
ab54fdb490 | ||
|
|
925dc1c178 | ||
|
|
8c1d9cd4c7 | ||
|
|
b883bd5156 | ||
|
|
520214977e | ||
|
|
0a891e500e | ||
|
|
c55af87a23 | ||
|
|
b9c3e7d0f3 | ||
|
|
958b4898f0 | ||
|
|
03d2a28544 | ||
|
|
f1d55dfc6b | ||
|
|
65cb3ee020 | ||
|
|
0bb795cc11 | ||
| bf6f102307 | |||
| 1c729b9c8d | |||
| f00762b5fe | |||
|
|
71e34ac8c0 | ||
|
|
5024b297ec | ||
|
|
d29741f9a8 | ||
|
|
0d6cca625c | ||
|
|
b7df19e1c0 | ||
|
|
f80fee841f | ||
|
|
00df25e8ff | ||
|
|
e51fabfe64 | ||
|
|
eea9f714d6 | ||
|
|
007391f124 | ||
|
|
006e58a922 | ||
|
|
427d837b35 | ||
|
|
d62915bfe5 |
17
.browserslistrc
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||||
|
# For additional information regarding the format and rule options, please see:
|
||||||
|
# https://github.com/browserslist/browserslist#queries
|
||||||
|
|
||||||
|
# For the full list of supported browsers by the Angular framework, please see:
|
||||||
|
# https://angular.io/guide/browser-support
|
||||||
|
|
||||||
|
# You can see what browsers were selected by your queries by running:
|
||||||
|
# npx browserslist
|
||||||
|
|
||||||
|
last 1 Chrome version
|
||||||
|
last 1 Firefox version
|
||||||
|
last 2 Edge major versions
|
||||||
|
last 2 Safari major versions
|
||||||
|
last 2 iOS major versions
|
||||||
|
Firefox ESR
|
||||||
|
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
|
||||||
16
.editorconfig
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Editor configuration, see https://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.ts]
|
||||||
|
quote_type = single
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
||||||
46
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# compiled output
|
||||||
|
/dist
|
||||||
|
/tmp
|
||||||
|
/out-tsc
|
||||||
|
# Only exists if Bazel was run
|
||||||
|
/bazel-out
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
# profiling files
|
||||||
|
chrome-profiler-events*.json
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
/.idea
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# IDE - VSCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.history/*
|
||||||
|
|
||||||
|
# misc
|
||||||
|
/.sass-cache
|
||||||
|
/connect.lock
|
||||||
|
/coverage
|
||||||
|
/libpeerconnection.log
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
testem.log
|
||||||
|
/typings
|
||||||
|
|
||||||
|
# System Files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
1
Procfile
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
web: npm start
|
||||||
42
README.md
|
|
@ -1,40 +1,28 @@
|
||||||
# PolyNotFound
|
# Admin
|
||||||
|
|
||||||
Le projet est séparé en 3 parties :
|
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.10.
|
||||||
|
|
||||||
- 2 Frontend :
|
## Development server
|
||||||
- 1 partie Administrateur
|
|
||||||
- 1 partie pour les utilisateurs et les publicitaires
|
|
||||||
- 1 Backend
|
|
||||||
|
|
||||||
Nous avons décidé que chaque partie du projet a sa propre branche git et non leur propre répertoire git.
|
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||||
|
|
||||||
En effet, les branches concernées par le projet est :
|
## Code scaffolding
|
||||||
- Frontend partie Administrateur : `front-admin`
|
|
||||||
- Frontend partie pour les utilisateurs et les publicitaires : `front-user-advertiser`
|
|
||||||
- Backend : `backend`
|
|
||||||
|
|
||||||
Nous pouvons récupérer une branche git avec `git checkout <nom_branche>`.
|
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||||
|
|
||||||
# Lancer le projet en Local
|
## Build
|
||||||
|
|
||||||
Dans un premier temps, il est obligatoire d'avoir NodeJS (version >12 au minimum), [téléchargeable sur cette page](https://nodejs.org/en/download/).
|
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||||
|
Then run `npm start`
|
||||||
|
|
||||||
Pour lancer le projet en local dans son ensemble, il faut impérativement avoir les branches concernées dans leur propre dossier.
|
## Running unit tests
|
||||||
Il faudra donc __clone__ le projet 3 fois.
|
|
||||||
|
|
||||||
Dans un dossier nommé par exemple `Polynotfound`:
|
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||||
- frontend-admin : `git clone --branch frontend-admin https://github.com/NyxiumYuuki/PolyNotFound.git polynotfound-frontend-admin`
|
|
||||||
- front-user-advertiser : `git clone --branch front-user-advertiser https://github.com/NyxiumYuuki/PolyNotFound.git polynotfound-front-user-advertiser`
|
|
||||||
- backend : `git clone --branch backend https://github.com/NyxiumYuuki/PolyNotFound.git polynotfound-backend`
|
|
||||||
|
|
||||||
Un README est disponible pour chaque branche pour lancer le projet en local soit en mode **production** soit en mode **développement**.
|
## Running end-to-end tests
|
||||||
|
|
||||||
# Lancer le projet en ligne avec Heroku
|
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
||||||
|
|
||||||
Nous avons déployé le projet en ligne avec Heroku.
|
## Further help
|
||||||
|
|
||||||
Le projet est disponible sur ces URL :
|
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||||
- Partie Utilisateurs et Publicitaires : https://polynotfound.herokuapp.com/
|
|
||||||
- Partie Administrateur : https://admin-polynotfound.herokuapp.com/
|
|
||||||
- API : https://api-polynotfound.herokuapp.com/
|
|
||||||
|
|
|
||||||
113
angular.json
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"version": 1,
|
||||||
|
"newProjectRoot": "projects",
|
||||||
|
"projects": {
|
||||||
|
"admin": {
|
||||||
|
"projectType": "application",
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"style": "scss"
|
||||||
|
},
|
||||||
|
"@schematics/angular:application": {
|
||||||
|
"strict": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"prefix": "app",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/frontend-admin",
|
||||||
|
"index": "src/index.html",
|
||||||
|
"main": "src/main.ts",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "tsconfig.app.json",
|
||||||
|
"inlineStyleLanguage": "scss",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||||
|
"src/styles.scss",
|
||||||
|
"node_modules/bootstrap/scss/bootstrap.scss"
|
||||||
|
],
|
||||||
|
"scripts": []
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "1mb",
|
||||||
|
"maximumError": "2mb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "anyComponentStyle",
|
||||||
|
"maximumWarning": "2kb",
|
||||||
|
"maximumError": "4kb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "src/environments/environment.ts",
|
||||||
|
"with": "src/environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputHashing": "all"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"buildOptimizer": false,
|
||||||
|
"optimization": false,
|
||||||
|
"vendorChunk": true,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"namedChunks": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "production"
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"browserTarget": "admin:build:production"
|
||||||
|
},
|
||||||
|
"development": {
|
||||||
|
"browserTarget": "admin:build:development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultConfiguration": "development"
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "admin:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular-devkit/build-angular:karma",
|
||||||
|
"options": {
|
||||||
|
"main": "src/test.ts",
|
||||||
|
"polyfills": "src/polyfills.ts",
|
||||||
|
"tsConfig": "tsconfig.spec.json",
|
||||||
|
"karmaConfig": "karma.conf.js",
|
||||||
|
"inlineStyleLanguage": "scss",
|
||||||
|
"assets": [
|
||||||
|
"src/favicon.ico",
|
||||||
|
"src/assets"
|
||||||
|
],
|
||||||
|
"styles": [
|
||||||
|
"src/styles.scss"
|
||||||
|
],
|
||||||
|
"scripts": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultProject": "admin"
|
||||||
|
}
|
||||||
37
e2e/protractor.conf.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
// @ts-check
|
||||||
|
// Protractor configuration file, see link for more information
|
||||||
|
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||||
|
|
||||||
|
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type { import("protractor").Config }
|
||||||
|
*/
|
||||||
|
exports.config = {
|
||||||
|
allScriptsTimeout: 11000,
|
||||||
|
specs: [
|
||||||
|
'./src/**/*.e2e-spec.ts'
|
||||||
|
],
|
||||||
|
capabilities: {
|
||||||
|
browserName: 'chrome'
|
||||||
|
},
|
||||||
|
directConnect: true,
|
||||||
|
SELENIUM_PROMISE_MANAGER: false,
|
||||||
|
baseUrl: 'http://localhost:4200/',
|
||||||
|
framework: 'jasmine',
|
||||||
|
jasmineNodeOpts: {
|
||||||
|
showColors: true,
|
||||||
|
defaultTimeoutInterval: 30000,
|
||||||
|
print: function() {}
|
||||||
|
},
|
||||||
|
onPrepare() {
|
||||||
|
require('ts-node').register({
|
||||||
|
project: require('path').join(__dirname, './tsconfig.json')
|
||||||
|
});
|
||||||
|
jasmine.getEnv().addReporter(new SpecReporter({
|
||||||
|
spec: {
|
||||||
|
displayStacktrace: StacktraceOption.PRETTY
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
23
e2e/src/app.e2e-spec.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { browser, logging } from 'protractor';
|
||||||
|
import { AppPage } from './app.po';
|
||||||
|
|
||||||
|
describe('workspace-project App', () => {
|
||||||
|
let page: AppPage;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
page = new AppPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display welcome message', async () => {
|
||||||
|
await page.navigateTo();
|
||||||
|
expect(await page.getTitleText()).toEqual('frontend app is running!');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
// Assert that there are no errors emitted from the browser
|
||||||
|
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||||
|
expect(logs).not.toContain(jasmine.objectContaining({
|
||||||
|
level: logging.Level.SEVERE,
|
||||||
|
} as logging.Entry));
|
||||||
|
});
|
||||||
|
});
|
||||||
11
e2e/src/app.po.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { browser, by, element } from 'protractor';
|
||||||
|
|
||||||
|
export class AppPage {
|
||||||
|
async navigateTo(): Promise<unknown> {
|
||||||
|
return browser.get(browser.baseUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTitleText(): Promise<string> {
|
||||||
|
return element(by.css('app-root .content span')).getText();
|
||||||
|
}
|
||||||
|
}
|
||||||
13
e2e/tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../out-tsc/e2e",
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es2018",
|
||||||
|
"types": [
|
||||||
|
"jasmine",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
44
karma.conf.js
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Karma configuration file, see link for more information
|
||||||
|
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||||
|
|
||||||
|
module.exports = function (config) {
|
||||||
|
config.set({
|
||||||
|
basePath: 'admin',
|
||||||
|
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||||
|
plugins: [
|
||||||
|
require('karma-jasmine'),
|
||||||
|
require('karma-chrome-launcher'),
|
||||||
|
require('karma-jasmine-html-reporter'),
|
||||||
|
require('karma-coverage'),
|
||||||
|
require('@angular-devkit/build-angular/plugins/karma')
|
||||||
|
],
|
||||||
|
client: {
|
||||||
|
jasmine: {
|
||||||
|
// you can add configuration options for Jasmine here
|
||||||
|
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||||
|
// for example, you can disable the random execution with `random: false`
|
||||||
|
// or set a specific seed with `seed: 4321`
|
||||||
|
},
|
||||||
|
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||||
|
},
|
||||||
|
jasmineHtmlReporter: {
|
||||||
|
suppressAll: true // removes the duplicated traces
|
||||||
|
},
|
||||||
|
coverageReporter: {
|
||||||
|
dir: require('path').join(__dirname, './coverage/admin'),
|
||||||
|
subdir: '.',
|
||||||
|
reporters: [
|
||||||
|
{ type: 'html' },
|
||||||
|
{ type: 'text-summary' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
reporters: ['progress', 'kjhtml'],
|
||||||
|
port: 9876,
|
||||||
|
colors: true,
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
autoWatch: true,
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
singleRun: false,
|
||||||
|
restartOnFileChange: true
|
||||||
|
});
|
||||||
|
};
|
||||||
45
package.json
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"name": "frontend-admin",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"ng": "ng",
|
||||||
|
"start": "node server.js",
|
||||||
|
"build": "ng build",
|
||||||
|
"watch": "ng build --watch --configuration development",
|
||||||
|
"test": "ng test"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/animations": "~12.2.0",
|
||||||
|
"@angular/cdk": "^13.1.1",
|
||||||
|
"@angular/common": "~12.2.0",
|
||||||
|
"@angular/compiler": "~12.2.0",
|
||||||
|
"@angular/core": "~12.2.0",
|
||||||
|
"@angular/forms": "~12.2.0",
|
||||||
|
"@angular/material": "^13.1.1",
|
||||||
|
"@angular/platform-browser": "~12.2.0",
|
||||||
|
"@angular/platform-browser-dynamic": "~12.2.0",
|
||||||
|
"@angular/router": "~12.2.0",
|
||||||
|
"bootstrap": "^5.1.3",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"jquery": "^3.6.0",
|
||||||
|
"popper": "^1.0.1",
|
||||||
|
"rxjs": "~6.6.0",
|
||||||
|
"tslib": "^2.3.0",
|
||||||
|
"zone.js": "~0.11.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@angular-devkit/build-angular": "~12.2.10",
|
||||||
|
"@angular/cli": "~12.2.10",
|
||||||
|
"@angular/compiler-cli": "~12.2.0",
|
||||||
|
"@types/jasmine": "~3.8.0",
|
||||||
|
"@types/node": "^12.11.1",
|
||||||
|
"jasmine-core": "~3.8.0",
|
||||||
|
"karma": "~6.3.0",
|
||||||
|
"karma-chrome-launcher": "~3.1.0",
|
||||||
|
"karma-coverage": "~2.0.3",
|
||||||
|
"karma-jasmine": "~4.0.0",
|
||||||
|
"karma-jasmine-html-reporter": "~1.7.0",
|
||||||
|
"typescript": "~4.3.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
server.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
const path = require('path');
|
||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
const port = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
app.use(express.static(__dirname + '/dist/frontend-admin'));
|
||||||
|
app.get('/*', function(req,res) {
|
||||||
|
res.sendFile(path.join(__dirname+ '/dist/frontend-admin/index.html'));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(port, '0.0.0.0',() => {
|
||||||
|
console.log (`listening on port ${port}`);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
<div [class]="themeService.getClassTheme()">
|
||||||
|
<div class="myContainer">
|
||||||
|
|
||||||
|
<!-- Navbar -->
|
||||||
|
<app-navbar-admin></app-navbar-admin><br><br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- filtre -->
|
||||||
|
<div class="filtersContainer mat-elevation-z8">
|
||||||
|
|
||||||
|
<!-- titre -->
|
||||||
|
<div style="font-weight: bold; margin-bottom: 10px;">
|
||||||
|
Filtre
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<!-- filtre textuelle-->
|
||||||
|
<div style="margin: 10px 0px 20px 2%;">
|
||||||
|
<input class="textFilter" [(ngModel)]="filteredText" (ngModelChange)="onFilter()" placeholder="Rechercher par mots-clés...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- visible + interests + période -->
|
||||||
|
<div class="row myRow">
|
||||||
|
|
||||||
|
<!-- visible -->
|
||||||
|
<div class="col-2">
|
||||||
|
<mat-checkbox [(ngModel)]="visible" (change)="onFilter()">visible</mat-checkbox><br>
|
||||||
|
<mat-checkbox [(ngModel)]="noVisible" (change)="onFilter()">non visible</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- subjects -->
|
||||||
|
<div class="col-4">
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Sujets</mat-label>
|
||||||
|
<mat-select [formControl]="formControlInterests" multiple>
|
||||||
|
<mat-select-trigger>
|
||||||
|
{{formControlInterests.value ? formControlInterests.value[0] : ''}}
|
||||||
|
<span *ngIf="formControlInterests.value?.length > 1">
|
||||||
|
(+{{formControlInterests.value.length - 1}} {{formControlInterests.value?.length === 2 ? 'autre' : 'autres'}})
|
||||||
|
</span>
|
||||||
|
</mat-select-trigger>
|
||||||
|
<mat-option *ngFor="let topping of allInterests" [value]="topping">{{topping}}</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-icon-button (click)="onFilter()">
|
||||||
|
<mat-icon>keyboard_tab</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- période -->
|
||||||
|
<div class="col-6" style="text-align: right; font-size: small;">
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Période de date de création</mat-label>
|
||||||
|
<mat-date-range-input
|
||||||
|
[formGroup]="campaignOne"
|
||||||
|
[rangePicker]="campaignOnePicker">
|
||||||
|
<input matStartDate placeholder="Start date" formControlName="start" style="font-size: small;">
|
||||||
|
<input matEndDate placeholder="End date" formControlName="end" style="font-size: small;">
|
||||||
|
</mat-date-range-input>
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="campaignOnePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-date-range-picker #campaignOnePicker></mat-date-range-picker>
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-icon-button (click)="onFilter()">
|
||||||
|
<mat-icon>keyboard_tab</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button (click)="onEffacerDate()">
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- table -->
|
||||||
|
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
|
||||||
|
|
||||||
|
<!-- Title Column -->
|
||||||
|
<ng-container matColumnDef="title">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Titre </th>
|
||||||
|
<td mat-cell *matCellDef="let advert">
|
||||||
|
{{advert.title}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- company Column -->
|
||||||
|
<ng-container matColumnDef="company">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Entreprise </th>
|
||||||
|
<td mat-cell *matCellDef="let advert">
|
||||||
|
{{advert.company}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- email Column -->
|
||||||
|
<ng-container matColumnDef="email">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Email </th>
|
||||||
|
<td mat-cell *matCellDef="let advert">
|
||||||
|
{{advert.email}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Tags Column -->
|
||||||
|
<ng-container matColumnDef="interests">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Sujets </th>
|
||||||
|
<td mat-cell *matCellDef="let advert">
|
||||||
|
<span *ngFor="let objectInterest of advert.interests; let isLast = last;">
|
||||||
|
<span *ngIf="!isLast"> {{objectInterest.interest}}, </span>
|
||||||
|
<span *ngIf="isLast"> {{objectInterest.interest}} </span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- CreatedAt Column -->
|
||||||
|
<ng-container matColumnDef="createdAt">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Date de création </th>
|
||||||
|
<td mat-cell *matCellDef="let advert">
|
||||||
|
{{ advert.createdAt | date:'dd/LL/YYYY à HH:mm:ss' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- UpdatedAt Column -->
|
||||||
|
<ng-container matColumnDef="updatedAt">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Dernière modification </th>
|
||||||
|
<td mat-cell *matCellDef="let advert">
|
||||||
|
{{ advert.updatedAt | date:'dd/LL/YYYY à HH:mm:ss' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- CountViews Column -->
|
||||||
|
<ng-container matColumnDef="countViews">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Vues </th>
|
||||||
|
<td mat-cell *matCellDef="let advert">
|
||||||
|
{{advert.countViews}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- IsVisible Column -->
|
||||||
|
<ng-container matColumnDef="isVisible">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Visible </th>
|
||||||
|
<td mat-cell *matCellDef="let advert">
|
||||||
|
<span *ngIf="advert.isVisible"> <mat-icon>check</mat-icon> </span>
|
||||||
|
<span *ngIf="!advert.isVisible"></span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Actions Column -->
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let advert">
|
||||||
|
<button mat-icon-button (click)="onVisualizeImages(advert)">
|
||||||
|
<mat-icon> insert_photo</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button (click)="onDelete(advert)">
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Directives -->
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<div style="width: 94%; margin: auto auto">
|
||||||
|
<mat-paginator [pageSizeOptions]="[10, 20, 50, 100]" showFirstLastButtons aria-label="Select page of periodic elements"></mat-paginator>
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
.myContainer {
|
||||||
|
max-width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow-x: hidden;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
.filtersContainer {
|
||||||
|
width: 80%;
|
||||||
|
background-color: white;
|
||||||
|
padding: 10px 10px 10px 10px;
|
||||||
|
margin: 20px 3% 20px 3%
|
||||||
|
}
|
||||||
|
|
||||||
|
.myRow {
|
||||||
|
margin-left: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textFilter {
|
||||||
|
width: 50%;
|
||||||
|
font-size: medium;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
table {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 94%;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
.darkTheme table { border: solid 2px white; }
|
||||||
|
|
||||||
|
th.mat-sort-header-sorted {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
//width: 30%;
|
||||||
|
font-size: large;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// aura
|
||||||
|
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
|
||||||
|
background-color: grey !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// contenu coche
|
||||||
|
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||||
|
background-color: black !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// indeterminate
|
||||||
|
::ng-deep .mat-checkbox .mat-checkbox-frame {
|
||||||
|
border: solid 1px black !important;
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-pseudo-checkbox-checked {
|
||||||
|
background-color: black !important;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PageAdListAdminComponent } from './page-ad-list-admin.component';
|
||||||
|
|
||||||
|
describe('PageAdListAdminComponent', () => {
|
||||||
|
let component: PageAdListAdminComponent;
|
||||||
|
let fixture: ComponentFixture<PageAdListAdminComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ PageAdListAdminComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PageAdListAdminComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,286 @@
|
||||||
|
import {AfterViewInit, Component, ViewChild} from '@angular/core';
|
||||||
|
import {MatSort} from "@angular/material/sort";
|
||||||
|
import {MatPaginator} from "@angular/material/paginator";
|
||||||
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
|
import {MatTableDataSource} from "@angular/material/table";
|
||||||
|
import {PopupDeleteAdAdminComponent} from "../popup-delete-ad-admin/popup-delete-ad-admin.component";
|
||||||
|
import {PopupVisualizeImagesAdminComponent} from "../popup-visualize-images-admin/popup-visualize-images-admin.component";
|
||||||
|
import {FormControl, FormGroup} from "@angular/forms";
|
||||||
|
import {HttpParams} from "@angular/common/http";
|
||||||
|
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||||
|
import {MessageService} from "../../../utils/message/message.service";
|
||||||
|
import {DatePipe} from "@angular/common";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export interface AdvertWithCountViewsAndCompany {
|
||||||
|
id: string,
|
||||||
|
userId: string,
|
||||||
|
company: string,
|
||||||
|
email: string,
|
||||||
|
title: string,
|
||||||
|
url: string,
|
||||||
|
images: {
|
||||||
|
url: string,
|
||||||
|
description: string,
|
||||||
|
}[],
|
||||||
|
interests: string[],
|
||||||
|
comment: string,
|
||||||
|
views: Date[],
|
||||||
|
countViews: number,
|
||||||
|
isVisible: boolean,
|
||||||
|
isActive: boolean,
|
||||||
|
createdAt: Date,
|
||||||
|
updatedAt: Date,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-page-ad-list-admin',
|
||||||
|
templateUrl: './page-ad-list-admin.component.html',
|
||||||
|
styleUrls: ['./page-ad-list-admin.component.scss']
|
||||||
|
})
|
||||||
|
export class PageAdListAdminComponent implements AfterViewInit
|
||||||
|
{
|
||||||
|
tabAdvertWithCountViews: AdvertWithCountViewsAndCompany[] = [];
|
||||||
|
tabAdvertiser: any[];
|
||||||
|
displayedColumns: string[] = [ 'title', 'company', 'email', 'interests', 'createdAt', 'updatedAt', 'countViews', 'isVisible', 'actions' ];
|
||||||
|
dataSource ;
|
||||||
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||||
|
|
||||||
|
filteredText: string = "" ;
|
||||||
|
visible: boolean = true;
|
||||||
|
noVisible: boolean = true;
|
||||||
|
campaignOne = new FormGroup({
|
||||||
|
start: new FormControl(null),
|
||||||
|
end: new FormControl(null),
|
||||||
|
});
|
||||||
|
formControlInterests = new FormControl();
|
||||||
|
allInterests: string[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
constructor( public themeService: ThemeService,
|
||||||
|
public dialog: MatDialog,
|
||||||
|
private snackBar: MatSnackBar,
|
||||||
|
private messageService: MessageService) { }
|
||||||
|
|
||||||
|
|
||||||
|
ngAfterViewInit(): void
|
||||||
|
{
|
||||||
|
// Ask for ads and then for advertiser
|
||||||
|
let params = new HttpParams();
|
||||||
|
params = params.append("isActive", true);
|
||||||
|
this.messageService
|
||||||
|
.get("ad/findAll", params)
|
||||||
|
.subscribe(ret => this.afterReceivingAds(ret), err => this.afterReceivingAds(err) );
|
||||||
|
|
||||||
|
// Ask for interest
|
||||||
|
this.messageService
|
||||||
|
.get("misc/getInterests")
|
||||||
|
.subscribe(ret => this.afterReceivingInterests(ret), err => this.afterReceivingInterests(err) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
afterReceivingAds(retour: any): void
|
||||||
|
{
|
||||||
|
if(retour.status !== "success") {
|
||||||
|
console.log(retour);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const tabAdvert = retour.data;
|
||||||
|
this.messageService
|
||||||
|
.get("user/findAll")
|
||||||
|
.subscribe(ret => this.afterReceivingAdvertiser(ret, tabAdvert), err => this.afterReceivingAdvertiser(err, tabAdvert) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
afterReceivingAdvertiser(retour: any, tabAdvert): void
|
||||||
|
{
|
||||||
|
if(retour.status !== "success") {
|
||||||
|
console.log(retour);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.tabAdvertiser = retour.data.filter(x => x.role.name === "advertiser");
|
||||||
|
for(let advert of tabAdvert) this.tabAdvertWithCountViews.push(this.advertToAdvertWithCountViewsAndCompany(advert));
|
||||||
|
this.dataSource = new MatTableDataSource<any>();
|
||||||
|
this.onFilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
afterReceivingInterests(retour: any): void
|
||||||
|
{
|
||||||
|
if(retour.status !== "success") {
|
||||||
|
console.log(retour);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.allInterests = retour.data.map(x => x.interest);
|
||||||
|
this.allInterests.sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onVisualizeImages(advert: AdvertWithCountViewsAndCompany)
|
||||||
|
{
|
||||||
|
if(advert.images.length !== 0)
|
||||||
|
{
|
||||||
|
const config = {
|
||||||
|
width: '30%',
|
||||||
|
height: '90%',
|
||||||
|
data: { images: advert.images }
|
||||||
|
};
|
||||||
|
this.dialog
|
||||||
|
.open(PopupVisualizeImagesAdminComponent, config)
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe(retour => {});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const config = { duration: 2000, panelClass: "custom-class" };
|
||||||
|
const message = "Cette annonce ne contient aucune image" ;
|
||||||
|
this.snackBar.open( message, "", config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onDelete(advert: AdvertWithCountViewsAndCompany): void
|
||||||
|
{
|
||||||
|
const config = {
|
||||||
|
data: { advert: advert }
|
||||||
|
};
|
||||||
|
this.dialog
|
||||||
|
.open(PopupDeleteAdAdminComponent, config)
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe( retour => {
|
||||||
|
|
||||||
|
const config = { duration: 1000, panelClass: "custom-class" };
|
||||||
|
let message = "" ;
|
||||||
|
if((retour === undefined) || (retour === null)) {
|
||||||
|
message = "Opération annulée" ;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const index = this.dataSource.data.findIndex( elt => (elt.id === advert.id));
|
||||||
|
this.dataSource.data.splice(index, 1);
|
||||||
|
this.dataSource.data = this.dataSource.data;
|
||||||
|
this.dataSource = this.dataSource;
|
||||||
|
message = advert.title + " a bien été supprimée ✔" ;
|
||||||
|
}
|
||||||
|
this.snackBar.open( message, "", config);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onFilter(): void
|
||||||
|
{
|
||||||
|
const startDate = this.campaignOne.get("start").value;
|
||||||
|
const endDate = this.campaignOne.get("end").value;
|
||||||
|
|
||||||
|
this.dataSource.data = [];
|
||||||
|
for(let advert of this.tabAdvertWithCountViews)
|
||||||
|
{
|
||||||
|
// filtre textuelle
|
||||||
|
let valide: boolean = this.isTextFiltrationValid(advert);;
|
||||||
|
|
||||||
|
// filtre actif
|
||||||
|
if(valide)
|
||||||
|
{
|
||||||
|
if(advert.isVisible && this.visible) valide = true;
|
||||||
|
else if((!advert.isVisible) && this.noVisible) valide = true;
|
||||||
|
else valide = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// filtre date
|
||||||
|
if(valide)
|
||||||
|
{
|
||||||
|
if ((advert.createdAt === null) && (startDate !== null)) valide = false;
|
||||||
|
else if ((advert.createdAt === null) && (endDate !== null)) valide = false;
|
||||||
|
else if (startDate !== null)
|
||||||
|
{
|
||||||
|
let timeCreatedAt = 0;
|
||||||
|
if(advert.createdAt !== null) timeCreatedAt = (new Date(advert.createdAt)).getTime();
|
||||||
|
|
||||||
|
if(startDate.getTime() > timeCreatedAt) valide = false;
|
||||||
|
else if (endDate !== null)
|
||||||
|
{
|
||||||
|
if(endDate.getTime() < timeCreatedAt) valide = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filtre interests
|
||||||
|
if(valide) {
|
||||||
|
if(this.formControlInterests.value !== null) {
|
||||||
|
for (let interest of this.formControlInterests.value) {
|
||||||
|
if (advert.interests.indexOf(interest) === -1) {
|
||||||
|
valide = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(valide) this.dataSource.data.push(advert);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataSource = new MatTableDataSource(this.dataSource.data);
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
|
this.dataSource.paginator = this.paginator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
isTextFiltrationValid(advert): boolean
|
||||||
|
{
|
||||||
|
let datePipe = new DatePipe('en-GB');
|
||||||
|
if(advert.title.includes(this.filteredText)) return true;
|
||||||
|
if(advert.company.includes(this.filteredText)) return true;
|
||||||
|
if(advert.email.includes(this.filteredText)) return true;
|
||||||
|
const createdAt = datePipe.transform(new Date(advert.createdAt), 'dd/MM/yyyy à HH:mm:ss');
|
||||||
|
if(createdAt.includes(this.filteredText)) return true;
|
||||||
|
const updatedAt = datePipe.transform(new Date(advert.updatedAt), 'dd/MM/yyyy à HH:mm:ss');
|
||||||
|
if(updatedAt.includes(this.filteredText)) return true;
|
||||||
|
if(advert.countViews.toString().includes(this.filteredText)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
advertToAdvertWithCountViewsAndCompany(advert): AdvertWithCountViewsAndCompany
|
||||||
|
{
|
||||||
|
let company0 = "company" ;
|
||||||
|
let email0 = "email" ;
|
||||||
|
for(let advertiser of this.tabAdvertiser)
|
||||||
|
{
|
||||||
|
if(advert.userId === advertiser.id) {
|
||||||
|
company0 = advertiser.company;
|
||||||
|
email0 = advertiser.email;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: advert.id,
|
||||||
|
userId: advert.userId,
|
||||||
|
title: advert.title,
|
||||||
|
company: company0,
|
||||||
|
email: email0,
|
||||||
|
url: advert.url,
|
||||||
|
images: advert.images,
|
||||||
|
interests: advert.interests,
|
||||||
|
comment: advert.comment,
|
||||||
|
views: advert.views,
|
||||||
|
countViews: advert.views.length,
|
||||||
|
isVisible: advert.isVisible,
|
||||||
|
isActive: advert.isActive,
|
||||||
|
createdAt: advert.createdAt,
|
||||||
|
updatedAt: advert.updatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onEffacerDate(): void {
|
||||||
|
this.campaignOne.setValue({start: null, end: null });
|
||||||
|
this.onFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<mat-dialog-content class="mat-typography">
|
||||||
|
Êtes-vous sûr de vouloir supprimer l'annonce <i>{{advert.title}}</i> ?
|
||||||
|
</mat-dialog-content>
|
||||||
|
|
||||||
|
<mat-dialog-actions align="end">
|
||||||
|
<button mat-button (click)="dialogRef.close();">Annuler</button>
|
||||||
|
<button mat-button (click)="onValidate()" cdkFocusInitial>Valider</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PopupDeleteAdAdminComponent } from './popup-delete-ad-admin.component';
|
||||||
|
|
||||||
|
describe('PopupDeleteAdAdminComponent', () => {
|
||||||
|
let component: PopupDeleteAdAdminComponent;
|
||||||
|
let fixture: ComponentFixture<PopupDeleteAdAdminComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ PopupDeleteAdAdminComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PopupDeleteAdAdminComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import {Component, Inject, OnInit} from '@angular/core';
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
import {MessageService} from "../../../utils/message/message.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-popup-delete-ad-admin',
|
||||||
|
templateUrl: './popup-delete-ad-admin.component.html',
|
||||||
|
styleUrls: ['./popup-delete-ad-admin.component.scss']
|
||||||
|
})
|
||||||
|
export class PopupDeleteAdAdminComponent implements OnInit
|
||||||
|
{
|
||||||
|
advert: any;
|
||||||
|
|
||||||
|
|
||||||
|
constructor( public dialogRef: MatDialogRef<PopupDeleteAdAdminComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data,
|
||||||
|
private messageService: MessageService) { }
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void
|
||||||
|
{
|
||||||
|
this.advert = this.data.advert;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onValidate(): void
|
||||||
|
{
|
||||||
|
this.messageService
|
||||||
|
.delete("ad/delete/"+this.advert.id)
|
||||||
|
.subscribe(ret => this.onValidateCallback(ret), err => this.onValidateCallback(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onValidateCallback(retour: any): void
|
||||||
|
{
|
||||||
|
if(retour.status !== "success") {
|
||||||
|
console.log(retour);
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("suppr");
|
||||||
|
console.log(retour);
|
||||||
|
this.dialogRef.close(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<div mat-dialog-title class="dialog-title">
|
||||||
|
<h2></h2>
|
||||||
|
<button mat-icon-button aria-label="close dialog" mat-dialog-close>
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<mat-grid-list cols="12" rowHeight="500">
|
||||||
|
<mat-grid-tile colspan="1" rowspan="1" (click)="onPrecedent()">
|
||||||
|
<button> < </button>
|
||||||
|
</mat-grid-tile>
|
||||||
|
<mat-grid-tile colspan="10" rowspan="1">
|
||||||
|
<img [src]="tabImages[index].base64" [alt]="tabImages[index].description">
|
||||||
|
</mat-grid-tile>
|
||||||
|
<mat-grid-tile colspan="1" rowspan="1" (click)="onSuivant()">
|
||||||
|
<button> > </button>
|
||||||
|
</mat-grid-tile>
|
||||||
|
</mat-grid-list>
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
carousel {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.dialog-title {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PopupVisualizeImagesAdminComponent } from './popup-visualize-images-admin.component';
|
||||||
|
|
||||||
|
describe('PopupVisualizeImagesAdminComponent', () => {
|
||||||
|
let component: PopupVisualizeImagesAdminComponent;
|
||||||
|
let fixture: ComponentFixture<PopupVisualizeImagesAdminComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ PopupVisualizeImagesAdminComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PopupVisualizeImagesAdminComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
import {Component, Inject, OnInit} from '@angular/core';
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-popup-visualize-images-admin',
|
||||||
|
templateUrl: './popup-visualize-images-admin.component.html',
|
||||||
|
styleUrls: ['./popup-visualize-images-admin.component.scss']
|
||||||
|
})
|
||||||
|
export class PopupVisualizeImagesAdminComponent implements OnInit {
|
||||||
|
tabImages = [];
|
||||||
|
index: number = 0;
|
||||||
|
nbImage: number = 0;
|
||||||
|
|
||||||
|
|
||||||
|
constructor( public dialogRef: MatDialogRef<PopupVisualizeImagesAdminComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data ) { }
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void
|
||||||
|
{
|
||||||
|
this.tabImages = this.data.images;
|
||||||
|
this.nbImage = this.tabImages.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
onPrecedent(): void
|
||||||
|
{
|
||||||
|
if(this.index !== 0) this.index -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSuivant(): void
|
||||||
|
{
|
||||||
|
if(this.index !== (this.nbImage-1)) this.index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<div [class]="themeService.getClassTheme()">
|
||||||
|
<div class="myContainer">
|
||||||
|
|
||||||
|
<!-- NavBar -->
|
||||||
|
<app-navbar-admin></app-navbar-admin>
|
||||||
|
|
||||||
|
<!-- Boite -->
|
||||||
|
<div class="boite">
|
||||||
|
|
||||||
|
<!-- Photo de profil -->
|
||||||
|
<div style="text-align: center">
|
||||||
|
<img [src]="admin.profileImageUrl"
|
||||||
|
onerror="this.onerror=null; this.src='assets/profil.png'">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- login -->
|
||||||
|
<div class="row myRow">
|
||||||
|
<div class="col-6 myLabel">Pseudo:</div>
|
||||||
|
<div class="col-6 myValue"> {{admin.login}} </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- email -->
|
||||||
|
<div class="row myRow">
|
||||||
|
<div class="col-6 myLabel">Mail:</div>
|
||||||
|
<div class="col-6 myValue"> {{admin.email}} </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- createdAt -->
|
||||||
|
<div class="row myRow">
|
||||||
|
<div class="col-6 myLabel">Date de création:</div>
|
||||||
|
<div class="col-6 myValue">{{admin.createdAt | date:'dd/LL/YYYY'}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modifier profil -->
|
||||||
|
<div class="btnContainer">
|
||||||
|
<button mat-button class="myBtn" (click)="onModifier()">Modifier profil</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
.myContainer {
|
||||||
|
max-width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.boite {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
width: 25%;
|
||||||
|
margin-top: 10vh;
|
||||||
|
border: solid 3px;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px 40px 20px 40px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 10px 5px 5px black;
|
||||||
|
}
|
||||||
|
.lightTheme .boite {
|
||||||
|
border-color: black;
|
||||||
|
}
|
||||||
|
.darkTheme .boite {
|
||||||
|
border-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin: 0px 0px 10px 0px;
|
||||||
|
width: 5vw;
|
||||||
|
height: 5vw;
|
||||||
|
border: solid 2px black;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: xxx-large;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.myRow {
|
||||||
|
margin: 15px 0px 15px 0px;
|
||||||
|
}
|
||||||
|
.myLabel {
|
||||||
|
text-align: right;
|
||||||
|
padding: 0px 5px 0px 0px;
|
||||||
|
margin: 0px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.myValue {
|
||||||
|
text-align: left;
|
||||||
|
padding: 0px 0px 0px 5px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btnContainer {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
.myBtn {
|
||||||
|
border: solid 1px black;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PageProfilAdminComponent } from './page-profil-admin.component';
|
||||||
|
|
||||||
|
describe('PageProfilAdminComponent', () => {
|
||||||
|
let component: PageProfilAdminComponent;
|
||||||
|
let fixture: ComponentFixture<PageProfilAdminComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ PageProfilAdminComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PageProfilAdminComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||||
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
|
import {MessageService} from "../../../utils/message/message.service";
|
||||||
|
import {ProfilService} from "../../../utils/profil/profil.service";
|
||||||
|
import {PopupUpdateAdminComponent} from "../popup-update-admin/popup-update-admin.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-page-profil-admin',
|
||||||
|
templateUrl: './page-profil-admin.component.html',
|
||||||
|
styleUrls: ['./page-profil-admin.component.scss']
|
||||||
|
})
|
||||||
|
export class PageProfilAdminComponent implements OnInit
|
||||||
|
{
|
||||||
|
admin = {
|
||||||
|
_id: "",
|
||||||
|
login: "",
|
||||||
|
hashPass: "",
|
||||||
|
email: "",
|
||||||
|
role: {
|
||||||
|
name: "admin",
|
||||||
|
permission: 10,
|
||||||
|
isAccepted: true,
|
||||||
|
},
|
||||||
|
profileImageUrl: "",
|
||||||
|
dateOfBirth: null,
|
||||||
|
gender: "man",
|
||||||
|
interests: [],
|
||||||
|
company: "",
|
||||||
|
isActive: true,
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
lastConnexion: null
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
constructor( public themeService: ThemeService,
|
||||||
|
public dialog: MatDialog,
|
||||||
|
private snackBar: MatSnackBar,
|
||||||
|
private messageService: MessageService,
|
||||||
|
private profilService: ProfilService ) { }
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void
|
||||||
|
{
|
||||||
|
this.messageService
|
||||||
|
.get( "user/findOne/"+this.profilService.getId())
|
||||||
|
.subscribe( retour => this.ngOnInitCallback(retour), err => this.ngOnInitCallback(err) )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInitCallback(retour: any)
|
||||||
|
{
|
||||||
|
if(retour.status !== "success") {
|
||||||
|
console.log(retour);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.admin = retour.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onModifier()
|
||||||
|
{
|
||||||
|
const config = {
|
||||||
|
width: '25%',
|
||||||
|
data: { admin: this.admin }
|
||||||
|
};
|
||||||
|
this.dialog
|
||||||
|
.open(PopupUpdateAdminComponent, config)
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe(retour => {
|
||||||
|
|
||||||
|
if((retour === null) || (retour === undefined))
|
||||||
|
{
|
||||||
|
const config = { duration: 1000, panelClass: "custom-class" };
|
||||||
|
this.snackBar.open( "Opération annulé", "", config);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.admin = retour;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
<div class="myContainer">
|
||||||
|
<div class="boite">
|
||||||
|
|
||||||
|
<!-- photo de profil -->
|
||||||
|
<div style="text-align: center">
|
||||||
|
<img [src]="adminCopy.profileImageUrl" onerror="this.onerror=null; this.src='assets/profil.png'"><br>
|
||||||
|
<input title="lien vers image" type="text" [(ngModel)]="adminCopy.profileImageUrl" style="width: 90%">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- divider -->
|
||||||
|
<br><mat-divider></mat-divider><br>
|
||||||
|
|
||||||
|
<!-- login -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Pseudo</mat-label>
|
||||||
|
<input matInput type="text" [(ngModel)]="adminCopy.login">
|
||||||
|
</mat-form-field><br>
|
||||||
|
|
||||||
|
<!-- divider -->
|
||||||
|
<mat-divider></mat-divider><br>
|
||||||
|
|
||||||
|
<!-- Modifier mot de passe -->
|
||||||
|
<div>
|
||||||
|
Modifier mot de passe:
|
||||||
|
<mat-checkbox [(ngModel)]="changePassword"></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- nouveau mot de passe -->
|
||||||
|
<div *ngIf="changePassword" style="margin-top: 10px">
|
||||||
|
<!-- Nouveau mot de passe -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Nouveau mot de passe</mat-label>
|
||||||
|
<input matInput type="password" [(ngModel)]="newPassword">
|
||||||
|
</mat-form-field>
|
||||||
|
<br>
|
||||||
|
<!-- Confirmation npuveau mot de passe -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Confirmation nouveau mot de passe</mat-label>
|
||||||
|
<input matInput type="password" [(ngModel)]="confirmNewPassword">
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="!changePassword"><br></div>
|
||||||
|
|
||||||
|
<!-- divider -->
|
||||||
|
<mat-divider></mat-divider><br>
|
||||||
|
|
||||||
|
<!-- message d'erreur -->
|
||||||
|
<div *ngIf="hasError" style="text-align: center; margin-bottom: 20px;">
|
||||||
|
<span class="mat-error">{{errorMessage}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- boutons -->
|
||||||
|
<div style="width: 100%; text-align: right">
|
||||||
|
<button mat-button (click)="this.dialogRef.close(null)"> Annuler </button>
|
||||||
|
<button mat-button (click)="onValider()"> Enregistrer </button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
.boite {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin: 0px 0px 10px 0px;
|
||||||
|
width: 5vw;
|
||||||
|
height: 5vw;
|
||||||
|
border: solid 2px black;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: xxx-large;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// aura
|
||||||
|
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
|
||||||
|
background-color: grey !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// contenu coche
|
||||||
|
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||||
|
background-color: black !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// indeterminate
|
||||||
|
::ng-deep .mat-checkbox .mat-checkbox-frame {
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PopupUpdateAdminComponent } from './popup-update-admin.component';
|
||||||
|
|
||||||
|
describe('PopupUpdateAdminComponent', () => {
|
||||||
|
let component: PopupUpdateAdminComponent;
|
||||||
|
let fixture: ComponentFixture<PopupUpdateAdminComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ PopupUpdateAdminComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PopupUpdateAdminComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
import {Component, Inject, OnInit} from '@angular/core';
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
import {MessageService} from "../../../utils/message/message.service";
|
||||||
|
import {ProfilService} from "../../../utils/profil/profil.service";
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-popup-update-admin',
|
||||||
|
templateUrl: './popup-update-admin.component.html',
|
||||||
|
styleUrls: ['./popup-update-admin.component.scss']
|
||||||
|
})
|
||||||
|
export class PopupUpdateAdminComponent implements OnInit
|
||||||
|
{
|
||||||
|
adminCopy;
|
||||||
|
newPassword: string = "";
|
||||||
|
confirmNewPassword: string = "" ;
|
||||||
|
changePassword: boolean = false ;
|
||||||
|
hasError: boolean = false;
|
||||||
|
errorMessage: string = "" ;
|
||||||
|
|
||||||
|
|
||||||
|
constructor( public dialogRef: MatDialogRef<PopupUpdateAdminComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data,
|
||||||
|
private messageService: MessageService,
|
||||||
|
private profilService: ProfilService ) { }
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void
|
||||||
|
{
|
||||||
|
const admin0 = this.data.admin;
|
||||||
|
this.adminCopy = {
|
||||||
|
_id: admin0._id,
|
||||||
|
login: admin0.login,
|
||||||
|
hashPass: admin0.hashPass,
|
||||||
|
email: admin0.email,
|
||||||
|
role: {
|
||||||
|
name: admin0.role.name,
|
||||||
|
permission: admin0.role.permission,
|
||||||
|
isAccepted: admin0.role.isAccepted,
|
||||||
|
},
|
||||||
|
profileImageUrl: admin0.profileImageUrl,
|
||||||
|
dateOfBirth: admin0.dateOfBirth,
|
||||||
|
gender: admin0.gender,
|
||||||
|
interests: [],
|
||||||
|
company: "",
|
||||||
|
isActive: admin0.isActive,
|
||||||
|
createdAt: admin0.createdAt,
|
||||||
|
updatedAt: admin0.updatedAt,
|
||||||
|
lastConnexion: admin0.lastConnexion
|
||||||
|
};
|
||||||
|
for(let interest of admin0.interests) this.adminCopy.interests.push(interest);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onValider()
|
||||||
|
{
|
||||||
|
this.checkField();
|
||||||
|
if(!this.hasError)
|
||||||
|
{
|
||||||
|
if(this.changePassword) this.adminCopy.hashPass = this.newPassword;
|
||||||
|
const data = {
|
||||||
|
login: this.adminCopy.login,
|
||||||
|
hashPass: this.adminCopy.hashPass,
|
||||||
|
email: this.adminCopy.email,
|
||||||
|
profileImageUrl: this.adminCopy.profileImageUrl,
|
||||||
|
};
|
||||||
|
this.messageService
|
||||||
|
.put("user/update/"+this.profilService.getId(), data)
|
||||||
|
.subscribe( ret => this.onValiderCallback(ret), err => this.onValiderCallback(err) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onValiderCallback(retour: any)
|
||||||
|
{
|
||||||
|
if(retour.status !== "success") {
|
||||||
|
console.log(retour);
|
||||||
|
this.dialogRef.close(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.profilService.setProfileImageUrl(this.adminCopy.profileImageUrl);
|
||||||
|
this.dialogRef.close(this.adminCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
checkField()
|
||||||
|
{
|
||||||
|
if(this.adminCopy.login.length === 0) {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ 'pseudo'" ;
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(this.adminCopy.email.length === 0) {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ 'email'" ;
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(!this.isValidEmail(this.adminCopy.email)) {
|
||||||
|
this.errorMessage = "Email invalide" ;
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if((this.changePassword) && (this.newPassword.length === 0)) {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ 'mot de passe'" ;
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if((this.changePassword) && (this.newPassword !== this.confirmNewPassword)) {
|
||||||
|
this.errorMessage = "Le mot de passe est différent de sa confirmation" ;
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.errorMessage = "" ;
|
||||||
|
this.hasError = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
isValidEmail(email)
|
||||||
|
{
|
||||||
|
let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
return re.test(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<mat-form-field class="example-chip-list" appearance="fill">
|
||||||
|
|
||||||
|
<!-- ------------------------------------------------------------------------------------ -->
|
||||||
|
|
||||||
|
<mat-label>Centres d'intérêt</mat-label>
|
||||||
|
|
||||||
|
<!-- ------------------------------------------------------------------------------------ -->
|
||||||
|
|
||||||
|
<mat-chip-list #chipList aria-label="Fruit selection">
|
||||||
|
|
||||||
|
<mat-chip
|
||||||
|
*ngFor="let interest of myInterests"
|
||||||
|
[selectable]="selectable"
|
||||||
|
[removable]="removable"
|
||||||
|
(removed)="remove(interest)">
|
||||||
|
{{interest}}
|
||||||
|
<button matChipRemove *ngIf="removable">
|
||||||
|
<mat-icon>cancel</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-chip>
|
||||||
|
|
||||||
|
<input
|
||||||
|
placeholder="Tapez un centre d'intérêt et pressez 'Entrer' pour l'inserer"
|
||||||
|
#tagInput
|
||||||
|
[formControl]="formControl"
|
||||||
|
[matAutocomplete]="auto"
|
||||||
|
[matChipInputFor]="chipList"
|
||||||
|
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||||
|
(matChipInputTokenEnd)="add($event)">
|
||||||
|
|
||||||
|
</mat-chip-list>
|
||||||
|
|
||||||
|
<!-- ------------------------------------------------------------------------------------ -->
|
||||||
|
|
||||||
|
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
|
||||||
|
<mat-option *ngFor="let interest of filteredInterests | async" [value]="interest">
|
||||||
|
{{interest}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-autocomplete>
|
||||||
|
|
||||||
|
<!-- ------------------------------------------------------------------------------------ -->
|
||||||
|
|
||||||
|
</mat-form-field>
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { InputInterestsAdminComponent } from './input-interests-admin.component';
|
||||||
|
|
||||||
|
describe('InputInterestsAdminComponent', () => {
|
||||||
|
let component: InputInterestsAdminComponent;
|
||||||
|
let fixture: ComponentFixture<InputInterestsAdminComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ InputInterestsAdminComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(InputInterestsAdminComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
|
||||||
|
import {COMMA, ENTER} from "@angular/cdk/keycodes";
|
||||||
|
import {FormControl} from "@angular/forms";
|
||||||
|
import {Observable} from "rxjs";
|
||||||
|
import {map, startWith} from "rxjs/operators";
|
||||||
|
import {MatChipInputEvent} from "@angular/material/chips";
|
||||||
|
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
|
||||||
|
import {MessageService} from "../../../utils/message/message.service";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-input-interests-admin',
|
||||||
|
templateUrl: './input-interests-admin.component.html',
|
||||||
|
styleUrls: ['./input-interests-admin.component.scss']
|
||||||
|
})
|
||||||
|
export class InputInterestsAdminComponent implements OnInit
|
||||||
|
{
|
||||||
|
selectable = true;
|
||||||
|
removable = true;
|
||||||
|
separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||||
|
formControl = new FormControl();
|
||||||
|
filteredInterests: Observable<string[]>;
|
||||||
|
@Input() myInterests: string[] = [];
|
||||||
|
allInterests: string[] = [];
|
||||||
|
@Output() eventEmitter = new EventEmitter<string[]>();
|
||||||
|
@ViewChild('tagInput') tagInput: ElementRef<HTMLInputElement>;
|
||||||
|
interestsNotSelected: string[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
constructor( private messageService: MessageService ) {}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void
|
||||||
|
{
|
||||||
|
this.filteredInterests = this.formControl.valueChanges.pipe(
|
||||||
|
startWith(null),
|
||||||
|
map((fruit: string | null) => fruit ? this._filter(fruit) : this.interestsNotSelected.slice()));
|
||||||
|
|
||||||
|
this.messageService
|
||||||
|
.get("misc/getInterests")
|
||||||
|
.subscribe( retour => {
|
||||||
|
|
||||||
|
if(retour.status !== "success") {
|
||||||
|
console.log(retour);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.allInterests = [];
|
||||||
|
for(let elt of retour.data)
|
||||||
|
{
|
||||||
|
this.allInterests.push(elt.interest);
|
||||||
|
this.interestsNotSelected.push(elt.interest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
add(event: MatChipInputEvent): void
|
||||||
|
{
|
||||||
|
const value = (event.value || '').trim();
|
||||||
|
const index = this.interestsNotSelected.indexOf(value);
|
||||||
|
if (value && (index !== -1) && (!this.myInterests.includes(value)))
|
||||||
|
{
|
||||||
|
this.myInterests.push(value);
|
||||||
|
event.chipInput!.clear();
|
||||||
|
this.formControl.setValue(null);
|
||||||
|
this.eventEmitter.emit(this.myInterests);
|
||||||
|
this.interestsNotSelected.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
remove(interest: string): void
|
||||||
|
{
|
||||||
|
// supprimer 'interest' de 'myInterest'
|
||||||
|
const index = this.myInterests.indexOf(interest);
|
||||||
|
if (index >= 0) this.myInterests.splice(index, 1);
|
||||||
|
this.eventEmitter.emit(this.myInterests);
|
||||||
|
|
||||||
|
// remmettre 'interest' dans 'interestsNotSelected'
|
||||||
|
if(!this.interestsNotSelected.includes(interest))
|
||||||
|
{
|
||||||
|
const indexOfAutres = this.interestsNotSelected.indexOf("Autres");
|
||||||
|
if(indexOfAutres !== -1)
|
||||||
|
{
|
||||||
|
this.interestsNotSelected.splice(indexOfAutres, 1);
|
||||||
|
if(interest !== "Autres") this.interestsNotSelected.push(interest);
|
||||||
|
this.interestsNotSelected.sort();
|
||||||
|
this.interestsNotSelected.push("Autres");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.interestsNotSelected.push(interest);
|
||||||
|
if(interest !== "Autres") this.interestsNotSelected.sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
selected(event: MatAutocompleteSelectedEvent): void
|
||||||
|
{
|
||||||
|
const value = event.option.viewValue;
|
||||||
|
if(!this.myInterests.includes(value))
|
||||||
|
{
|
||||||
|
this.myInterests.push(value);
|
||||||
|
const index = this.interestsNotSelected.indexOf(value);
|
||||||
|
this.interestsNotSelected.splice(index, 1);
|
||||||
|
}
|
||||||
|
this.tagInput.nativeElement.value = '';
|
||||||
|
this.formControl.setValue(null);
|
||||||
|
this.eventEmitter.emit(this.myInterests);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private _filter(value: string): string[]
|
||||||
|
{
|
||||||
|
const filterValue = value.toLowerCase();
|
||||||
|
return this.interestsNotSelected.filter(fruit => fruit.toLowerCase().includes(filterValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
<div [class]="themeService.getClassTheme()">
|
||||||
|
<div class="myContainer">
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Navbar -->
|
||||||
|
<app-navbar-admin></app-navbar-admin><br><br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- filtre + btnAddUser -->
|
||||||
|
<div class="row" style="margin: 20px 3% 20px 3%">
|
||||||
|
|
||||||
|
<!-- filtre -->
|
||||||
|
<div class="col-9" style="padding: 0px 0px 0px 0px;">
|
||||||
|
<div class="filtersContainer mat-elevation-z8">
|
||||||
|
|
||||||
|
<!-- titre -->
|
||||||
|
<div style="font-weight: bold; margin-bottom: 10px;">
|
||||||
|
Filtre
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<!-- filtre textuelle-->
|
||||||
|
<div style="margin: 10px 0px 20px 2%;">
|
||||||
|
<input class="textFilter" [(ngModel)]="filteredText" (ngModelChange)="onFilter()" placeholder="Rechercher par mots-clés...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- role + actif + période -->
|
||||||
|
<div class="row myRow">
|
||||||
|
|
||||||
|
<!-- choix role -->
|
||||||
|
<div class="col-2">
|
||||||
|
<mat-radio-group [(ngModel)]="roleName">
|
||||||
|
<mat-radio-button value="user" (click)="this.roleName = 'user'; onFilter()">
|
||||||
|
Utilisateur
|
||||||
|
</mat-radio-button><br>
|
||||||
|
<mat-radio-button value="advertiser" (click)="this.roleName = 'advertiser'; onFilter()">
|
||||||
|
Annonceur
|
||||||
|
</mat-radio-button><br>
|
||||||
|
<mat-radio-button value="admin" (click)="this.roleName = 'admin'; onFilter()">
|
||||||
|
Admin
|
||||||
|
</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- actif -->
|
||||||
|
<div class="col-2">
|
||||||
|
<mat-checkbox [(ngModel)]="active" (change)="onFilter()">actif</mat-checkbox><br>
|
||||||
|
<mat-checkbox [(ngModel)]="noActive" (change)="onFilter()">non actif</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- période -->
|
||||||
|
<div class="col-8" style="text-align: right;">
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Période de dernière connexion</mat-label>
|
||||||
|
<mat-date-range-input
|
||||||
|
[formGroup]="campaignOne"
|
||||||
|
[rangePicker]="campaignOnePicker">
|
||||||
|
<input matStartDate placeholder="Start date" formControlName="start">
|
||||||
|
<input matEndDate placeholder="End date" formControlName="end">
|
||||||
|
</mat-date-range-input>
|
||||||
|
<mat-datepicker-toggle matSuffix [for]="campaignOnePicker"></mat-datepicker-toggle>
|
||||||
|
<mat-date-range-picker #campaignOnePicker></mat-date-range-picker>
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-icon-button (click)="onFilter()">
|
||||||
|
<mat-icon>keyboard_tab</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button (click)="onEffacerDate()">
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- btnAddUser -->
|
||||||
|
<div class="col-3" style="text-align: right; position: relative;">
|
||||||
|
<button mat-button class="btnAjouter" (click)="onCreateUser()" style="position: absolute; bottom: 0; right: 0;">
|
||||||
|
<mat-icon>add_circle</mat-icon> Ajouter un utilisateur
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Table -->
|
||||||
|
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
|
||||||
|
|
||||||
|
<!-- IsActive Column -->
|
||||||
|
<ng-container matColumnDef="isActive">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header>
|
||||||
|
<mat-icon>power_settings_new</mat-icon>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let user">
|
||||||
|
<mat-slide-toggle *ngIf="user.role.name !== 'superAdmin'" [(ngModel)]="user.isActive" (click)="onSliderIsActive(user)"></mat-slide-toggle>
|
||||||
|
<mat-slide-toggle *ngIf="user.role.name === 'superAdmin'" [(ngModel)]="user.isActive" disabled></mat-slide-toggle>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Login Column -->
|
||||||
|
<ng-container matColumnDef="login">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Pseudo </th>
|
||||||
|
<td mat-cell *matCellDef="let user">
|
||||||
|
{{user.login}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Mail Column -->
|
||||||
|
<ng-container matColumnDef="email">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Email </th>
|
||||||
|
<td mat-cell *matCellDef="let user">
|
||||||
|
{{user.email}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Company Column -->
|
||||||
|
<ng-container matColumnDef="company">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Entreprise </th>
|
||||||
|
<td mat-cell *matCellDef="let user">
|
||||||
|
{{user.company}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- DateOfBirth Column -->
|
||||||
|
<ng-container matColumnDef="dateOfBirth">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Date de naissance </th>
|
||||||
|
<td mat-cell *matCellDef="let user">
|
||||||
|
<span *ngIf="((user.dateOfBirth === null) || (user.dateOfBirth === undefined)); else elseDateOfBirth"></span>
|
||||||
|
<ng-template #elseDateOfBirth> {{ user.dateOfBirth | date:'dd/LL/YYYY' }} </ng-template>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Age Column -->
|
||||||
|
<ng-container matColumnDef="age">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Âge </th>
|
||||||
|
<td mat-cell *matCellDef="let user">
|
||||||
|
<span *ngIf="(user.age < 0) ; else elseAge"></span>
|
||||||
|
<ng-template #elseAge> {{user.age}} </ng-template>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Sexe Column -->
|
||||||
|
<ng-container matColumnDef="sexe">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Sexe </th>
|
||||||
|
<td mat-cell *matCellDef="let user">
|
||||||
|
<span *ngIf="(user.gender === 'man') ; else elseSexe"> H </span>
|
||||||
|
<ng-template #elseSexe> F </ng-template>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Interests Column -->
|
||||||
|
<ng-container matColumnDef="interests">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Centres d'intérêt </th>
|
||||||
|
<td mat-cell *matCellDef="let user">
|
||||||
|
<span *ngFor="let interest of user.interests; let isLast = last;">
|
||||||
|
<span *ngIf="!isLast ; else elseInterests"> {{interest}}, </span>
|
||||||
|
<ng-template #elseInterests> {{interest}} </ng-template>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- CreatedAt Column -->
|
||||||
|
<ng-container matColumnDef="createdAt">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Date de création </th>
|
||||||
|
<td mat-cell *matCellDef="let user">
|
||||||
|
{{ user.createdAt | date:'dd/LL/YYYY à HH:mm:ss' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- LastConnexion Column -->
|
||||||
|
<ng-container matColumnDef="lastConnexion">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Dernière connexion </th>
|
||||||
|
<td mat-cell *matCellDef="let user">
|
||||||
|
{{ user.lastConnexion | date:'dd/LL/YYYY à HH:mm:ss' }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- IsAccepted Column -->
|
||||||
|
<ng-container matColumnDef="isAccepted">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Accepté </th>
|
||||||
|
<td mat-cell *matCellDef="let user">
|
||||||
|
<mat-slide-toggle [(ngModel)]="user.role.isAccepted" (click)="onSlideIsAccepted(user)"></mat-slide-toggle>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Directives -->
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
<div style="width: 94%; margin: auto auto">
|
||||||
|
<mat-paginator [pageSizeOptions]="[10, 20, 50, 100]" showFirstLastButtons aria-label="Select page of periodic elements"></mat-paginator>
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
.myContainer {
|
||||||
|
min-height: 100vh;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
|
.filtersContainer {
|
||||||
|
width: 90%;
|
||||||
|
background-color: white;
|
||||||
|
padding: 10px 10px 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.myRow {
|
||||||
|
margin-left: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textFilter {
|
||||||
|
width: 50%;
|
||||||
|
font-size: medium;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btnAjouter {
|
||||||
|
background-color: white;
|
||||||
|
border: solid 1px black;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
|
table {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 94%;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
.darkTheme table { border: solid 2px white; }
|
||||||
|
|
||||||
|
th.mat-sort-header-sorted {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
::ng-deep .mat-radio-inner-circle {
|
||||||
|
color: black !important;
|
||||||
|
background-color: black !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-radio-outer-circle{
|
||||||
|
color: black !important;
|
||||||
|
border: solid 1px gray !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// aura
|
||||||
|
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
|
||||||
|
background-color: grey !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// contenu coche
|
||||||
|
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||||
|
background-color: black !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// indeterminate
|
||||||
|
::ng-deep .mat-checkbox .mat-checkbox-frame {
|
||||||
|
border: solid 1px black !important;
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// rong gauche
|
||||||
|
::ng-deep .mat-slide-toggle-thumb {
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// trait droite
|
||||||
|
::ng-deep .mat-slide-toggle-bar {
|
||||||
|
background-color: gray !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rond droite
|
||||||
|
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-thumb {
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// trait gauche
|
||||||
|
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-bar {
|
||||||
|
background-color: cornflowerblue !important;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PageUserListComponent } from './page-user-list.component';
|
||||||
|
|
||||||
|
describe('PageUserListComponent', () => {
|
||||||
|
let component: PageUserListComponent;
|
||||||
|
let fixture: ComponentFixture<PageUserListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ PageUserListComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PageUserListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,242 @@
|
||||||
|
import {AfterViewInit, Component, ViewChild} from '@angular/core';
|
||||||
|
import {MatSort} from "@angular/material/sort";
|
||||||
|
import {MatPaginator} from "@angular/material/paginator";
|
||||||
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
|
import {MatTableDataSource} from "@angular/material/table";
|
||||||
|
import {PopupCreateUserComponent} from "../popup-create-user/popup-create-user.component";
|
||||||
|
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||||
|
import {MessageService} from "../../../utils/message/message.service";
|
||||||
|
import {FormControl, FormGroup} from "@angular/forms";
|
||||||
|
import {DatePipe} from "@angular/common";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-page-user-list',
|
||||||
|
templateUrl: './page-user-list.component.html',
|
||||||
|
styleUrls: ['./page-user-list.component.scss']
|
||||||
|
})
|
||||||
|
export class PageUserListComponent implements AfterViewInit
|
||||||
|
{
|
||||||
|
displayedColumns: string[];
|
||||||
|
displayedColumnsUser: string[] = [ 'isActive', 'login', 'email', 'dateOfBirth', 'age', 'sexe', 'interests', 'createdAt', 'lastConnexion' ];
|
||||||
|
displayedColumnsAdvertiser: string[] = [ 'isActive', 'login', 'email', 'company', 'createdAt', 'lastConnexion', 'isAccepted' ];
|
||||||
|
displayedColumnsAdmin: string[] = [ 'isActive', 'login', 'email', 'createdAt', 'lastConnexion' ];
|
||||||
|
|
||||||
|
tabUser: any[] = [];
|
||||||
|
tabAdvertiser: any[] = [];
|
||||||
|
tabAdmin: any[] = [];
|
||||||
|
|
||||||
|
roleName: string = "user" ;
|
||||||
|
dataSource ;
|
||||||
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||||
|
|
||||||
|
filteredText: string = "" ;
|
||||||
|
active: boolean = true;
|
||||||
|
noActive: boolean = false;
|
||||||
|
campaignOne = new FormGroup({
|
||||||
|
start: new FormControl(null),
|
||||||
|
end: new FormControl(null),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
constructor( public themeService: ThemeService,
|
||||||
|
public dialog: MatDialog,
|
||||||
|
private snackBar: MatSnackBar,
|
||||||
|
private messageService: MessageService ) { }
|
||||||
|
|
||||||
|
|
||||||
|
ngAfterViewInit(): void
|
||||||
|
{
|
||||||
|
this.messageService
|
||||||
|
.get("user/findAll")
|
||||||
|
.subscribe(ret => this.ngAfterViewInitCallback(ret), err => this.ngAfterViewInitCallback(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngAfterViewInitCallback(retour: any): void
|
||||||
|
{
|
||||||
|
if(retour.status !== "success") {
|
||||||
|
console.log(retour);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for(let person of retour.data)
|
||||||
|
{
|
||||||
|
if(person.role.name === "user") {
|
||||||
|
person["age"] = this.getAge(person.dateOfBirth);
|
||||||
|
delete person.profileImageUrl;
|
||||||
|
this.tabUser.push(person);
|
||||||
|
}
|
||||||
|
else if(person.role.name === "advertiser") this.tabAdvertiser.push(person);
|
||||||
|
else this.tabAdmin.push(person);
|
||||||
|
}
|
||||||
|
this.onFilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onCreateUser(): void
|
||||||
|
{
|
||||||
|
const config = { width: '50%' };
|
||||||
|
this.dialog
|
||||||
|
.open(PopupCreateUserComponent, config)
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe( retour => {
|
||||||
|
|
||||||
|
const config = { duration: 1000, panelClass: "custom-class" };
|
||||||
|
if((retour === null) || (retour === undefined)) {
|
||||||
|
this.snackBar.open( "Opération annulée", "", config);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.snackBar.open( "L'utilisateur a bien été créé", "", config);
|
||||||
|
if(retour.role.name === "user") this.tabUser.push(retour);
|
||||||
|
else if(retour.role.name === "advertiser") this.tabAdvertiser.push(retour);
|
||||||
|
else if(retour.role.name === "admin") this.tabAdmin.push(retour);
|
||||||
|
this.onFilter();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onSliderIsActive(user: any): void
|
||||||
|
{
|
||||||
|
// il faut envoyer la négation de user.isActive
|
||||||
|
this.messageService
|
||||||
|
.put("user/update/"+user.id, { isActive: !user.isActive })
|
||||||
|
.subscribe(
|
||||||
|
ret => {},
|
||||||
|
err => {
|
||||||
|
console.log("onSliderIsActive");
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onSlideIsAccepted(user: any): void
|
||||||
|
{
|
||||||
|
// il faut envoyer la négation de user.role.isAccepted
|
||||||
|
const role0 = {
|
||||||
|
name: user.role.name,
|
||||||
|
permission: user.role.permission,
|
||||||
|
isAccepted: !user.role.isAccepted,
|
||||||
|
};
|
||||||
|
this.messageService
|
||||||
|
.put("user/update/"+user.id, {role: role0})
|
||||||
|
.subscribe(
|
||||||
|
ret => {},
|
||||||
|
err => {
|
||||||
|
console.log("onSlideIsAccepted");
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getAge(date: Date): number
|
||||||
|
{
|
||||||
|
if((date === null) || (date === undefined)) return -1;
|
||||||
|
else {
|
||||||
|
const diff = Date.now() - (new Date(date)).getTime();
|
||||||
|
const age = new Date(diff);
|
||||||
|
return Math.abs(age.getUTCFullYear() - 1970);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onFilter(): void
|
||||||
|
{
|
||||||
|
const startDate = this.campaignOne.get("start").value;
|
||||||
|
const endDate = this.campaignOne.get("end").value;
|
||||||
|
|
||||||
|
let tab1 = [];
|
||||||
|
if(this.roleName === "user") {
|
||||||
|
this.displayedColumns = this.displayedColumnsUser;
|
||||||
|
tab1 = this.tabUser;
|
||||||
|
}
|
||||||
|
else if(this.roleName === "advertiser") {
|
||||||
|
this.displayedColumns = this.displayedColumnsAdvertiser;
|
||||||
|
tab1 = this.tabAdvertiser;
|
||||||
|
}
|
||||||
|
else if(this.roleName === "admin") {
|
||||||
|
this.displayedColumns = this.displayedColumnsAdmin;
|
||||||
|
tab1 = this.tabAdmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tab2 = [];
|
||||||
|
for(let user of tab1)
|
||||||
|
{
|
||||||
|
// filtre textuelle
|
||||||
|
let valide: boolean = this.isTextFiltrationValid(user);;
|
||||||
|
|
||||||
|
// filtre actif
|
||||||
|
if(valide)
|
||||||
|
{
|
||||||
|
if(user.isActive && this.active) valide = true;
|
||||||
|
else if((!user.isActive) && this.noActive) valide = true;
|
||||||
|
else valide = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// filtre date
|
||||||
|
if(valide)
|
||||||
|
{
|
||||||
|
if ((user.lastConnexion === null) && (startDate !== null)) valide = false;
|
||||||
|
else if ((user.lastConnexion === null) && (endDate !== null)) valide = false;
|
||||||
|
else if (startDate !== null)
|
||||||
|
{
|
||||||
|
let timeLastConnexion = 0;
|
||||||
|
if(user.lastConnexion !== null) timeLastConnexion = (new Date(user.lastConnexion)).getTime();
|
||||||
|
|
||||||
|
if(startDate.getTime() > timeLastConnexion) valide = false;
|
||||||
|
else if (endDate !== null)
|
||||||
|
{
|
||||||
|
if(endDate.getTime() < timeLastConnexion) valide = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(valide) tab2.push(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dataSource = new MatTableDataSource(tab2);
|
||||||
|
this.dataSource.sort = this.sort;
|
||||||
|
this.dataSource.paginator = this.paginator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
isTextFiltrationValid(user): boolean
|
||||||
|
{
|
||||||
|
let datePipe = new DatePipe('en-GB');
|
||||||
|
if(user.login.includes(this.filteredText)) return true;
|
||||||
|
if(user.email.includes(this.filteredText)) return true;
|
||||||
|
const createdAt = datePipe.transform(new Date(user.createdAt), 'dd/MM/yyyy à HH:mm:ss');
|
||||||
|
if(createdAt.includes(this.filteredText)) return true;
|
||||||
|
const lastConnexion = datePipe.transform(new Date(user.lastConnexion), 'dd/MM/yyyy à HH:mm:ss');
|
||||||
|
if(lastConnexion.includes(this.filteredText)) return true;
|
||||||
|
|
||||||
|
if(this.roleName === 'user')
|
||||||
|
{
|
||||||
|
const dateOfBirth = datePipe.transform(new Date(user.dateOfBirth), 'dd/MM/yyyy à HH:mm:ss');
|
||||||
|
if(dateOfBirth.includes(this.filteredText)) return true;
|
||||||
|
if(user.age.toString().includes(this.filteredText)) return true;
|
||||||
|
if((user.sexe === 'man') && (this.filteredText === 'M')) return true;
|
||||||
|
if((user.sexe === 'woman') && (this.filteredText === 'F')) return true;
|
||||||
|
if(user.interests.toString().includes(this.filteredText)) return true;
|
||||||
|
}
|
||||||
|
else if(this.roleName === 'advertiser')
|
||||||
|
{
|
||||||
|
if(user.company.includes(this.filteredText)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onEffacerDate(): void {
|
||||||
|
this.campaignOne.setValue({start: null, end: null });
|
||||||
|
this.onFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
<div class="myContainer">
|
||||||
|
|
||||||
|
<!-- Rôle -->
|
||||||
|
<div class="matRadioGroupContainer">
|
||||||
|
<mat-radio-group [(ngModel)]="user.role.name" (change)="hasError=false; errorMessage = '';">
|
||||||
|
<mat-radio-button value="user"> Utilisateur </mat-radio-button><br>
|
||||||
|
<mat-radio-button value="advertiser"> Annonceur </mat-radio-button><br>
|
||||||
|
<mat-radio-button value="admin"> Admin </mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
</div><br>
|
||||||
|
|
||||||
|
<!-- divider -->
|
||||||
|
<mat-divider></mat-divider><br>
|
||||||
|
|
||||||
|
<!-- userBlock or advertiserOrAdminBlock -->
|
||||||
|
<div *ngIf="user.role.name === 'user'; then userBlock"></div>
|
||||||
|
<div *ngIf="(user.role.name === 'advertiser' || user.role.name === 'admin'); then advertiserOrAdminBlock"></div>
|
||||||
|
|
||||||
|
<!-- divider -->
|
||||||
|
<mat-divider *ngIf="user.role.name !== ''" style="margin-top: 10px;"></mat-divider><br>
|
||||||
|
|
||||||
|
<!-- Error -->
|
||||||
|
<div *ngIf="hasError" style="text-align: center; margin-bottom: 20px;">
|
||||||
|
<span class="mat-error">{{errorMessage}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Boutons -->
|
||||||
|
<div style="width: 100%; text-align: right">
|
||||||
|
<button mat-button (click)="this.dialogRef.close(null)"> Annuler </button>
|
||||||
|
<button mat-button (click)="onEnregistrer()" [disabled]="user.role.name === ''" > Enregistrer </button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ------------------------------------------------------------------------------------------------------------------- -->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- userBlock -->
|
||||||
|
<ng-template #userBlock>
|
||||||
|
|
||||||
|
<!-- photo de profil -->
|
||||||
|
<div style="text-align: center">
|
||||||
|
<img [src]="user.profileImageUrl" onerror="this.onerror=null; this.src='assets/profil.png'"><br>
|
||||||
|
<input title="lien vers image" type="text" [(ngModel)]="user.profileImageUrl" style="width: 90%">
|
||||||
|
</div><br>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<!-- Colonne gauche -->
|
||||||
|
<div class="col-4 leftCol">
|
||||||
|
|
||||||
|
<!-- Email -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Email</mat-label>
|
||||||
|
<input matInput type="email" [(ngModel)]="user.email" required>
|
||||||
|
</mat-form-field><br>
|
||||||
|
|
||||||
|
<!-- Login -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Pseudo</mat-label>
|
||||||
|
<input matInput type="text" [(ngModel)]="user.login" required>
|
||||||
|
</mat-form-field><br>
|
||||||
|
|
||||||
|
<!-- Mot de passe -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Mot de passe</mat-label>
|
||||||
|
<input matInput type="password" [(ngModel)]="password" required>
|
||||||
|
</mat-form-field><br>
|
||||||
|
|
||||||
|
<!-- Confirmation mot de passe -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Confirmation mot de passe</mat-label>
|
||||||
|
<input matInput type="password" [(ngModel)]="confirmPassword" required>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Colonne droite -->
|
||||||
|
<div class="col-8">
|
||||||
|
|
||||||
|
<!-- dateOfBirth -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Date de naissance</mat-label>
|
||||||
|
<input matInput type="date"
|
||||||
|
[ngModel] ="user.dateOfBirth | date:'yyyy-MM-dd'"
|
||||||
|
(ngModelChange)="user.dateOfBirth = $event">
|
||||||
|
</mat-form-field><br>
|
||||||
|
|
||||||
|
<!-- gender -->
|
||||||
|
<mat-radio-group [(ngModel)]="user.gender">
|
||||||
|
<mat-radio-button value="man"> Homme </mat-radio-button>
|
||||||
|
<mat-radio-button value="woman"> Femme </mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
<!-- interests -->
|
||||||
|
<app-input-interests-admin
|
||||||
|
[myInterests]="user.interests"
|
||||||
|
(eventEmitter)="onEventInputInterests($event)"></app-input-interests-admin>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ------------------------------------------------------------------------------------------------------------------- -->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- advertiserOrAdminBlock -->
|
||||||
|
<ng-template #advertiserOrAdminBlock>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- photo de profil -->
|
||||||
|
<div style="text-align: center">
|
||||||
|
<img [src]="user.profileImageUrl" onerror="this.onerror=null; this.src='assets/profil.png'"><br>
|
||||||
|
<input title="lien vers image" type="text" [(ngModel)]="user.profileImageUrl" style="width: 100%">
|
||||||
|
</div><br>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- email + login + mdp + confirmation -->
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<!-- email + login -->
|
||||||
|
<div class="col-6">
|
||||||
|
<!-- email -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Email</mat-label>
|
||||||
|
<input matInput type="text" [(ngModel)]="user.email" required>
|
||||||
|
</mat-form-field><br>
|
||||||
|
<!-- login -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Pseudo</mat-label>
|
||||||
|
<input matInput type="text" [(ngModel)]="user.login" required>
|
||||||
|
</mat-form-field><br>
|
||||||
|
<!-- company -->
|
||||||
|
<mat-form-field appearance="fill" *ngIf="user.role.name === 'advertiser'">
|
||||||
|
<mat-label>Entreprise</mat-label>
|
||||||
|
<input matInput type="text" [(ngModel)]="user.company">
|
||||||
|
</mat-form-field><br *ngIf="user.role.name === 'advertiser'">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- mdp + confirmation -->
|
||||||
|
<div class="col-6">
|
||||||
|
<!-- mot de passe -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Mot de passe</mat-label>
|
||||||
|
<input matInput type="password" [(ngModel)]="password" required>
|
||||||
|
</mat-form-field><br>
|
||||||
|
<!-- confirmation mot de passe -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Confirmation nouveau mot de passe</mat-label>
|
||||||
|
<input matInput type="password" [(ngModel)]="confirmPassword" required>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
.myContainer {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin: 0px 0px 10px 0px;
|
||||||
|
width: 10%;
|
||||||
|
height: 10%;
|
||||||
|
border: solid 2px black;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: xxx-large;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leftCol {
|
||||||
|
border-right: solid 1px #dcdcdc;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PopupCreateUserComponent } from './popup-create-user.component';
|
||||||
|
|
||||||
|
describe('PopupCreateUserComponent', () => {
|
||||||
|
let component: PopupCreateUserComponent;
|
||||||
|
let fixture: ComponentFixture<PopupCreateUserComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ PopupCreateUserComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PopupCreateUserComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
import {Component, Inject, OnInit} from '@angular/core';
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
import {MessageService} from "../../../utils/message/message.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-popup-create-user',
|
||||||
|
templateUrl: './popup-create-user.component.html',
|
||||||
|
styleUrls: ['./popup-create-user.component.scss']
|
||||||
|
})
|
||||||
|
export class PopupCreateUserComponent implements OnInit
|
||||||
|
{
|
||||||
|
user: any;
|
||||||
|
hasError: boolean = false;
|
||||||
|
errorMessage: string = "";
|
||||||
|
password: string = "";
|
||||||
|
confirmPassword: string = "";
|
||||||
|
|
||||||
|
|
||||||
|
constructor( public dialogRef: MatDialogRef<PopupCreateUserComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data,
|
||||||
|
private messageService: MessageService ) { }
|
||||||
|
|
||||||
|
|
||||||
|
// Initialise l'utilisateur qui va être créé
|
||||||
|
ngOnInit(): void
|
||||||
|
{
|
||||||
|
this.user = {
|
||||||
|
_id: "",
|
||||||
|
login: "",
|
||||||
|
hashPass: "",
|
||||||
|
email: "",
|
||||||
|
role: {
|
||||||
|
name: "",
|
||||||
|
permission: 0,
|
||||||
|
isAccepted: false,
|
||||||
|
},
|
||||||
|
profileImageUrl: "",
|
||||||
|
dateOfBirth: null,
|
||||||
|
gender: "man",
|
||||||
|
interests: [],
|
||||||
|
company: "",
|
||||||
|
isActive: false,
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
lastConnexion: new Date()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Crée le nouvel utilisateur
|
||||||
|
onEnregistrer(): void
|
||||||
|
{
|
||||||
|
this.checkField();
|
||||||
|
if(!this.hasError)
|
||||||
|
{
|
||||||
|
this.user.hashPass = this.password;
|
||||||
|
this.user.role = this.user.role.name;
|
||||||
|
this.messageService
|
||||||
|
.post("user/create", this.user)
|
||||||
|
.subscribe(ret => this.onEnregistrerCallback(ret), err => this.onEnregistrerCallback(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Callback de 'onEnregistrer'
|
||||||
|
onEnregistrerCallback(retour: any): void
|
||||||
|
{
|
||||||
|
if(retour.status !== "success") {
|
||||||
|
console.log(retour);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.dialogRef.close(retour.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check les champs saisies par l'utilisateur
|
||||||
|
checkField(): void
|
||||||
|
{
|
||||||
|
if(this.user.login.length === 0) {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ 'pseudo'.";
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(this.user.email.length === 0) {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ 'email'.";
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(!this.isValidEmail(this.user.email)) {
|
||||||
|
this.errorMessage = "Email invalide.";
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(this.password.length === 0) {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ 'mot de passe'.";
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(this.password !== this.confirmPassword) {
|
||||||
|
this.errorMessage = "Le mot de passe est différent de sa confirmation.";
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if((this.user.role.name === 'advertiser') && (this.user.company.length === 0)) {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ 'entreprise'.";
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.errorMessage = "" ;
|
||||||
|
this.hasError = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Indique si email a bien le format d'un email
|
||||||
|
isValidEmail(email): boolean
|
||||||
|
{
|
||||||
|
let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
return re.test(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Récupère la liste des centres d'intérets (car celle-ci est remplie à l'aide d'un component intermédiaire)
|
||||||
|
onEventInputInterests(myInterets: string[]): void
|
||||||
|
{
|
||||||
|
this.user.interests = myInterets;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
37
src/app/admin/utils/navbar-admin/navbar-admin.component.html
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
|
||||||
|
<!-- PolyNotFound -->
|
||||||
|
<a class="navbar-brand" routerLink="/admin/userList"> StreamNotFound </a>
|
||||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- [userList] [adList] -->
|
||||||
|
<div class="collapse navbar-collapse">
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item active monLi">
|
||||||
|
<a *ngIf="(url !== routes[0]) && (url !== routes[1])" [routerLink]="routes[1]" class="nav-link">Liste des utillisateurs</a>
|
||||||
|
<a *ngIf="(url === routes[0]) || (url === routes[1])" [routerLink]="routes[1]" class="nav-link myActiveLink">Liste des utillisateurs</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item active monLi">
|
||||||
|
<a *ngIf="url !== routes[2]" [routerLink]="routes[2]" class="nav-link">Liste des publicités</a>
|
||||||
|
<a *ngIf="url === routes[2]" [routerLink]="routes[2]" class="nav-link myActiveLink">Liste des publicités</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Mon profil -->
|
||||||
|
<img [src]=profilService.getProfileImageUrl()
|
||||||
|
onerror="this.onerror=null; this.src='assets/profil.png'"
|
||||||
|
[routerLink]="routes[3]"
|
||||||
|
alt="">
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Deconnexion -->
|
||||||
|
<button mat-button class="btnDeconnexion" (click)="onDeconnexion()" routerLink="/">
|
||||||
|
Déconnexion
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</nav>
|
||||||
80
src/app/admin/utils/navbar-admin/navbar-admin.component.scss
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
.navbar {
|
||||||
|
background-color: black;
|
||||||
|
height: 60px;
|
||||||
|
font-size: medium;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.navbar-expand-lg {
|
||||||
|
border-bottom: solid;
|
||||||
|
border-color: white;
|
||||||
|
border-bottom-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// PolyNotFound
|
||||||
|
.navbar-brand {
|
||||||
|
font-family: cursive;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: x-large;
|
||||||
|
margin-left: 15px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.monLi {
|
||||||
|
margin: 0px 10px 0px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.nav-link:hover {
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
|
.myActiveLink {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btnDeconnexion {
|
||||||
|
font-size: medium;
|
||||||
|
margin: 0px 10px 0px 10px
|
||||||
|
}
|
||||||
|
.btnDeconnexion:hover {
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
img {
|
||||||
|
border: solid 2px white;
|
||||||
|
border-radius: 50px;
|
||||||
|
margin: 0px 10px 0px 15px;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
img:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
::ng-deep .mat-slide-toggle-thumb {
|
||||||
|
background-color: #c8c8c8;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-slide-toggle-bar {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-thumb {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-bar {
|
||||||
|
background-color: #646464;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { NavbarAdminComponent } from './navbar-admin.component';
|
||||||
|
|
||||||
|
describe('NavbarAdminComponent', () => {
|
||||||
|
let component: NavbarAdminComponent;
|
||||||
|
let fixture: ComponentFixture<NavbarAdminComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ NavbarAdminComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(NavbarAdminComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
39
src/app/admin/utils/navbar-admin/navbar-admin.component.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import {Router} from "@angular/router";
|
||||||
|
import {ProfilService} from "../../../utils/profil/profil.service";
|
||||||
|
import {MessageService} from "../../../utils/message/message.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-navbar-admin',
|
||||||
|
templateUrl: './navbar-admin.component.html',
|
||||||
|
styleUrls: ['./navbar-admin.component.scss']
|
||||||
|
})
|
||||||
|
export class NavbarAdminComponent
|
||||||
|
{
|
||||||
|
routes: string[] = [
|
||||||
|
"/admin", // 0
|
||||||
|
"/admin/userList", // 1
|
||||||
|
"/admin/adList", // 2
|
||||||
|
"/admin/myProfil", // 3
|
||||||
|
];
|
||||||
|
|
||||||
|
url = this.router.url;
|
||||||
|
|
||||||
|
constructor( private router: Router,
|
||||||
|
public profilService: ProfilService,
|
||||||
|
private messageService: MessageService ) { }
|
||||||
|
|
||||||
|
onDeconnexion(): void
|
||||||
|
{
|
||||||
|
this.messageService
|
||||||
|
.delete('user/logout')
|
||||||
|
.subscribe(retour => this.onDeconnexionCallback(retour), err => this.onDeconnexionCallback(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeconnexionCallback(retour: any): void
|
||||||
|
{
|
||||||
|
if(retour.status !== "success") console.log(retour);
|
||||||
|
this.profilService.setId("");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
28
src/app/app-routing.module.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import {PageLoginComponent} from "./beforeConnexion/login/page-login/page-login.component";
|
||||||
|
import {PageRegisterComponent} from "./beforeConnexion/register/page-register/page-register.component";
|
||||||
|
import {PageAdListAdminComponent} from "./admin/adList/page-ad-list-admin/page-ad-list-admin.component";
|
||||||
|
import {PageProfilAdminComponent} from "./admin/myProfil/page-profil-admin/page-profil-admin.component";
|
||||||
|
import {PageUserListComponent} from "./admin/userList/page-user-list/page-user-list.component";
|
||||||
|
import {MyGuardGuard} from "./utils/my-guard/my-guard.guard";
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
|
||||||
|
// Before connexion
|
||||||
|
{ path: '', component: PageLoginComponent },
|
||||||
|
{ path: 'login', component: PageLoginComponent },
|
||||||
|
{ path: 'register', component: PageRegisterComponent },
|
||||||
|
|
||||||
|
// Admin
|
||||||
|
{ path: 'admin', component: PageUserListComponent, canActivate: [MyGuardGuard] },
|
||||||
|
{ path: 'admin/userList', component: PageUserListComponent, canActivate: [MyGuardGuard] },
|
||||||
|
{ path: 'admin/adList', component: PageAdListAdminComponent, canActivate: [MyGuardGuard] },
|
||||||
|
{ path: 'admin/myProfil', component: PageProfilAdminComponent, canActivate: [MyGuardGuard] },
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forRoot(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class AppRoutingModule { }
|
||||||
2
src/app/app.component.html
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
<router-outlet></router-outlet>
|
||||||
24
src/app/app.component.scss
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
::ng-deep snack-bar-container.custom-class {
|
||||||
|
//background: yellow;
|
||||||
|
}
|
||||||
|
::ng-deep .custom-class .mat-simple-snackbar {
|
||||||
|
//color: green;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// aura
|
||||||
|
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
|
||||||
|
background-color: grey !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// contenu coche
|
||||||
|
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||||
|
background-color: black !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// indeterminate
|
||||||
|
::ng-deep .mat-checkbox .mat-checkbox-frame {
|
||||||
|
border: solid 1px black !important;
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
35
src/app/app.component.spec.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
RouterTestingModule
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the app', () => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should have as title 'admin'`, () => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.componentInstance;
|
||||||
|
expect(app.title).toEqual('admin');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render title', () => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.nativeElement as HTMLElement;
|
||||||
|
expect(compiled.querySelector('.content span')?.textContent).toContain('admin app is running!');
|
||||||
|
});
|
||||||
|
});
|
||||||
10
src/app/app.component.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.scss']
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
title = 'admin';
|
||||||
|
}
|
||||||
95
src/app/app.module.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
import { PageLoginComponent } from './beforeConnexion/login/page-login/page-login.component';
|
||||||
|
import { PopupForgottenPasswordComponent } from './beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component';
|
||||||
|
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
|
||||||
|
import {HttpClientModule} from "@angular/common/http";
|
||||||
|
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||||
|
import {MatTableModule} from "@angular/material/table";
|
||||||
|
import {MatSortModule} from "@angular/material/sort";
|
||||||
|
import {MatRadioModule} from "@angular/material/radio";
|
||||||
|
import {MatDialogModule} from "@angular/material/dialog";
|
||||||
|
import {MatSnackBarModule} from "@angular/material/snack-bar";
|
||||||
|
import {MatButtonModule} from "@angular/material/button";
|
||||||
|
import {MatCheckboxModule} from "@angular/material/checkbox";
|
||||||
|
import { PageRegisterComponent } from './beforeConnexion/register/page-register/page-register.component';
|
||||||
|
import { PopupConfirmationComponent } from './beforeConnexion/register/popup-confirmation/popup-confirmation.component';
|
||||||
|
import { InputInterestsRegisterComponent } from './beforeConnexion/register/input-interests-register/input-interests-register.component';
|
||||||
|
import {MatFormFieldModule} from "@angular/material/form-field";
|
||||||
|
import {MatChipsModule} from "@angular/material/chips";
|
||||||
|
import {MatIconModule} from "@angular/material/icon";
|
||||||
|
import {MatAutocompleteModule} from "@angular/material/autocomplete";
|
||||||
|
import {MatStepperModule} from "@angular/material/stepper";
|
||||||
|
import { NavbarBeforeConnexionComponent } from './beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component';
|
||||||
|
import { PageProfilAdminComponent } from './admin/myProfil/page-profil-admin/page-profil-admin.component';
|
||||||
|
import { PopupUpdateAdminComponent } from './admin/myProfil/popup-update-admin/popup-update-admin.component';
|
||||||
|
import { PageAdListAdminComponent } from './admin/adList/page-ad-list-admin/page-ad-list-admin.component';
|
||||||
|
import { PopupDeleteAdAdminComponent } from './admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component';
|
||||||
|
import { PopupVisualizeImagesAdminComponent } from './admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component';
|
||||||
|
import {MatDividerModule} from "@angular/material/divider";
|
||||||
|
import {MatSelectModule} from "@angular/material/select";
|
||||||
|
import {MatPaginatorModule} from "@angular/material/paginator";
|
||||||
|
import {MatGridListModule} from "@angular/material/grid-list";
|
||||||
|
import { PageUserListComponent } from './admin/userList/page-user-list/page-user-list.component';
|
||||||
|
import { InputInterestsAdminComponent } from './admin/userList/input-interests-admin/input-interests-admin.component';
|
||||||
|
import { PopupCreateUserComponent } from './admin/userList/popup-create-user/popup-create-user.component';
|
||||||
|
import {MatSlideToggleModule} from "@angular/material/slide-toggle";
|
||||||
|
import { NavbarAdminComponent } from './admin/utils/navbar-admin/navbar-admin.component';
|
||||||
|
import {MatInputModule} from "@angular/material/input";
|
||||||
|
import {MatDatepickerModule} from "@angular/material/datepicker";
|
||||||
|
import {MAT_DATE_LOCALE, MatNativeDateModule} from "@angular/material/core";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent,
|
||||||
|
PageLoginComponent,
|
||||||
|
PopupForgottenPasswordComponent,
|
||||||
|
PageRegisterComponent,
|
||||||
|
PopupConfirmationComponent,
|
||||||
|
InputInterestsRegisterComponent,
|
||||||
|
NavbarBeforeConnexionComponent,
|
||||||
|
PageProfilAdminComponent,
|
||||||
|
PopupUpdateAdminComponent,
|
||||||
|
PageAdListAdminComponent,
|
||||||
|
PopupDeleteAdAdminComponent,
|
||||||
|
PopupVisualizeImagesAdminComponent,
|
||||||
|
PageUserListComponent,
|
||||||
|
InputInterestsAdminComponent,
|
||||||
|
PopupCreateUserComponent,
|
||||||
|
NavbarAdminComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
AppRoutingModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatSortModule,
|
||||||
|
MatRadioModule,
|
||||||
|
FormsModule,
|
||||||
|
HttpClientModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatSnackBarModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatChipsModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatAutocompleteModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
MatStepperModule,
|
||||||
|
MatDividerModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
MatGridListModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatDatepickerModule,
|
||||||
|
MatNativeDateModule,
|
||||||
|
],
|
||||||
|
providers: [{ provide: MAT_DATE_LOCALE, useValue: 'en-GB' }],
|
||||||
|
bootstrap: [AppComponent]
|
||||||
|
})
|
||||||
|
export class AppModule { }
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<div [class]="themeService.getClassTheme()">
|
||||||
|
<div class="bg">
|
||||||
|
|
||||||
|
<app-navbar-before-connexion pour="login"></app-navbar-before-connexion>
|
||||||
|
|
||||||
|
<div class="wrapper fadeInDown">
|
||||||
|
<div id="formContent">
|
||||||
|
|
||||||
|
<!-- Icon -->
|
||||||
|
<div class="fadeIn first">
|
||||||
|
<h1 style="font-family: cursive; font-size: 45px; margin-top: 20px; margin-bottom: 10px">StreamNotFound</h1>
|
||||||
|
<img src="../../../../assets/logo.png" id="icon" alt="User Icon" style="margin-top: 10px"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Login Form -->
|
||||||
|
<form>
|
||||||
|
<input [(ngModel)]="email" type="text" id="email" class="fadeIn second" name="email" placeholder="Email">
|
||||||
|
<input [(ngModel)]="password" type="password" id="password" class="fadeIn third" name="password" placeholder="Mot de passe">
|
||||||
|
<!-- Message d'erreur -->
|
||||||
|
<div *ngIf="hasError" style="text-align: center; margin-bottom: 20px;">
|
||||||
|
<span class="mat-error"> {{errorMessage}} </span>
|
||||||
|
</div>
|
||||||
|
<input type="submit" class="fadeIn fourth" value="Se connecter" (click)="onSeConnecter()">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Oubli mot de passe -->
|
||||||
|
<div id="formFooter">
|
||||||
|
<a class="underlineHover" (click)="onForgottenPassword()">J'ai oublié mon mot de passe</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,271 @@
|
||||||
|
html {
|
||||||
|
background-color: #56baed;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Poppins", sans-serif;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #5E89FF;
|
||||||
|
display:inline-block;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
display:inline-block;
|
||||||
|
margin: 40px 8px 10px 8px;
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* STRUCTURE */
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 80%;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#formContent {
|
||||||
|
-webkit-border-radius: 10px 10px 10px 10px;
|
||||||
|
border-radius: 10px 10px 10px 10px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 30px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 450px;
|
||||||
|
position: relative;
|
||||||
|
padding: 0px;
|
||||||
|
-webkit-box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3);
|
||||||
|
box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#formFooter {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
border-top: 1px solid #dce8f1;
|
||||||
|
padding: 25px;
|
||||||
|
text-align: center;
|
||||||
|
-webkit-border-radius: 0 0 10px 10px;
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* TABS */
|
||||||
|
|
||||||
|
h2.inactive {
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2.active {
|
||||||
|
color: #0d0d0d;
|
||||||
|
border-bottom: 2px solid #5fbae9;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* FORM TYPOGRAPHY*/
|
||||||
|
|
||||||
|
input[type=button], input[type=submit], input[type=reset] {
|
||||||
|
background-color: #5E89FF;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
padding: 15px 80px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 13px;
|
||||||
|
-webkit-box-shadow: 0 10px 30px 0 rgba(95,186,233,0.4);
|
||||||
|
box-shadow: 0 10px 30px 0 rgba(95,186,233,0.4);
|
||||||
|
-webkit-border-radius: 5px 5px 5px 5px;
|
||||||
|
border-radius: 5px 5px 5px 5px;
|
||||||
|
margin: 5px 20px 40px 20px;
|
||||||
|
-webkit-transition: all 0.3s ease-in-out;
|
||||||
|
-moz-transition: all 0.3s ease-in-out;
|
||||||
|
-ms-transition: all 0.3s ease-in-out;
|
||||||
|
-o-transition: all 0.3s ease-in-out;
|
||||||
|
transition: all 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button]:hover, input[type=submit]:hover, input[type=reset]:hover {
|
||||||
|
background-color: #39ace7;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=button]:active, input[type=submit]:active, input[type=reset]:active {
|
||||||
|
-moz-transform: scale(0.95);
|
||||||
|
-webkit-transform: scale(0.95);
|
||||||
|
-o-transform: scale(0.95);
|
||||||
|
-ms-transform: scale(0.95);
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text], input[type=password] {
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
border: none;
|
||||||
|
color: #0d0d0d;
|
||||||
|
padding: 15px 32px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 5px;
|
||||||
|
width: 85%;
|
||||||
|
border: 2px solid #f6f6f6;
|
||||||
|
-webkit-transition: all 0.5s ease-in-out;
|
||||||
|
-moz-transition: all 0.5s ease-in-out;
|
||||||
|
-ms-transition: all 0.5s ease-in-out;
|
||||||
|
-o-transition: all 0.5s ease-in-out;
|
||||||
|
transition: all 0.5s ease-in-out;
|
||||||
|
-webkit-border-radius: 5px 5px 5px 5px;
|
||||||
|
border-radius: 5px 5px 5px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
input[type=text]:focus, input[type=password]:focus {
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 2px solid #5fbae9;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=text]::placeholder, input[type=password]::placeholder {
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
overflow-y: hidden;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ANIMATIONS */
|
||||||
|
|
||||||
|
/* Simple CSS3 Fade-in-down Animation */
|
||||||
|
.fadeInDown {
|
||||||
|
-webkit-animation-name: fadeInDown;
|
||||||
|
animation-name: fadeInDown;
|
||||||
|
-webkit-animation-duration: 1s;
|
||||||
|
animation-duration: 1s;
|
||||||
|
-webkit-animation-fill-mode: both;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes fadeInDown {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translate3d(0, -100%, 0);
|
||||||
|
transform: translate3d(0, -100%, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: none;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInDown {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
-webkit-transform: translate3d(0, -100%, 0);
|
||||||
|
transform: translate3d(0, -100%, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transform: none;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple CSS3 Fade-in Animation */
|
||||||
|
@-webkit-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
||||||
|
@-moz-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
||||||
|
@keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
||||||
|
|
||||||
|
.fadeIn {
|
||||||
|
opacity:0;
|
||||||
|
-webkit-animation:fadeIn ease-in 1;
|
||||||
|
-moz-animation:fadeIn ease-in 1;
|
||||||
|
animation:fadeIn ease-in 1;
|
||||||
|
|
||||||
|
-webkit-animation-fill-mode:forwards;
|
||||||
|
-moz-animation-fill-mode:forwards;
|
||||||
|
animation-fill-mode:forwards;
|
||||||
|
|
||||||
|
-webkit-animation-duration:1s;
|
||||||
|
-moz-animation-duration:1s;
|
||||||
|
animation-duration:1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadeIn.first {
|
||||||
|
-webkit-animation-delay: 0.4s;
|
||||||
|
-moz-animation-delay: 0.4s;
|
||||||
|
animation-delay: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadeIn.second {
|
||||||
|
-webkit-animation-delay: 0.6s;
|
||||||
|
-moz-animation-delay: 0.6s;
|
||||||
|
animation-delay: 0.6s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadeIn.third {
|
||||||
|
-webkit-animation-delay: 0.8s;
|
||||||
|
-moz-animation-delay: 0.8s;
|
||||||
|
animation-delay: 0.8s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadeIn.fourth {
|
||||||
|
-webkit-animation-delay: 1s;
|
||||||
|
-moz-animation-delay: 1s;
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple CSS3 Fade-in Animation */
|
||||||
|
.underlineHover:after {
|
||||||
|
display: block;
|
||||||
|
left: 0;
|
||||||
|
bottom: -10px;
|
||||||
|
width: 0;
|
||||||
|
height: 2px;
|
||||||
|
//background-color: #5E89FF;
|
||||||
|
background-color: #5E89FF;
|
||||||
|
content: "";
|
||||||
|
transition: width 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.underlineHover:hover {
|
||||||
|
color: #0d0d0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.underlineHover:hover:after{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1{
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OTHERS */
|
||||||
|
|
||||||
|
*:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#icon {
|
||||||
|
width:30%;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PageLoginComponent } from './page-login.component';
|
||||||
|
|
||||||
|
describe('PageLoginComponent', () => {
|
||||||
|
let component: PageLoginComponent;
|
||||||
|
let fixture: ComponentFixture<PageLoginComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ PageLoginComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PageLoginComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
101
src/app/beforeConnexion/login/page-login/page-login.component.ts
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {Router} from "@angular/router";
|
||||||
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
|
import {PopupForgottenPasswordComponent} from "../popup-forgotten-password/popup-forgotten-password.component";
|
||||||
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
|
import {ProfilService} from "../../../utils/profil/profil.service";
|
||||||
|
import {MessageService} from "../../../utils/message/message.service";
|
||||||
|
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-page-login',
|
||||||
|
templateUrl: './page-login.component.html',
|
||||||
|
styleUrls: ['./page-login.component.scss']
|
||||||
|
})
|
||||||
|
export class PageLoginComponent implements OnInit
|
||||||
|
{
|
||||||
|
email: string = "" ;
|
||||||
|
password: string = "" ;
|
||||||
|
hasError: boolean = false;
|
||||||
|
errorMessage: string = "";
|
||||||
|
|
||||||
|
|
||||||
|
constructor( private messageService: MessageService,
|
||||||
|
private router: Router,
|
||||||
|
public themeService: ThemeService,
|
||||||
|
public dialog: MatDialog,
|
||||||
|
private snackBar: MatSnackBar,
|
||||||
|
private profilService: ProfilService) { }
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
|
||||||
|
|
||||||
|
onSeConnecter(): void
|
||||||
|
{
|
||||||
|
this.checkError();
|
||||||
|
|
||||||
|
if(!this.hasError)
|
||||||
|
{
|
||||||
|
let data = {
|
||||||
|
email: this.email,
|
||||||
|
hashPass: this.password
|
||||||
|
};
|
||||||
|
this.messageService
|
||||||
|
.post('user/auth', data)
|
||||||
|
.subscribe( retour => this.onSeConnecterCallback(retour), err => this.onSeConnecterCallback(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onSeConnecterCallback(retour: any): void
|
||||||
|
{
|
||||||
|
if(retour.status !== "success") {
|
||||||
|
console.log(retour);
|
||||||
|
this.errorMessage = retour.error.reason;
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.profilService.setId(retour.data.id);
|
||||||
|
this.profilService.setProfileImageUrl(retour.data.profileImageUrl);
|
||||||
|
if(retour.data.role.name === "user") this.router.navigateByUrl( '/user/search');
|
||||||
|
else if(retour.data.role.name === "advertiser") this.router.navigateByUrl( '/advertiser/adList');
|
||||||
|
else if(retour.data.role.name === "admin" || retour.data.role.name === "superAdmin") this.router.navigateByUrl( '/admin/userList');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onForgottenPassword(): void
|
||||||
|
{
|
||||||
|
this.dialog
|
||||||
|
.open(PopupForgottenPasswordComponent, {width: '30%'})
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe(result => {
|
||||||
|
if((result !== null) && (result !== undefined))
|
||||||
|
{
|
||||||
|
const config = { duration: 5000, panelClass: "custom-class" };
|
||||||
|
this.snackBar.open( "Un mail de réinitialisation de mot de passe vous a été envoyé.", "", config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
checkError(): void
|
||||||
|
{
|
||||||
|
if(this.email === "") {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ email" ;
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(this.password === "") {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ mot de passe" ;
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.errorMessage = "" ;
|
||||||
|
this.hasError = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<h4>Récupération du mot de passe</h4>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider><br>
|
||||||
|
|
||||||
|
<!-- email -->
|
||||||
|
<div class="myDiv">
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Email</mat-label>
|
||||||
|
<input matInput type="email" [(ngModel)]="email" required>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- message d'erreur -->
|
||||||
|
<div class="myError" *ngIf="hasError">
|
||||||
|
<span class="mat-error">{{errorMessage}}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-divider></mat-divider>
|
||||||
|
|
||||||
|
<!-- boutons -->
|
||||||
|
<mat-dialog-actions align="end">
|
||||||
|
<button mat-button (click)="dialogRef.close()">Annuler</button>
|
||||||
|
<button mat-button (click)="onValidate()" cdkFocusInitial>Valider</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
h4 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.myDiv {
|
||||||
|
text-align: center;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.myError {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PopupForgottenPasswordComponent } from './popup-forgotten-password.component';
|
||||||
|
|
||||||
|
describe('PopupForgottenPasswordComponent', () => {
|
||||||
|
let component: PopupForgottenPasswordComponent;
|
||||||
|
let fixture: ComponentFixture<PopupForgottenPasswordComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ PopupForgottenPasswordComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PopupForgottenPasswordComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import {MatDialogRef} from "@angular/material/dialog";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-popup-forgotten-password',
|
||||||
|
templateUrl: './popup-forgotten-password.component.html',
|
||||||
|
styleUrls: ['./popup-forgotten-password.component.scss']
|
||||||
|
})
|
||||||
|
export class PopupForgottenPasswordComponent
|
||||||
|
{
|
||||||
|
email: string;
|
||||||
|
hasError: boolean = false;
|
||||||
|
errorMessage: string = "";
|
||||||
|
|
||||||
|
|
||||||
|
constructor(public dialogRef: MatDialogRef<PopupForgottenPasswordComponent>) {}
|
||||||
|
|
||||||
|
|
||||||
|
// Click sur valider
|
||||||
|
onValidate()
|
||||||
|
{
|
||||||
|
if(this.email.length === 0) {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ 'email'." ;
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(!this.isValidEmail(this.email)) {
|
||||||
|
this.errorMessage = "Email invalide." ;
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.errorMessage = "" ;
|
||||||
|
this.hasError = false;
|
||||||
|
this.dialogRef.close(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Indique si email a bien le format d'un email
|
||||||
|
isValidEmail(email): boolean
|
||||||
|
{
|
||||||
|
let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
return re.test(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<mat-form-field class="example-chip-list" appearance="fill">
|
||||||
|
|
||||||
|
<!-- ------------------------------------------------------------------------------------ -->
|
||||||
|
|
||||||
|
<mat-label>Centres d'intérêt</mat-label>
|
||||||
|
|
||||||
|
<!-- ------------------------------------------------------------------------------------ -->
|
||||||
|
|
||||||
|
<mat-chip-list #chipList aria-label="Fruit selection">
|
||||||
|
|
||||||
|
<mat-chip
|
||||||
|
*ngFor="let interest of myInterests"
|
||||||
|
[selectable]="selectable"
|
||||||
|
[removable]="removable"
|
||||||
|
(removed)="remove(interest)">
|
||||||
|
{{interest}}
|
||||||
|
<button matChipRemove *ngIf="removable">
|
||||||
|
<mat-icon>cancel</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-chip>
|
||||||
|
|
||||||
|
<input
|
||||||
|
placeholder="Tapez un centre d'intérêt et pressez 'Entrer' pour l'inserer"
|
||||||
|
#tagInput
|
||||||
|
[formControl]="formControl"
|
||||||
|
[matAutocomplete]="auto"
|
||||||
|
[matChipInputFor]="chipList"
|
||||||
|
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||||
|
(matChipInputTokenEnd)="add($event)">
|
||||||
|
|
||||||
|
</mat-chip-list>
|
||||||
|
|
||||||
|
<!-- ------------------------------------------------------------------------------------ -->
|
||||||
|
|
||||||
|
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
|
||||||
|
<mat-option *ngFor="let interest of filteredInterests | async" [value]="interest">
|
||||||
|
{{interest}}
|
||||||
|
</mat-option>
|
||||||
|
</mat-autocomplete>
|
||||||
|
|
||||||
|
<!-- ------------------------------------------------------------------------------------ -->
|
||||||
|
|
||||||
|
</mat-form-field>
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { InputInterestsRegisterComponent } from './input-interests-register.component';
|
||||||
|
|
||||||
|
describe('InputInterestsRegisterComponent', () => {
|
||||||
|
let component: InputInterestsRegisterComponent;
|
||||||
|
let fixture: ComponentFixture<InputInterestsRegisterComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ InputInterestsRegisterComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(InputInterestsRegisterComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
|
||||||
|
import {COMMA, ENTER} from "@angular/cdk/keycodes";
|
||||||
|
import {FormControl} from "@angular/forms";
|
||||||
|
import {Observable} from "rxjs";
|
||||||
|
import {map, startWith} from "rxjs/operators";
|
||||||
|
import {MatChipInputEvent} from "@angular/material/chips";
|
||||||
|
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
|
||||||
|
import {MessageService} from "../../../utils/message/message.service";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-input-interests-register',
|
||||||
|
templateUrl: './input-interests-register.component.html',
|
||||||
|
styleUrls: ['./input-interests-register.component.scss']
|
||||||
|
})
|
||||||
|
export class InputInterestsRegisterComponent implements OnInit
|
||||||
|
{
|
||||||
|
selectable = true;
|
||||||
|
removable = true;
|
||||||
|
separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||||
|
formControl = new FormControl();
|
||||||
|
filteredInterests: Observable<string[]>;
|
||||||
|
@Input() myInterests: string[] = [];
|
||||||
|
allInterests: string[] = [];
|
||||||
|
@Output() eventEmitter = new EventEmitter<string[]>();
|
||||||
|
@ViewChild('tagInput') tagInput: ElementRef<HTMLInputElement>;
|
||||||
|
interestsNotSelected: string[] = [];
|
||||||
|
|
||||||
|
|
||||||
|
constructor( private messageService: MessageService ) {}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnInit(): void
|
||||||
|
{
|
||||||
|
this.filteredInterests = this.formControl.valueChanges.pipe(
|
||||||
|
startWith(null),
|
||||||
|
map((fruit: string | null) => fruit ? this._filter(fruit) : this.interestsNotSelected.slice()));
|
||||||
|
|
||||||
|
this.messageService
|
||||||
|
.get("misc/getInterests")
|
||||||
|
.subscribe( retour => {
|
||||||
|
|
||||||
|
if(retour.status !== "success") {
|
||||||
|
console.log(retour);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.allInterests = [];
|
||||||
|
for(let elt of retour.data)
|
||||||
|
{
|
||||||
|
this.allInterests.push(elt.interest);
|
||||||
|
this.interestsNotSelected.push(elt.interest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
add(event: MatChipInputEvent): void
|
||||||
|
{
|
||||||
|
const value = (event.value || '').trim();
|
||||||
|
const index = this.interestsNotSelected.indexOf(value);
|
||||||
|
if (value && (index !== -1) && (!this.myInterests.includes(value)))
|
||||||
|
{
|
||||||
|
this.myInterests.push(value);
|
||||||
|
event.chipInput!.clear();
|
||||||
|
this.formControl.setValue(null);
|
||||||
|
this.eventEmitter.emit(this.myInterests);
|
||||||
|
this.interestsNotSelected.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
remove(interest: string): void
|
||||||
|
{
|
||||||
|
// supprimer 'interest' de 'myInterest'
|
||||||
|
const index = this.myInterests.indexOf(interest);
|
||||||
|
if (index >= 0) this.myInterests.splice(index, 1);
|
||||||
|
this.eventEmitter.emit(this.myInterests);
|
||||||
|
|
||||||
|
// remmettre 'interest' dans 'interestsNotSelected'
|
||||||
|
if(!this.interestsNotSelected.includes(interest))
|
||||||
|
{
|
||||||
|
const indexOfAutres = this.interestsNotSelected.indexOf("Autres");
|
||||||
|
if(indexOfAutres !== -1)
|
||||||
|
{
|
||||||
|
this.interestsNotSelected.splice(indexOfAutres, 1);
|
||||||
|
if(interest !== "Autres") this.interestsNotSelected.push(interest);
|
||||||
|
this.interestsNotSelected.sort();
|
||||||
|
this.interestsNotSelected.push("Autres");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.interestsNotSelected.push(interest);
|
||||||
|
if(interest !== "Autres") this.interestsNotSelected.sort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
selected(event: MatAutocompleteSelectedEvent): void
|
||||||
|
{
|
||||||
|
const value = event.option.viewValue;
|
||||||
|
if(!this.myInterests.includes(value))
|
||||||
|
{
|
||||||
|
this.myInterests.push(value);
|
||||||
|
const index = this.interestsNotSelected.indexOf(value);
|
||||||
|
this.interestsNotSelected.splice(index, 1);
|
||||||
|
}
|
||||||
|
this.tagInput.nativeElement.value = '';
|
||||||
|
this.formControl.setValue(null);
|
||||||
|
this.eventEmitter.emit(this.myInterests);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private _filter(value: string): string[]
|
||||||
|
{
|
||||||
|
const filterValue = value.toLowerCase();
|
||||||
|
return this.interestsNotSelected.filter(fruit => fruit.toLowerCase().includes(filterValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
<div [class]="themeService.getClassTheme()">
|
||||||
|
<div class="myContainer">
|
||||||
|
|
||||||
|
<!-- navbar -->
|
||||||
|
<app-navbar-before-connexion pour="register"></app-navbar-before-connexion>
|
||||||
|
|
||||||
|
<!-- stepper -->
|
||||||
|
<mat-stepper [linear]=true>
|
||||||
|
|
||||||
|
<!-- Choix du role -->
|
||||||
|
<mat-step label="Type de compte">
|
||||||
|
<form style="margin-top: 10px">
|
||||||
|
<!-- Choix du rôle -->
|
||||||
|
<mat-radio-group [(ngModel)]="user.role.name" [ngModelOptions]="{standalone: true}" (click)="hasError = false; errorMessage = '';">
|
||||||
|
<mat-radio-button value="user">Utilisateur standard</mat-radio-button>
|
||||||
|
<mat-radio-button value="advertiser">Annonceur</mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
|
||||||
|
<!-- Bouton suivant -->
|
||||||
|
<div style="text-align: right;">
|
||||||
|
<button mat-button matStepperNext>Suivant</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Infos personelles -->
|
||||||
|
<mat-step label="Compte & Informations personelles">
|
||||||
|
<form style="margin-top: 10px">
|
||||||
|
<!-- Redirection vers le bon bloc -->
|
||||||
|
<ng-template *ngIf="user.role.name==='user'; then userBlock else advertiserBlock"></ng-template>
|
||||||
|
|
||||||
|
<!-- Message d'erreur -->
|
||||||
|
<div *ngIf="hasError">
|
||||||
|
<mat-error>{{errorMessage}}</mat-error>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Boutons-->
|
||||||
|
<div style="text-align: right;">
|
||||||
|
<button mat-button matStepperPrevious>Précédent</button>
|
||||||
|
<button mat-button (click)="onEnregistrer()">Enregistrer</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</mat-step>
|
||||||
|
|
||||||
|
</mat-stepper>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- -------------------------------------------------------------------------------------------------------- -->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Info personnelles user -->
|
||||||
|
<ng-template #userBlock>
|
||||||
|
<br>
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<!-- Colonne gauche -->
|
||||||
|
<div class="col-5 leftCol">
|
||||||
|
<h4>Compte</h4>
|
||||||
|
|
||||||
|
<!-- Login -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Pseudo</mat-label>
|
||||||
|
<input matInput type="text" [(ngModel)]="user.login" [ngModelOptions]="{standalone: true}" required>
|
||||||
|
</mat-form-field>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<!-- Mot de passe -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Mot de passe</mat-label>
|
||||||
|
<input matInput type="password" [(ngModel)]="password" [ngModelOptions]="{standalone: true}" required>
|
||||||
|
</mat-form-field>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<!-- Confirmation mot de passe -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Confirmation mot de passe</mat-label>
|
||||||
|
<input matInput type="password" [(ngModel)]="confirmPassword" [ngModelOptions]="{standalone: true}" required>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Colonne droite -->
|
||||||
|
<div class="col-7">
|
||||||
|
<h4>Informations personelles</h4>
|
||||||
|
|
||||||
|
<!-- Email -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Email</mat-label>
|
||||||
|
<input matInput type="email" [(ngModel)]="user.email" [ngModelOptions]="{standalone: true}" required>
|
||||||
|
</mat-form-field>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<!-- gender -->
|
||||||
|
<mat-radio-group [(ngModel)]="user.gender" [ngModelOptions]="{standalone: true}">
|
||||||
|
<mat-radio-button value="man"> Homme </mat-radio-button>
|
||||||
|
<mat-radio-button value="woman"> Femme </mat-radio-button>
|
||||||
|
</mat-radio-group>
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
<!-- dateOfBirth -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Date de naissance</mat-label>
|
||||||
|
<input matInput type="date"
|
||||||
|
[ngModel] ="user.dateOfBirth | date:'yyyy-MM-dd'"
|
||||||
|
(ngModelChange)="user.dateOfBirth = $event"
|
||||||
|
[ngModelOptions]="{standalone: true}">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<!-- interests -->
|
||||||
|
<app-input-interests-register
|
||||||
|
[myInterests]="user.interests"
|
||||||
|
(eventEmitter)="onEventInputInterests($event)"></app-input-interests-register>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- -------------------------------------------------------------------------------------------------------- -->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Info personnelles advertiser -->
|
||||||
|
<ng-template #advertiserBlock>
|
||||||
|
|
||||||
|
<!-- Entreprise -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Entreprise</mat-label>
|
||||||
|
<input matInput type="text" [(ngModel)]="user.company" [ngModelOptions]="{standalone: true}" required>
|
||||||
|
</mat-form-field>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<!-- Login -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Pseudo</mat-label>
|
||||||
|
<input matInput type="text" [(ngModel)]="user.login" [ngModelOptions]="{standalone: true}" required>
|
||||||
|
</mat-form-field>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<!-- Email -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Email</mat-label>
|
||||||
|
<input matInput type="email" [(ngModel)]="user.email" [ngModelOptions]="{standalone: true}" required>
|
||||||
|
</mat-form-field>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<!-- Mot de passe -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Mot de passe</mat-label>
|
||||||
|
<input matInput type="password" [(ngModel)]="password" [ngModelOptions]="{standalone: true}" required>
|
||||||
|
</mat-form-field>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<!-- Confirmation mot de passe -->
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Confirmation mot de passe</mat-label>
|
||||||
|
<input matInput type="password" [(ngModel)]="confirmPassword" [ngModelOptions]="{standalone: true}" required>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
</ng-template>
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
.myContainer {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mat-stepper {
|
||||||
|
width: 60%;
|
||||||
|
margin: 10vh auto;
|
||||||
|
border: solid 1px black;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.leftCol {
|
||||||
|
border-right: solid 1px #dcdcdc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.myRow {
|
||||||
|
margin: 15px 0px 15px 0px;
|
||||||
|
}
|
||||||
|
.myLabel {
|
||||||
|
text-align: right;
|
||||||
|
padding: 0px 5px 0px 0px;
|
||||||
|
margin: 0px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.myValue {
|
||||||
|
text-align: left;
|
||||||
|
padding: 0px 0px 0px 5px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
::ng-deep .mat-radio-inner-circle {
|
||||||
|
color: black !important;
|
||||||
|
background-color: black !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-radio-outer-circle{
|
||||||
|
color: black !important;
|
||||||
|
border: solid 1px gray !important;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PageRegisterComponent } from './page-register.component';
|
||||||
|
|
||||||
|
describe('PageRegisterComponent', () => {
|
||||||
|
let component: PageRegisterComponent;
|
||||||
|
let fixture: ComponentFixture<PageRegisterComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ PageRegisterComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PageRegisterComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import {PopupConfirmationComponent} from "../popup-confirmation/popup-confirmation.component";
|
||||||
|
import {MessageService} from "../../../utils/message/message.service";
|
||||||
|
import {Router} from "@angular/router";
|
||||||
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
|
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-page-register',
|
||||||
|
templateUrl: './page-register.component.html',
|
||||||
|
styleUrls: ['./page-register.component.scss']
|
||||||
|
})
|
||||||
|
export class PageRegisterComponent
|
||||||
|
{
|
||||||
|
password: string = "";
|
||||||
|
confirmPassword: string = "";
|
||||||
|
hasError: boolean = false;
|
||||||
|
errorMessage: string = "";
|
||||||
|
user = {
|
||||||
|
_id: "",
|
||||||
|
login: "",
|
||||||
|
hashPass: "",
|
||||||
|
email: "",
|
||||||
|
role: {
|
||||||
|
name: "user",
|
||||||
|
permission: 0,
|
||||||
|
isAccepted: false,
|
||||||
|
},
|
||||||
|
profileImageUrl: "",
|
||||||
|
dateOfBirth: null,
|
||||||
|
gender: "man",
|
||||||
|
interests: [],
|
||||||
|
company: "",
|
||||||
|
isActive: true,
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
lastConnexion: null
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
constructor( private messageService: MessageService,
|
||||||
|
private router: Router,
|
||||||
|
public dialog: MatDialog,
|
||||||
|
public themeService: ThemeService ) { }
|
||||||
|
|
||||||
|
|
||||||
|
// Envoie de l'utilisateur au backend
|
||||||
|
onEnregistrer(): void
|
||||||
|
{
|
||||||
|
this.checkField();
|
||||||
|
if(!this.hasError)
|
||||||
|
{
|
||||||
|
let data: any = Object.assign({}, this.user);
|
||||||
|
if(this.user.role.name === "user") data.role = "user" ;
|
||||||
|
else data.role = "advertiser";
|
||||||
|
data.hashPass = this.password;
|
||||||
|
this.messageService
|
||||||
|
.post('user/create', data)
|
||||||
|
.subscribe(retour => this.onEnregistrerCallback(retour), err => this.onEnregistrerCallback(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Gestion de la réponse du backend
|
||||||
|
onEnregistrerCallback(retour): void
|
||||||
|
{
|
||||||
|
if(retour.status !== "success") {
|
||||||
|
console.log(retour);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const config = {
|
||||||
|
width: '25%',
|
||||||
|
data: {roleName: this.user.role.name}
|
||||||
|
};
|
||||||
|
this.dialog
|
||||||
|
.open(PopupConfirmationComponent, config)
|
||||||
|
.afterClosed()
|
||||||
|
.subscribe(result => this.router.navigateByUrl( '/login' ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check les champs saisies par l'utilisateur
|
||||||
|
checkField(): void
|
||||||
|
{
|
||||||
|
if((this.user.role.name === 'advertiser') && (this.user.company.length === 0)) {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ 'entreprise'.";
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(this.user.login.length === 0) {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ 'pseudo'.";
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(this.user.email.length === 0) {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ 'email'.";
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(!this.isValidEmail(this.user.email)) {
|
||||||
|
this.errorMessage = "Email invalide.";
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(this.password.length === 0) {
|
||||||
|
this.errorMessage = "Veuillez remplir le champ 'mot de passe'.";
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else if(this.password !== this.confirmPassword) {
|
||||||
|
this.errorMessage = "Le mot de passe est différent de sa confirmation.";
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.errorMessage = "" ;
|
||||||
|
this.hasError = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Indique si email a bien le format d'un email
|
||||||
|
isValidEmail(email): boolean
|
||||||
|
{
|
||||||
|
let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
return re.test(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Récupère la liste des centres d'intérets (car celle-ci est remplie à l'aide d'un component intermédiaire)
|
||||||
|
onEventInputInterests(myInterets: string[]): void
|
||||||
|
{
|
||||||
|
this.user.interests = myInterets;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<p *ngIf="data.roleName === 'user'">
|
||||||
|
Votre inscription a bien été effectuée.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p *ngIf="data.roleName === 'advertiser'">
|
||||||
|
Votre inscription est en cours de validation.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div style="text-align: right">
|
||||||
|
<button mat-button mat-dialog-close> Continuer </button>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
p {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { PopupConfirmationComponent } from './popup-confirmation.component';
|
||||||
|
|
||||||
|
describe('PopupConfirmationComponent', () => {
|
||||||
|
let component: PopupConfirmationComponent;
|
||||||
|
let fixture: ComponentFixture<PopupConfirmationComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ PopupConfirmationComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PopupConfirmationComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import {Component, Inject} from '@angular/core';
|
||||||
|
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-popup-confirmation',
|
||||||
|
templateUrl: './popup-confirmation.component.html',
|
||||||
|
styleUrls: ['./popup-confirmation.component.scss']
|
||||||
|
})
|
||||||
|
export class PopupConfirmationComponent
|
||||||
|
{
|
||||||
|
constructor( public dialogRef: MatDialogRef<PopupConfirmationComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data) {}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
<!-- Login -->
|
||||||
|
<div *ngIf="pour === 'login'">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
|
||||||
|
<!-- PolyNotFound -->
|
||||||
|
<a class="navbar-brand" routerLink=""> StreamNotFound </a>
|
||||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Rien -->
|
||||||
|
<div class="collapse navbar-collapse"></div>
|
||||||
|
|
||||||
|
<!-- S'inscrire -->
|
||||||
|
<button mat-button class="btnDeconnexion" routerLink="/register">
|
||||||
|
S'inscrire
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- --------------------------------------------------------------------------------------------------------- -->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Register -->
|
||||||
|
<div *ngIf="pour === 'register'">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
|
||||||
|
<!-- PolyNotFound -->
|
||||||
|
<a class="navbar-brand" routerLink=""> StreamNotFound </a>
|
||||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Rien -->
|
||||||
|
<div class="collapse navbar-collapse"></div>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
.navbar {
|
||||||
|
background-color: black;
|
||||||
|
height: 60px;
|
||||||
|
font-size: medium;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.navbar-expand-lg {
|
||||||
|
border-bottom: solid;
|
||||||
|
border-color: white;
|
||||||
|
border-bottom-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// PolyNotFound
|
||||||
|
.navbar-brand {
|
||||||
|
font-family: cursive;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: x-large;
|
||||||
|
margin-left: 15px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Recherche, Mes Playlists, Historique
|
||||||
|
.nav-link {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.nav-link:hover {
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Bonton deconnexion
|
||||||
|
.btnDeconnexion {
|
||||||
|
font-size: medium;
|
||||||
|
margin: 0px 10px 0px 10px
|
||||||
|
}
|
||||||
|
.btnDeconnexion:hover {
|
||||||
|
color: grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.monLi {
|
||||||
|
margin: 0px 10px 0px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
img {
|
||||||
|
border: solid 2px white;
|
||||||
|
border-radius: 50px;
|
||||||
|
margin: 0px 10px 0px 15px;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
img:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
::ng-deep .mat-slide-toggle-thumb {
|
||||||
|
background-color: #c8c8c8;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-slide-toggle-bar {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-thumb {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-bar {
|
||||||
|
background-color: #646464;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { NavbarBeforeConnexionComponent } from './navbar-before-connexion.component';
|
||||||
|
|
||||||
|
describe('NavbarBeforeConnexionComponent', () => {
|
||||||
|
let component: NavbarBeforeConnexionComponent;
|
||||||
|
let fixture: ComponentFixture<NavbarBeforeConnexionComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ NavbarBeforeConnexionComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(NavbarBeforeConnexionComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-navbar-before-connexion',
|
||||||
|
templateUrl: './navbar-before-connexion.component.html',
|
||||||
|
styleUrls: ['./navbar-before-connexion.component.scss']
|
||||||
|
})
|
||||||
|
export class NavbarBeforeConnexionComponent
|
||||||
|
{
|
||||||
|
@Input() pour = "login";
|
||||||
|
}
|
||||||
16
src/app/utils/message/message.service.spec.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MessageService } from './message.service';
|
||||||
|
|
||||||
|
describe('MessageService', () => {
|
||||||
|
let service: MessageService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(MessageService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
38
src/app/utils/message/message.service.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {HttpClient, HttpParams} from "@angular/common/http";
|
||||||
|
import {Observable} from "rxjs";
|
||||||
|
import {environment} from "../../../environments/environment";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class MessageService
|
||||||
|
{
|
||||||
|
|
||||||
|
constructor( private http: HttpClient ) { }
|
||||||
|
|
||||||
|
post(url: string, data: any): Observable<any>
|
||||||
|
{
|
||||||
|
const urlComplete = environment.debutUrl + url ;
|
||||||
|
return this.http.post<any>(urlComplete, data, {withCredentials: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
get(url: string, params:HttpParams = new HttpParams()): Observable<any>
|
||||||
|
{
|
||||||
|
const urlComplete = environment.debutUrl + url ;
|
||||||
|
return this.http.get<any>(urlComplete,{ withCredentials: true, params: params });
|
||||||
|
}
|
||||||
|
|
||||||
|
put(url: string, data: any): Observable<any>
|
||||||
|
{
|
||||||
|
const urlComplete = environment.debutUrl + url ;
|
||||||
|
return this.http.put<any>(urlComplete, data, {withCredentials: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(url: string): Observable<any>
|
||||||
|
{
|
||||||
|
const urlComplete = environment.debutUrl + url ;
|
||||||
|
return this.http.delete<any>(urlComplete,{withCredentials: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
16
src/app/utils/my-guard/my-guard.guard.spec.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { MyGuardGuard } from './my-guard.guard';
|
||||||
|
|
||||||
|
describe('MyGuardGuard', () => {
|
||||||
|
let guard: MyGuardGuard;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
guard = TestBed.inject(MyGuardGuard);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(guard).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
25
src/app/utils/my-guard/my-guard.guard.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import {ProfilService} from "../profil/profil.service";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class MyGuardGuard implements CanActivate
|
||||||
|
{
|
||||||
|
|
||||||
|
constructor(private profilService: ProfilService, private router: Router) {}
|
||||||
|
|
||||||
|
canActivate(
|
||||||
|
route: ActivatedRouteSnapshot,
|
||||||
|
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree
|
||||||
|
{
|
||||||
|
if(this.profilService.getId() !== "") return true;
|
||||||
|
else {
|
||||||
|
this.router.navigateByUrl("login");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
16
src/app/utils/profil/profil.service.spec.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ProfilService } from './profil.service';
|
||||||
|
|
||||||
|
describe('ProfilService', () => {
|
||||||
|
let service: ProfilService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(ProfilService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
29
src/app/utils/profil/profil.service.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ProfilService
|
||||||
|
{
|
||||||
|
|
||||||
|
getId(): string
|
||||||
|
{
|
||||||
|
return localStorage.getItem('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
getProfileImageUrl(): string
|
||||||
|
{
|
||||||
|
return localStorage.getItem('profileImageUrl');
|
||||||
|
}
|
||||||
|
|
||||||
|
setId(id: string): void
|
||||||
|
{
|
||||||
|
localStorage.setItem('id', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
setProfileImageUrl(profileImageUrl: string): void
|
||||||
|
{
|
||||||
|
localStorage.setItem('profileImageUrl', profileImageUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
16
src/app/utils/theme/theme.service.spec.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ThemeService } from './theme.service';
|
||||||
|
|
||||||
|
describe('ThemeService', () => {
|
||||||
|
let service: ThemeService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(ThemeService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
17
src/app/utils/theme/theme.service.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ThemeService
|
||||||
|
{
|
||||||
|
|
||||||
|
isLightTheme = true;
|
||||||
|
|
||||||
|
getClassTheme(): string
|
||||||
|
{
|
||||||
|
if(this.isLightTheme) return "lightTheme" ;
|
||||||
|
else return "darkTheme"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
0
src/assets/.gitkeep
Normal file
BIN
src/assets/darkBackground.webp
Normal file
|
After Width: | Height: | Size: 712 B |
BIN
src/assets/lightBackground.jpg
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
src/assets/logo.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
src/assets/logo_plateforms/dailymotion.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
src/assets/logo_plateforms/youtube.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
src/assets/play.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
src/assets/profil.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
src/assets/uploadFile.png
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
4
src/environments/environment.prod.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const environment = {
|
||||||
|
production: true,
|
||||||
|
debutUrl: "https://api-polynotfound.herokuapp.com/api/"
|
||||||
|
};
|
||||||
17
src/environments/environment.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// This file can be replaced during build by using the `fileReplacements` array.
|
||||||
|
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
|
||||||
|
// The list of file replacements can be found in `angular.json`.
|
||||||
|
|
||||||
|
export const environment = {
|
||||||
|
production: false,
|
||||||
|
debutUrl: "http://127.0.0.1:3000/api/"
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For easier debugging in development mode, you can import the following file
|
||||||
|
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||||
|
*
|
||||||
|
* This import should be commented out in production mode because it will have a negative impact
|
||||||
|
* on performance if an error is thrown.
|
||||||
|
*/
|
||||||
|
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
|
||||||
BIN
src/favicon.ico
Normal file
|
After Width: | Height: | Size: 948 B |
13
src/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>StreamNotFound</title>
|
||||||
|
<base href="/">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/x-icon" href="assets/logo.png">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-root></app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||