diff --git a/admin/.browserslistrc b/admin/.browserslistrc
new file mode 100644
index 0000000..427441d
--- /dev/null
+++ b/admin/.browserslistrc
@@ -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.
diff --git a/admin/.editorconfig b/admin/.editorconfig
new file mode 100644
index 0000000..59d9a3a
--- /dev/null
+++ b/admin/.editorconfig
@@ -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
diff --git a/admin/.gitignore b/admin/.gitignore
new file mode 100644
index 0000000..de51f68
--- /dev/null
+++ b/admin/.gitignore
@@ -0,0 +1,45 @@
+# 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
+
+# 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
diff --git a/admin/README.md b/admin/README.md
new file mode 100644
index 0000000..a653293
--- /dev/null
+++ b/admin/README.md
@@ -0,0 +1,27 @@
+# Admin
+
+This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.10.
+
+## Development server
+
+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.
+
+## Code scaffolding
+
+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`.
+
+## Build
+
+Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
+
+## Running unit tests
+
+Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+
+## Running end-to-end tests
+
+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.
+
+## Further help
+
+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.
diff --git a/admin/angular.json b/admin/angular.json
new file mode 100644
index 0000000..62b77eb
--- /dev/null
+++ b/admin/angular.json
@@ -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/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": "500kb",
+ "maximumError": "1mb"
+ },
+ {
+ "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"
+}
diff --git a/admin/karma.conf.js b/admin/karma.conf.js
new file mode 100644
index 0000000..2023ed1
--- /dev/null
+++ b/admin/karma.conf.js
@@ -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: '',
+ 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
+ });
+};
diff --git a/admin/package.json b/admin/package.json
new file mode 100644
index 0000000..1e0e314
--- /dev/null
+++ b/admin/package.json
@@ -0,0 +1,44 @@
+{
+ "name": "admin",
+ "version": "0.0.0",
+ "scripts": {
+ "ng": "ng",
+ "start": "ng serve",
+ "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",
+ "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"
+ }
+}
diff --git a/admin/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.html b/admin/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.html
new file mode 100644
index 0000000..76bc154
--- /dev/null
+++ b/admin/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.html
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Filtre
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ visible
+ non visible
+
+
+
+
+
+ Sujets
+
+
+ {{formControlInterests.value ? formControlInterests.value[0] : ''}}
+ 1">
+ (+{{formControlInterests.value.length - 1}} {{formControlInterests.value?.length === 2 ? 'autre' : 'autres'}})
+
+
+ {{topping}}
+
+
+
+
+
+
+
+ Période de création:
+
+ Date de début
+
+
+ -
+
+ Date de fin
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Titre |
+
+ {{advert.title}}
+ |
+
+
+
+
+ Entreprise |
+
+ {{advert.company}}
+ |
+
+
+
+
+ Sujets |
+
+
+ {{objectInterest.interest}},
+ {{objectInterest.interest}}
+
+ |
+
+
+
+
+ Date de création |
+
+ {{ advert.createdAt | date:'dd/LL/YYYY à HH:mm:ss' }}
+ |
+
+
+
+
+ Dernière modification |
+
+ {{ advert.updatedAt | date:'dd/LL/YYYY à HH:mm:ss' }}
+ |
+
+
+
+
+ Vues |
+
+ {{advert.countViews}}
+ |
+
+
+
+
+ Visible |
+
+ check
+
+ |
+
+
+
+
+ Actions |
+
+
+
+ |
+
+
+
+
+
+
+ | Aucune vidéo ne correspond au filtre: "{{input.value}}" |
+
+
+
+
+
+
+
+
+
+
diff --git a/admin/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.scss b/admin/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.scss
new file mode 100644
index 0000000..954f3d0
--- /dev/null
+++ b/admin/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.scss
@@ -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;
+}
diff --git a/admin/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.spec.ts b/admin/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.spec.ts
new file mode 100644
index 0000000..5b77dff
--- /dev/null
+++ b/admin/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageAdListAdminComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageAdListAdminComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.ts b/admin/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.ts
new file mode 100644
index 0000000..a0fa87f
--- /dev/null
+++ b/admin/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.ts
@@ -0,0 +1,260 @@
+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} from "@angular/forms";
+import {HttpParams} from "@angular/common/http";
+import {ThemeService} from "../../../utils/theme/theme.service";
+import {MessageService} from "../../../utils/message/message.service";
+
+
+
+export interface AdvertWithCountViewsAndCompany {
+ id: string,
+ userId: string,
+ company: 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', 'interests', 'createdAt', 'updatedAt', 'countViews', 'isVisible', 'actions' ];
+ dataSource ;
+ @ViewChild(MatSort) sort: MatSort;
+ @ViewChild(MatPaginator) paginator: MatPaginator;
+
+ visible: boolean = true;
+ noVisible: boolean = true;
+ startDate: Date = null;
+ endDate: Date = 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();
+ 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();
+ }
+ }
+
+
+ applyFilter(event: Event): void
+ {
+ const filterValue = (event.target as HTMLInputElement).value;
+ this.dataSource.filter = filterValue.trim().toLowerCase();
+ }
+
+
+ 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
+ {
+ this.dataSource.data = [];
+ for(let advert of this.tabAdvertWithCountViews)
+ {
+ let valide: boolean = true;
+
+ if(advert.isVisible && this.visible) valide = true;
+ else if((!advert.isVisible) && this.noVisible) valide = true;
+ else valide = false;
+
+ if(valide)
+ {
+ if ((advert.createdAt === null) && (this.startDate !== null)) valide = false;
+ else if ((advert.createdAt === null) && (this.endDate !== null)) valide = false;
+ else if (this.startDate !== null)
+ {
+ if(this.startDate.getTime() > advert.createdAt.getTime()) valide = false;
+ else if (this.endDate !== null)
+ {
+ if(this.endDate.getTime() < advert.createdAt.getTime()) valide = false;
+ }
+ }
+ }
+
+ 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;
+ }
+
+
+ onNewStartDate(event): void {
+ this.startDate = new Date(event);
+ }
+
+ onNewEndDate(event): void {
+ this.endDate = new Date(event);
+ }
+
+
+ advertToAdvertWithCountViewsAndCompany(advert): AdvertWithCountViewsAndCompany
+ {
+ let company0 = "company" ;
+ for(let advertiser of this.tabAdvertiser)
+ {
+ if(advert.userId === advertiser.id) {
+ company0 = advertiser.company;
+ break;
+ }
+ }
+
+ return {
+ id: advert.id,
+ userId: advert.userId,
+ title: advert.title,
+ company: company0,
+ 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,
+ }
+ }
+
+}
diff --git a/admin/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.html b/admin/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.html
new file mode 100644
index 0000000..d92e686
--- /dev/null
+++ b/admin/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.html
@@ -0,0 +1,8 @@
+
+ Êtes-vous sûr de vouloir supprimer l'annonce {{advert.title}} ?
+
+
+
+
+
+
diff --git a/admin/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.scss b/admin/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/admin/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.spec.ts b/admin/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.spec.ts
new file mode 100644
index 0000000..811eee8
--- /dev/null
+++ b/admin/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupDeleteAdAdminComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupDeleteAdAdminComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.ts b/admin/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.ts
new file mode 100644
index 0000000..8470921
--- /dev/null
+++ b/admin/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.ts
@@ -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,
+ @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);
+ }
+ }
+
+}
diff --git a/admin/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.html b/admin/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.html
new file mode 100644
index 0000000..dfbc2fe
--- /dev/null
+++ b/admin/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/admin/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.scss b/admin/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.scss
new file mode 100644
index 0000000..eb60d48
--- /dev/null
+++ b/admin/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.scss
@@ -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;
+}
diff --git a/admin/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.spec.ts b/admin/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.spec.ts
new file mode 100644
index 0000000..24f276f
--- /dev/null
+++ b/admin/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupVisualizeImagesAdminComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupVisualizeImagesAdminComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.ts b/admin/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.ts
new file mode 100644
index 0000000..634d051
--- /dev/null
+++ b/admin/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.ts
@@ -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,
+ @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;
+ }
+
+}
diff --git a/admin/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.html b/admin/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.html
new file mode 100644
index 0000000..7e025c5
--- /dev/null
+++ b/admin/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
Pseudo:
+
{{admin.login}}
+
+
+
+
+
Mail:
+
{{admin.email}}
+
+
+
+
+
Date de création:
+
{{admin.createdAt | date:'dd/LL/YYYY'}}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/admin/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.scss b/admin/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.scss
new file mode 100644
index 0000000..966c9a2
--- /dev/null
+++ b/admin/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.scss
@@ -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;
+}
diff --git a/admin/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.spec.ts b/admin/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.spec.ts
new file mode 100644
index 0000000..39fbdd9
--- /dev/null
+++ b/admin/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageProfilAdminComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageProfilAdminComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.ts b/admin/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.ts
new file mode 100644
index 0000000..a282706
--- /dev/null
+++ b/admin/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.ts
@@ -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;
+ }
+ });
+ }
+
+}
diff --git a/admin/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.html b/admin/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.html
new file mode 100644
index 0000000..38cf7e5
--- /dev/null
+++ b/admin/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.html
@@ -0,0 +1,59 @@
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+ Pseudo
+
+
+
+
+
+
+
+
+ Modifier mot de passe:
+
+
+
+
+
+
+
+ Nouveau mot de passe
+
+
+
+
+
+ Confirmation nouveau mot de passe
+
+
+
+
+
+
+
+
+
+
+ {{errorMessage}}
+
+
+
+
+
+
+
+
+
+
diff --git a/admin/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.scss b/admin/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.scss
new file mode 100644
index 0000000..1968e90
--- /dev/null
+++ b/admin/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.scss
@@ -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;
+}
diff --git a/admin/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.spec.ts b/admin/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.spec.ts
new file mode 100644
index 0000000..9f1a0f5
--- /dev/null
+++ b/admin/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupUpdateAdminComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupUpdateAdminComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.ts b/admin/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.ts
new file mode 100644
index 0000000..2e11b68
--- /dev/null
+++ b/admin/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.ts
@@ -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,
+ @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);
+ }
+
+}
diff --git a/admin/src/app/admin/userList/input-interests-admin/input-interests-admin.component.html b/admin/src/app/admin/userList/input-interests-admin/input-interests-admin.component.html
new file mode 100644
index 0000000..2a7c484
--- /dev/null
+++ b/admin/src/app/admin/userList/input-interests-admin/input-interests-admin.component.html
@@ -0,0 +1,43 @@
+
+
+
+
+ Centres d'intérêt
+
+
+
+
+
+
+ {{interest}}
+
+
+
+
+
+
+
+
+
+
+
+ {{interest}}
+
+
+
+
+
+
diff --git a/admin/src/app/admin/userList/input-interests-admin/input-interests-admin.component.scss b/admin/src/app/admin/userList/input-interests-admin/input-interests-admin.component.scss
new file mode 100644
index 0000000..c7acb4b
--- /dev/null
+++ b/admin/src/app/admin/userList/input-interests-admin/input-interests-admin.component.scss
@@ -0,0 +1,3 @@
+mat-form-field {
+ width: 100%;
+}
diff --git a/admin/src/app/admin/userList/input-interests-admin/input-interests-admin.component.spec.ts b/admin/src/app/admin/userList/input-interests-admin/input-interests-admin.component.spec.ts
new file mode 100644
index 0000000..62f9051
--- /dev/null
+++ b/admin/src/app/admin/userList/input-interests-admin/input-interests-admin.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ InputInterestsAdminComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(InputInterestsAdminComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/admin/userList/input-interests-admin/input-interests-admin.component.ts b/admin/src/app/admin/userList/input-interests-admin/input-interests-admin.component.ts
new file mode 100644
index 0000000..c0b3560
--- /dev/null
+++ b/admin/src/app/admin/userList/input-interests-admin/input-interests-admin.component.ts
@@ -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;
+ @Input() myInterests: string[] = [];
+ allInterests: string[] = [];
+ @Output() eventEmitter = new EventEmitter();
+ @ViewChild('tagInput') tagInput: ElementRef;
+ 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));
+ }
+
+}
diff --git a/admin/src/app/admin/userList/page-user-list/page-user-list.component.html b/admin/src/app/admin/userList/page-user-list/page-user-list.component.html
new file mode 100644
index 0000000..25fe6f5
--- /dev/null
+++ b/admin/src/app/admin/userList/page-user-list/page-user-list.component.html
@@ -0,0 +1,193 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Filtre
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Utilisateur
+
+
+ Annonceur
+
+
+ Admin
+
+
+
+
+
+
+ actif
+ non actif
+
+
+
+
+ Période de dernière connexion:
+
+ Date de début
+
+
+ -
+
+ Date de fin
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ power_settings_new
+ |
+
+
+
+ |
+
+
+
+
+ Pseudo |
+
+ {{user.login}}
+ |
+
+
+
+
+ Email |
+
+ {{user.email}}
+ |
+
+
+
+
+ Date de naissance |
+
+
+ {{ user.dateOfBirth | date:'dd/LL/YYYY' }}
+ |
+
+
+
+
+ Âge |
+
+
+ {{user.age}}
+ |
+
+
+
+
+ Sexe |
+
+ H
+ F
+ |
+
+
+
+
+ Centres d'intérêt |
+
+
+ {{interest}},
+ {{interest}}
+
+ |
+
+
+
+
+ Date de création |
+
+ {{ user.createdAt | date:'dd/LL/YYYY à HH:mm:ss' }}
+ |
+
+
+
+
+ Dernière connexion |
+
+ {{ user.lastConnexion | date:'dd/LL/YYYY à HH:mm:ss' }}
+ |
+
+
+
+
+ Accepté |
+
+
+ |
+
+
+
+
+
+
+ | Aucune vidéo ne correspond au filtre: "{{input.value}}" |
+
+
+
+
+
+
+
+
+
+
diff --git a/admin/src/app/admin/userList/page-user-list/page-user-list.component.scss b/admin/src/app/admin/userList/page-user-list/page-user-list.component.scss
new file mode 100644
index 0000000..bbeac05
--- /dev/null
+++ b/admin/src/app/admin/userList/page-user-list/page-user-list.component.scss
@@ -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;
+}
diff --git a/admin/src/app/admin/userList/page-user-list/page-user-list.component.spec.ts b/admin/src/app/admin/userList/page-user-list/page-user-list.component.spec.ts
new file mode 100644
index 0000000..edbbffe
--- /dev/null
+++ b/admin/src/app/admin/userList/page-user-list/page-user-list.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageUserListComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageUserListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/admin/userList/page-user-list/page-user-list.component.ts b/admin/src/app/admin/userList/page-user-list/page-user-list.component.ts
new file mode 100644
index 0000000..995b053
--- /dev/null
+++ b/admin/src/app/admin/userList/page-user-list/page-user-list.component.ts
@@ -0,0 +1,232 @@
+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 {PopupDeleteUserComponent} from "../popup-delete-user/popup-delete-user.component";
+import {PopupCreateUserComponent} from "../popup-create-user/popup-create-user.component";
+import {ThemeService} from "../../../utils/theme/theme.service";
+import {MessageService} from "../../../utils/message/message.service";
+
+
+@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', '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;
+
+ active: boolean = true;
+ noActive: boolean = false;
+ startDate: Date = null;
+ endDate: Date = 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);
+ this.tabUser.push(person);
+ }
+ else if(person.role.name === "advertiser") this.tabAdvertiser.push(person);
+ else this.tabAdmin.push(person);
+ }
+ this.onFilter();
+ }
+ }
+
+
+ applyFilter(event: Event): void
+ {
+ const filterValue = (event.target as HTMLInputElement).value;
+ this.dataSource.filter = filterValue.trim().toLowerCase();
+ }
+
+
+ onDelete(user: any): void
+ {
+ const config = {
+ data: { user: user }
+ };
+ this.dialog
+ .open(PopupDeleteUserComponent, 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 === user.id));
+ this.dataSource.data.splice(index, 1);
+ this.dataSource.data = this.dataSource.data;
+ this.dataSource = this.dataSource;
+ message = user.login + " a bien été supprimée ✔" ;
+ }
+ this.snackBar.open(message, "", config);
+ });
+ }
+
+
+ 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
+ {
+ 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)
+ {
+ let valide: boolean = true;
+
+ if(user.isActive && this.active) valide = true;
+ else if((!user.isActive) && this.noActive) valide = true;
+ else valide = false;
+ if(valide)
+ {
+ if ((user.lastConnexion === null) && (this.startDate !== null)) valide = false;
+ else if ((user.lastConnexion === null) && (this.endDate !== null)) valide = false;
+ else if (this.startDate !== null)
+ {
+ if(this.startDate.getTime() > user.lastConnexion.getTime()) valide = false;
+ else if (this.endDate !== null)
+ {
+ if(this.endDate.getTime() < user.lastConnexion.getTime()) valide = false;
+ }
+ }
+ }
+
+ if(valide) tab2.push(user);
+ }
+
+ this.dataSource = new MatTableDataSource(tab2);
+ this.dataSource.sort = this.sort;
+ this.dataSource.paginator = this.paginator;
+ }
+
+
+ onNewStartDate(event): void {
+ this.startDate = new Date(event);
+ }
+
+ onNewEndDate(event): void {
+ this.endDate = new Date(event);
+ }
+
+}
diff --git a/admin/src/app/admin/userList/popup-create-user/popup-create-user.component.html b/admin/src/app/admin/userList/popup-create-user/popup-create-user.component.html
new file mode 100644
index 0000000..dc7ac87
--- /dev/null
+++ b/admin/src/app/admin/userList/popup-create-user/popup-create-user.component.html
@@ -0,0 +1,160 @@
+
+
+
+
+
+ Utilisateur
+ Annonceur
+ Admin
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{errorMessage}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+
diff --git a/admin/src/app/admin/userList/popup-create-user/popup-create-user.component.scss b/admin/src/app/admin/userList/popup-create-user/popup-create-user.component.scss
new file mode 100644
index 0000000..4c8a0c6
--- /dev/null
+++ b/admin/src/app/admin/userList/popup-create-user/popup-create-user.component.scss
@@ -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;
+}
diff --git a/admin/src/app/admin/userList/popup-create-user/popup-create-user.component.spec.ts b/admin/src/app/admin/userList/popup-create-user/popup-create-user.component.spec.ts
new file mode 100644
index 0000000..9c57fcc
--- /dev/null
+++ b/admin/src/app/admin/userList/popup-create-user/popup-create-user.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupCreateUserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupCreateUserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/admin/userList/popup-create-user/popup-create-user.component.ts b/admin/src/app/admin/userList/popup-create-user/popup-create-user.component.ts
new file mode 100644
index 0000000..b5714a2
--- /dev/null
+++ b/admin/src/app/admin/userList/popup-create-user/popup-create-user.component.ts
@@ -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,
+ @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;
+ }
+
+}
diff --git a/admin/src/app/admin/userList/popup-delete-user/popup-delete-user.component.html b/admin/src/app/admin/userList/popup-delete-user/popup-delete-user.component.html
new file mode 100644
index 0000000..26e3854
--- /dev/null
+++ b/admin/src/app/admin/userList/popup-delete-user/popup-delete-user.component.html
@@ -0,0 +1,8 @@
+
+ Êtes-vous sûr de vouloir supprimer {{user.login}} ?
+
+
+
+
+
+
diff --git a/admin/src/app/admin/userList/popup-delete-user/popup-delete-user.component.scss b/admin/src/app/admin/userList/popup-delete-user/popup-delete-user.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/admin/src/app/admin/userList/popup-delete-user/popup-delete-user.component.spec.ts b/admin/src/app/admin/userList/popup-delete-user/popup-delete-user.component.spec.ts
new file mode 100644
index 0000000..273cdc6
--- /dev/null
+++ b/admin/src/app/admin/userList/popup-delete-user/popup-delete-user.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PopupDeleteUserComponent } from './popup-delete-user.component';
+
+describe('PopupDeleteUserComponent', () => {
+ let component: PopupDeleteUserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupDeleteUserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupDeleteUserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/admin/userList/popup-delete-user/popup-delete-user.component.ts b/admin/src/app/admin/userList/popup-delete-user/popup-delete-user.component.ts
new file mode 100644
index 0000000..cd8091c
--- /dev/null
+++ b/admin/src/app/admin/userList/popup-delete-user/popup-delete-user.component.ts
@@ -0,0 +1,29 @@
+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-user',
+ templateUrl: './popup-delete-user.component.html',
+ styleUrls: ['./popup-delete-user.component.scss']
+})
+export class PopupDeleteUserComponent implements OnInit
+{
+ user;
+
+ constructor( public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data,
+ private messageService: MessageService ) { }
+
+ ngOnInit(): void
+ {
+ this.user = this.data.user;
+ }
+
+ onValidate(): void
+ {
+ // --- FAUX CODE ---
+ this.dialogRef.close(true);
+ }
+
+}
diff --git a/admin/src/app/admin/utils/navbar-admin/navbar-admin.component.html b/admin/src/app/admin/utils/navbar-admin/navbar-admin.component.html
new file mode 100644
index 0000000..a73059e
--- /dev/null
+++ b/admin/src/app/admin/utils/navbar-admin/navbar-admin.component.html
@@ -0,0 +1,37 @@
+
diff --git a/admin/src/app/admin/utils/navbar-admin/navbar-admin.component.scss b/admin/src/app/admin/utils/navbar-admin/navbar-admin.component.scss
new file mode 100644
index 0000000..285d629
--- /dev/null
+++ b/admin/src/app/admin/utils/navbar-admin/navbar-admin.component.scss
@@ -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;
+}
diff --git a/admin/src/app/admin/utils/navbar-admin/navbar-admin.component.spec.ts b/admin/src/app/admin/utils/navbar-admin/navbar-admin.component.spec.ts
new file mode 100644
index 0000000..44f2cf6
--- /dev/null
+++ b/admin/src/app/admin/utils/navbar-admin/navbar-admin.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ NavbarAdminComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(NavbarAdminComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/admin/utils/navbar-admin/navbar-admin.component.ts b/admin/src/app/admin/utils/navbar-admin/navbar-admin.component.ts
new file mode 100644
index 0000000..bbf07fb
--- /dev/null
+++ b/admin/src/app/admin/utils/navbar-admin/navbar-admin.component.ts
@@ -0,0 +1,38 @@
+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);
+ }
+
+}
diff --git a/admin/src/app/app-routing.module.ts b/admin/src/app/app-routing.module.ts
new file mode 100644
index 0000000..a61b1b1
--- /dev/null
+++ b/admin/src/app/app-routing.module.ts
@@ -0,0 +1,27 @@
+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";
+
+const routes: Routes = [
+
+ // Before connexion
+ { path: '', component: PageLoginComponent },
+ { path: 'login', component: PageLoginComponent },
+ { path: 'register', component: PageRegisterComponent },
+
+ // Admin
+ { path: 'admin', component: PageUserListComponent },
+ { path: 'admin/userList', component: PageUserListComponent },
+ { path: 'admin/adList', component: PageAdListAdminComponent },
+ { path: 'admin/myProfil', component: PageProfilAdminComponent },
+];
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes)],
+ exports: [RouterModule]
+})
+export class AppRoutingModule { }
diff --git a/admin/src/app/app.component.html b/admin/src/app/app.component.html
new file mode 100644
index 0000000..d5d92f3
--- /dev/null
+++ b/admin/src/app/app.component.html
@@ -0,0 +1,2 @@
+
+
diff --git a/admin/src/app/app.component.scss b/admin/src/app/app.component.scss
new file mode 100644
index 0000000..22a5665
--- /dev/null
+++ b/admin/src/app/app.component.scss
@@ -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;
+}
diff --git a/admin/src/app/app.component.spec.ts b/admin/src/app/app.component.spec.ts
new file mode 100644
index 0000000..1391984
--- /dev/null
+++ b/admin/src/app/app.component.spec.ts
@@ -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!');
+ });
+});
diff --git a/admin/src/app/app.component.ts b/admin/src/app/app.component.ts
new file mode 100644
index 0000000..e81341b
--- /dev/null
+++ b/admin/src/app/app.component.ts
@@ -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';
+}
diff --git a/admin/src/app/app.module.ts b/admin/src/app/app.module.ts
new file mode 100644
index 0000000..3537350
--- /dev/null
+++ b/admin/src/app/app.module.ts
@@ -0,0 +1,93 @@
+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 { PopupDeleteUserComponent } from './admin/userList/popup-delete-user/popup-delete-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";
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ PageLoginComponent,
+ PopupForgottenPasswordComponent,
+ PageRegisterComponent,
+ PopupConfirmationComponent,
+ InputInterestsRegisterComponent,
+ NavbarBeforeConnexionComponent,
+ PageProfilAdminComponent,
+ PopupUpdateAdminComponent,
+ PageAdListAdminComponent,
+ PopupDeleteAdAdminComponent,
+ PopupVisualizeImagesAdminComponent,
+ PageUserListComponent,
+ InputInterestsAdminComponent,
+ PopupCreateUserComponent,
+ PopupDeleteUserComponent,
+ 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,
+ ],
+ providers: [],
+ bootstrap: [AppComponent]
+})
+export class AppModule { }
diff --git a/admin/src/app/beforeConnexion/login/page-login/page-login.component.html b/admin/src/app/beforeConnexion/login/page-login/page-login.component.html
new file mode 100644
index 0000000..675270e
--- /dev/null
+++ b/admin/src/app/beforeConnexion/login/page-login/page-login.component.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
StreamNotFound
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/admin/src/app/beforeConnexion/login/page-login/page-login.component.scss b/admin/src/app/beforeConnexion/login/page-login/page-login.component.scss
new file mode 100644
index 0000000..8924202
--- /dev/null
+++ b/admin/src/app/beforeConnexion/login/page-login/page-login.component.scss
@@ -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%;
+}
diff --git a/admin/src/app/beforeConnexion/login/page-login/page-login.component.spec.ts b/admin/src/app/beforeConnexion/login/page-login/page-login.component.spec.ts
new file mode 100644
index 0000000..a4ee677
--- /dev/null
+++ b/admin/src/app/beforeConnexion/login/page-login/page-login.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageLoginComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageLoginComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/beforeConnexion/login/page-login/page-login.component.ts b/admin/src/app/beforeConnexion/login/page-login/page-login.component.ts
new file mode 100644
index 0000000..555e496
--- /dev/null
+++ b/admin/src/app/beforeConnexion/login/page-login/page-login.component.ts
@@ -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;
+ }
+ }
+
+}
diff --git a/admin/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.html b/admin/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.html
new file mode 100644
index 0000000..c34b58e
--- /dev/null
+++ b/admin/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.html
@@ -0,0 +1,24 @@
+Récupération du mot de passe
+
+
+
+
+
+
+ Email
+
+
+
+
+
+
+ {{errorMessage}}
+
+
+
+
+
+
+
+
+
diff --git a/admin/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.scss b/admin/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.scss
new file mode 100644
index 0000000..fa75013
--- /dev/null
+++ b/admin/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.scss
@@ -0,0 +1,12 @@
+h4 {
+ text-align: center;
+}
+
+.myDiv {
+ text-align: center;
+ font-size: small;
+}
+
+.myError {
+ text-align: center;
+}
diff --git a/admin/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.spec.ts b/admin/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.spec.ts
new file mode 100644
index 0000000..ebf101c
--- /dev/null
+++ b/admin/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupForgottenPasswordComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupForgottenPasswordComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.ts b/admin/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.ts
new file mode 100644
index 0000000..1ff70ce
--- /dev/null
+++ b/admin/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.ts
@@ -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) {}
+
+
+ // 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);
+ }
+
+}
diff --git a/admin/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.html b/admin/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.html
new file mode 100644
index 0000000..2a7c484
--- /dev/null
+++ b/admin/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.html
@@ -0,0 +1,43 @@
+
+
+
+
+ Centres d'intérêt
+
+
+
+
+
+
+ {{interest}}
+
+
+
+
+
+
+
+
+
+
+
+ {{interest}}
+
+
+
+
+
+
diff --git a/admin/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.scss b/admin/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/admin/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.spec.ts b/admin/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.spec.ts
new file mode 100644
index 0000000..9917b1a
--- /dev/null
+++ b/admin/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ InputInterestsRegisterComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(InputInterestsRegisterComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.ts b/admin/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.ts
new file mode 100644
index 0000000..8dba84e
--- /dev/null
+++ b/admin/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.ts
@@ -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;
+ @Input() myInterests: string[] = [];
+ allInterests: string[] = [];
+ @Output() eventEmitter = new EventEmitter();
+ @ViewChild('tagInput') tagInput: ElementRef;
+ 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));
+ }
+
+}
diff --git a/admin/src/app/beforeConnexion/register/page-register/page-register.component.html b/admin/src/app/beforeConnexion/register/page-register/page-register.component.html
new file mode 100644
index 0000000..fb6e3ac
--- /dev/null
+++ b/admin/src/app/beforeConnexion/register/page-register/page-register.component.html
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Entreprise
+
+
+
+
+
+
+ Pseudo
+
+
+
+
+
+
+ Email
+
+
+
+
+
+
+ Mot de passe
+
+
+
+
+
+
+ Confirmation mot de passe
+
+
+
+
diff --git a/admin/src/app/beforeConnexion/register/page-register/page-register.component.scss b/admin/src/app/beforeConnexion/register/page-register/page-register.component.scss
new file mode 100644
index 0000000..5f0dc53
--- /dev/null
+++ b/admin/src/app/beforeConnexion/register/page-register/page-register.component.scss
@@ -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;
+}
diff --git a/admin/src/app/beforeConnexion/register/page-register/page-register.component.spec.ts b/admin/src/app/beforeConnexion/register/page-register/page-register.component.spec.ts
new file mode 100644
index 0000000..5cff194
--- /dev/null
+++ b/admin/src/app/beforeConnexion/register/page-register/page-register.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageRegisterComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageRegisterComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/beforeConnexion/register/page-register/page-register.component.ts b/admin/src/app/beforeConnexion/register/page-register/page-register.component.ts
new file mode 100644
index 0000000..788c8be
--- /dev/null
+++ b/admin/src/app/beforeConnexion/register/page-register/page-register.component.ts
@@ -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;
+ }
+
+}
diff --git a/admin/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.html b/admin/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.html
new file mode 100644
index 0000000..1cd51fe
--- /dev/null
+++ b/admin/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.html
@@ -0,0 +1,11 @@
+
+ Votre inscription a bien été effectuée.
+
+
+
+ Votre inscription est en cours de validation.
+
+
+
+
+
diff --git a/admin/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.scss b/admin/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.scss
new file mode 100644
index 0000000..85730e0
--- /dev/null
+++ b/admin/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.scss
@@ -0,0 +1,7 @@
+p {
+ font-size: small;
+}
+
+div {
+ font-size: small;
+}
diff --git a/admin/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.spec.ts b/admin/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.spec.ts
new file mode 100644
index 0000000..d6f9908
--- /dev/null
+++ b/admin/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupConfirmationComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupConfirmationComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.ts b/admin/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.ts
new file mode 100644
index 0000000..59e3325
--- /dev/null
+++ b/admin/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.ts
@@ -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,
+ @Inject(MAT_DIALOG_DATA) public data) {}
+}
diff --git a/admin/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.html b/admin/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.html
new file mode 100644
index 0000000..d4ad9f5
--- /dev/null
+++ b/admin/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
diff --git a/admin/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.scss b/admin/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.scss
new file mode 100644
index 0000000..e1fefaa
--- /dev/null
+++ b/admin/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.scss
@@ -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;
+}
diff --git a/admin/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.spec.ts b/admin/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.spec.ts
new file mode 100644
index 0000000..f3f7f27
--- /dev/null
+++ b/admin/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ NavbarBeforeConnexionComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(NavbarBeforeConnexionComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/admin/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.ts b/admin/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.ts
new file mode 100644
index 0000000..4a3f05e
--- /dev/null
+++ b/admin/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.ts
@@ -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";
+}
diff --git a/admin/src/app/utils/message/message.service.spec.ts b/admin/src/app/utils/message/message.service.spec.ts
new file mode 100644
index 0000000..1db761b
--- /dev/null
+++ b/admin/src/app/utils/message/message.service.spec.ts
@@ -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();
+ });
+});
diff --git a/admin/src/app/utils/message/message.service.ts b/admin/src/app/utils/message/message.service.ts
new file mode 100644
index 0000000..c20d5b1
--- /dev/null
+++ b/admin/src/app/utils/message/message.service.ts
@@ -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
+ {
+ const urlComplete = environment.debutUrl + url ;
+ return this.http.post(urlComplete, data, {withCredentials: true});
+ }
+
+ get(url: string, params:HttpParams = new HttpParams()): Observable
+ {
+ const urlComplete = environment.debutUrl + url ;
+ return this.http.get(urlComplete,{ withCredentials: true, params: params });
+ }
+
+ put(url: string, data: any): Observable
+ {
+ const urlComplete = environment.debutUrl + url ;
+ return this.http.put(urlComplete, data, {withCredentials: true});
+ }
+
+ delete(url: string): Observable
+ {
+ const urlComplete = environment.debutUrl + url ;
+ return this.http.delete(urlComplete,{withCredentials: true});
+ }
+
+}
diff --git a/admin/src/app/utils/profil/profil.service.spec.ts b/admin/src/app/utils/profil/profil.service.spec.ts
new file mode 100644
index 0000000..5cee000
--- /dev/null
+++ b/admin/src/app/utils/profil/profil.service.spec.ts
@@ -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();
+ });
+});
diff --git a/admin/src/app/utils/profil/profil.service.ts b/admin/src/app/utils/profil/profil.service.ts
new file mode 100644
index 0000000..4bbe5ea
--- /dev/null
+++ b/admin/src/app/utils/profil/profil.service.ts
@@ -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);
+ }
+
+}
diff --git a/admin/src/app/utils/theme/theme.service.spec.ts b/admin/src/app/utils/theme/theme.service.spec.ts
new file mode 100644
index 0000000..1c2957b
--- /dev/null
+++ b/admin/src/app/utils/theme/theme.service.spec.ts
@@ -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();
+ });
+});
diff --git a/admin/src/app/utils/theme/theme.service.ts b/admin/src/app/utils/theme/theme.service.ts
new file mode 100644
index 0000000..00768e5
--- /dev/null
+++ b/admin/src/app/utils/theme/theme.service.ts
@@ -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"
+ }
+
+}
diff --git a/admin/src/assets/.gitkeep b/admin/src/assets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/admin/src/assets/darkBackground.webp b/admin/src/assets/darkBackground.webp
new file mode 100644
index 0000000..0d0692b
Binary files /dev/null and b/admin/src/assets/darkBackground.webp differ
diff --git a/admin/src/assets/lightBackground.jpg b/admin/src/assets/lightBackground.jpg
new file mode 100644
index 0000000..164cb51
Binary files /dev/null and b/admin/src/assets/lightBackground.jpg differ
diff --git a/admin/src/assets/logo.png b/admin/src/assets/logo.png
new file mode 100644
index 0000000..93b9375
Binary files /dev/null and b/admin/src/assets/logo.png differ
diff --git a/admin/src/assets/logo_plateforms/dailymotion.png b/admin/src/assets/logo_plateforms/dailymotion.png
new file mode 100644
index 0000000..d35ee8a
Binary files /dev/null and b/admin/src/assets/logo_plateforms/dailymotion.png differ
diff --git a/admin/src/assets/logo_plateforms/youtube.png b/admin/src/assets/logo_plateforms/youtube.png
new file mode 100644
index 0000000..5924c8d
Binary files /dev/null and b/admin/src/assets/logo_plateforms/youtube.png differ
diff --git a/admin/src/assets/play.png b/admin/src/assets/play.png
new file mode 100644
index 0000000..194f73b
Binary files /dev/null and b/admin/src/assets/play.png differ
diff --git a/admin/src/assets/profil.png b/admin/src/assets/profil.png
new file mode 100644
index 0000000..b35b2e4
Binary files /dev/null and b/admin/src/assets/profil.png differ
diff --git a/admin/src/assets/uploadFile.png b/admin/src/assets/uploadFile.png
new file mode 100644
index 0000000..cff9f38
Binary files /dev/null and b/admin/src/assets/uploadFile.png differ
diff --git a/admin/src/environments/environment.prod.ts b/admin/src/environments/environment.prod.ts
new file mode 100644
index 0000000..8d9f516
--- /dev/null
+++ b/admin/src/environments/environment.prod.ts
@@ -0,0 +1,4 @@
+export const environment = {
+ production: true,
+ debutUrl: "https://polynotfound.herokuapp.com/api/"
+};
diff --git a/admin/src/environments/environment.ts b/admin/src/environments/environment.ts
new file mode 100644
index 0000000..6c4970f
--- /dev/null
+++ b/admin/src/environments/environment.ts
@@ -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.
diff --git a/admin/src/favicon.ico b/admin/src/favicon.ico
new file mode 100644
index 0000000..997406a
Binary files /dev/null and b/admin/src/favicon.ico differ
diff --git a/admin/src/index.html b/admin/src/index.html
new file mode 100644
index 0000000..0e892e2
--- /dev/null
+++ b/admin/src/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+ StreamNotFound
+
+
+
+
+
+
+
+
diff --git a/admin/src/main.ts b/admin/src/main.ts
new file mode 100644
index 0000000..c7b673c
--- /dev/null
+++ b/admin/src/main.ts
@@ -0,0 +1,12 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+ enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+ .catch(err => console.error(err));
diff --git a/admin/src/polyfills.ts b/admin/src/polyfills.ts
new file mode 100644
index 0000000..373f538
--- /dev/null
+++ b/admin/src/polyfills.ts
@@ -0,0 +1,65 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/**
+ * IE11 requires the following for NgClass support on SVG elements
+ */
+// import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/**
+ * Web Animations `@angular/platform-browser/animations`
+ * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
+ * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
+ */
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ * because those flags need to be set before `zone.js` being loaded, and webpack
+ * will put import in the top of bundle, so user need to create a separate file
+ * in this directory (for example: zone-flags.ts), and put the following flags
+ * into that file, and then add the following code before importing zone.js.
+ * import './zone-flags';
+ *
+ * The flags allowed in zone-flags.ts are listed here.
+ *
+ * The following flags will work for all browsers.
+ *
+ * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+ *
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ *
+ * (window as any).__Zone_enable_cross_context_check = true;
+ *
+ */
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js'; // Included with Angular CLI.
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/admin/src/styles.scss b/admin/src/styles.scss
new file mode 100644
index 0000000..90d4ee0
--- /dev/null
+++ b/admin/src/styles.scss
@@ -0,0 +1 @@
+/* You can add global styles to this file, and also import other style files */
diff --git a/admin/src/test.ts b/admin/src/test.ts
new file mode 100644
index 0000000..b4dd603
--- /dev/null
+++ b/admin/src/test.ts
@@ -0,0 +1,27 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/testing';
+import { getTestBed } from '@angular/core/testing';
+import {
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+declare const require: {
+ context(path: string, deep?: boolean, filter?: RegExp): {
+ keys(): string[];
+ (id: string): T;
+ };
+};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting(),
+ { teardown: { destroyAfterEach: true }},
+);
+
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);
diff --git a/admin/tsconfig.app.json b/admin/tsconfig.app.json
new file mode 100644
index 0000000..82d91dc
--- /dev/null
+++ b/admin/tsconfig.app.json
@@ -0,0 +1,15 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./out-tsc/app",
+ "types": []
+ },
+ "files": [
+ "src/main.ts",
+ "src/polyfills.ts"
+ ],
+ "include": [
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/admin/tsconfig.json b/admin/tsconfig.json
new file mode 100644
index 0000000..4a4dc62
--- /dev/null
+++ b/admin/tsconfig.json
@@ -0,0 +1,23 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "baseUrl": "./",
+ "outDir": "./dist/out-tsc",
+ "sourceMap": true,
+ "declaration": false,
+ "downlevelIteration": true,
+ "experimentalDecorators": true,
+ "moduleResolution": "node",
+ "importHelpers": true,
+ "target": "es2015",
+ "module": "es2020",
+ "lib": [
+ "es2018",
+ "dom"
+ ]
+ },
+ "angularCompilerOptions": {
+ "enableI18nLegacyMessageIdFormat": false
+ }
+}
diff --git a/admin/tsconfig.spec.json b/admin/tsconfig.spec.json
new file mode 100644
index 0000000..092345b
--- /dev/null
+++ b/admin/tsconfig.spec.json
@@ -0,0 +1,18 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./out-tsc/spec",
+ "types": [
+ "jasmine"
+ ]
+ },
+ "files": [
+ "src/test.ts",
+ "src/polyfills.ts"
+ ],
+ "include": [
+ "src/**/*.spec.ts",
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/app-backend/jwtRS256.key b/app-backend/jwtRS256.key
new file mode 100644
index 0000000..be25b1d
--- /dev/null
+++ b/app-backend/jwtRS256.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJJwIBAAKCAgEAlt1zgyBeUIHeCYz4vbxEdqtupPjwy7hqL2iFbGGoJUudbvw4
+LpRFmE0iw4cCuRGHwkO8vbqhCEJwpu7v5v/sYr3wOAioksr/PdHvbpKHMgLBUdnx
+n2fGBiNn6vdKMddAjqYoGBvgIhER+A5YXQ96IrVcvlu3ZJKfXx0alTZh8bsWV7PL
+rkf4j9brG66ymYdjg3VMfltQoLnV+MsubJp8WVSP7MnqJ09js2Kg7/FpvhZDhk1o
+/ky4ZZ3V6gpwVl16jApBpn6DRqpnOx1i67P19remDrFgCnKMpdHU9cg4zK7NqMVo
+Q5ZD1R/w6hu+y8UIpWFBQoby7GuAoUGmzAWvHrDDLJfAAnrYJmhDu7YOew5M+bAO
+BfVC3jO0gCO7Eu1TAbdkoBwvlFErorGlZxGRm79BlOmd590Ri+JvLHcek9KG3Yo4
+2M171QfYOK+167SJ+yGmIXadCENAEun7ocoNPZm4SMkJC2BfS9k00j83FhVPbE/v
+z0x8iGlCDQRxO/ML2YvT47bHiXpbDXz/iXE6m1YIVhabUW6xLt4LaHxSJ5hf5/4p
+OF7YCxfgFssL4QQKIxsac3eEOyMpUhudVXhH3TBaKipzUdmtY99zhP+u4cKnqAUV
+k3Esd15IJ3q3eOWd+s4sugTqZQ0eeHlvnRScTF0Fskrxt5uxbmrAHJ/JlgcCAwEA
+AQKCAgAkcCZXNHkzLrrHScJGaXOLygo2VF4shkU3YCJhtBCduW60AL09GtjYWUEd
+Qr24n3BPznaGyM3eJdX5q9pLy+J3UvLzRbwZgGg3sT4IUeHPhXTV5TMD0le04dZW
+nRXRhfD0w1MJx6nR8JeLd0OGbpeMTIuy+ooIA5X0rh3ejj0H2q0hz65RUNbLPxiY
+HhDLHjU96hXoqvuFZn000zZKSuHf3ZnJklc42Wb2S8sdSQzGObBjfszuJu+es4mY
+Np89bOSNcGi84u+oIL5AO+/JiDTPOzojcFoHC/XGkzDBkHOPlGTd75goQuHtVwKk
+OMFTjCpzb1MI03lW+vwddrLnsjPCcfY76v+5ASryMh5LFvIOaAXYrPaHU9vcaytJ
+hmyiVvviPf0VbpwBqkI3MPajoFNk8uScrRQe6frhHyOgmQLIpijaZ44PV9pZYED8
+EVULn0VQzyidF2T6JMU1/Zq6/Bk29uhqUPLof42jhi/jqCX0uJAnyqOHO2ZzMF5d
+xI55xFHJ+f3Zac5TacyOGA+7fsziyVWKXe73wk8RBgIbUiK0x273zvykIKJ1W19M
+2ihfr8fBjLgPpfeUont2cDX90hJUkEJhsi0PwQhfZS9WtH0Z88pBjO1OZsIfRdZP
+Y542RH1U1vl730CQTHl6YcqsEHV1rL0nxsBmBMjRA277xrqd0QKCAQEAxL7tjbXe
+IKAy3TvXBdLzRpZjWF/6NDCvpHicfjujx7/6yxyaIg5KbWNzRI4RTuAetPuPP1V0
+7ygAR49RZTkZxzSG0JjpzkXoPjydTBDqIazJ0X1tS7ltnVMil6wiX8vm2DnlAB/S
+fki5JK9x+jY/YrRIbkGp1LNMOG57pxRn3vQoJl4X1lmjTgqLiJR8lOlS/Ur07b6s
+yzsBinuWTtPrB5eCHFdn7nQPoHzUdbJHO/DOs0fmp1mbQ/IbFs4vSUay/KetBjDE
+4YbUyBRd6J7KIFEy2SlCeg1eyCQGK2cbghlBX7ljdWQmYaSK8QHlDRkbJaYw1q2P
+9Hi+HZhXR0YxuQKCAQEAxE0ff5b1fDsZYvJU4CZ73AWxjF4hlM2KMcJ37H53tVjq
+EpsQadxz2qFMZ60Nvk9lSzLMsXePgVChIXgL5s5GMesInqxbIUaQJ+fnwJaMYU+6
+OYrilV64IqUSC36Arecsm39faYk+y4I34Zqtn6iJeuiifGaVZr66uuKS820wq92g
+rVnsjSjWZw4F1Zh0X9hZRqteKPQZRcqT6Nnrb48Ko0GcC7MyeXAYjEGZi9EGBiWh
+xM5zzzmK1H3TrJyXopbmIxauKy+Uvzhbu8XiaCP2LJy5JKGoN39CYLZ5fD+gtHhU
+oycRyCNzv512QPngBc5L+qm27FB/nUcePYhNn3DlvwKCAQAqu/nnTPiJh6JksWm8
+Bxz2WRYNDRPQoD6Wb+g19whVC9sSoQzNluMNrYtM+brCsiWZVAbCT/KNO9gLsxKP
+9P4nab5assweFMskMZBNBGOGmvxfN2o1B3rKsFMUNLxmqGhk0PZvt3nGGk+0qzML
+kRrlepk88aBM5gEJRN4w5VrGb6wE4W02DtRM0DeMnAwPYDq5b72Rolfv8Mod5Ug5
+qQgk5wDI3SdjAOygBF01j1qvp1eOU3DDKtBxyrWdl31n4iZggJ8xeAhboCi5qMrZ
+CxyGNnzf9HlO/3Z17HU2mje8Y3Xyr9btmapIjS/st2ekF67w149GIf7hsBRDY2KZ
+xaJpAoIBABg5zGFaqAEk6gUkEwV2umJ08Wx5UXujwiJR9ariELP4vSp+qI/n/QRG
+U7+Xw9Jwyfd11X0xXCyBXuFah118p30RnIa4jqaUpsXGAPvrmHeJ91mSolP81Iyy
+AoAYpJjRptep6ISFw5IqB+t3w2Wozw60FxlzL4z2jOTzgV85YoBTYbkDGAZzu8IM
+IPwzTGlnsdze2UgEl/nc/lQGUN/7rzxmpHNtMhV6mAz3K5Ptv+iSTFyVfgVc/J0s
+yirLSItwRAagje4WeS0AL5IE4eQoTAFFdjPnH3N56R82ZdfHonWjRA6+i5s/hNFp
+BWcPb25H/2h3+XNAm/80/65oW+wJatECggEAfsCp+hf3lwV5RbI155z+7aAG8vsh
+09r4Dw3VjtaKfhZBqk0LsiqHr8J+v49Fo0Q5nJzMEyqIN47jrLuKcqV8f43CIIuP
+Rq3QpulJIisbSfhth4gJRhjpFzYAc+JLUftCNJXAii/yr0fFUg+O3XUwwwsdbDK5
+QV2N0lCRGyN1WUVH/Zm7NPeaLDz4xv4YWtz7Yj8bepbKgD8gHV4UCuApULbEwSQp
+uUPksMHLIz+1DKhZVgu+/ldz1sCV7Tomj/MMONQh0jgdfpOILfSad7M/f3rHtuDt
+DQHiWGLseNypmBZtaFDz1P+NkkFeSBlabbEpVOoTCUwTjIqfXPNyj/zTTQ==
+-----END RSA PRIVATE KEY-----
diff --git a/app-backend/jwtRS256.key.pub b/app-backend/jwtRS256.key.pub
index 6b52055..d89cfa5 100644
--- a/app-backend/jwtRS256.key.pub
+++ b/app-backend/jwtRS256.key.pub
@@ -1,14 +1,14 @@
-----BEGIN PUBLIC KEY-----
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtW31Xj62sjbJVBxnn0G2
-Habc22q7/pFIBdfn8+OsajdNVnmtfRNOsSXZP8sNhXt1QLPSgxZ/wogG0fLXIX2+
-ewzPgqrwTnr+quJ1DZ6RqOY3G1PGOibgk25aHkIXJ/gTPk1yTT6pjUmKiaGKM8pt
-M2wGwugCdEH5Wndgby8Jej30v/PPzyPxTSXrIWDeaSMX+jQyFZTGgEdgL7JvjkTj
-qLtfWKIAcEeO4PzOlRXVvbzBoYphBiZqkbzEeuOjSLPxgy4cQdbqVMlJ/lZt0SBO
-MLiIUBTufLcJS3ApesiZWWfUCq+pFFdhEABc9qrtVumzhmzWAv2rKVrHRXbguxc/
-eHKlRjAE4qmnNnTP2fsAuQIPkXVHOPWdXM1IBwnhXVB+XhxEHSANx/2oeKS6fO/e
-1oNJCiVkHin9gC8vkU9seEN73lNKZ5wPXMqTYUGA65dCY+8li+n/1pveJOJozFk7
-amkmOAPTi44lBJmxRm88XBHC3TXz6tFqX3phMqFDcQs2D9s3/2UylK0dSH5MSLnb
-9x24/ykO4RlPRVCC90vwlxzbnb0rfQVlT4dKcE6OIyXw3UsqIqFnXWmm+hnGu4QH
-Ysr+i1VIhPOs9YdZwlqhzcPTuNcdxmxy9ZfZ8KlLIWbAMbSH+obwm4w+HYTZjspe
-2MwrKGgzpl4YW7ct/ViqeQMCAwEAAQ==
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlt1zgyBeUIHeCYz4vbxE
+dqtupPjwy7hqL2iFbGGoJUudbvw4LpRFmE0iw4cCuRGHwkO8vbqhCEJwpu7v5v/s
+Yr3wOAioksr/PdHvbpKHMgLBUdnxn2fGBiNn6vdKMddAjqYoGBvgIhER+A5YXQ96
+IrVcvlu3ZJKfXx0alTZh8bsWV7PLrkf4j9brG66ymYdjg3VMfltQoLnV+MsubJp8
+WVSP7MnqJ09js2Kg7/FpvhZDhk1o/ky4ZZ3V6gpwVl16jApBpn6DRqpnOx1i67P1
+9remDrFgCnKMpdHU9cg4zK7NqMVoQ5ZD1R/w6hu+y8UIpWFBQoby7GuAoUGmzAWv
+HrDDLJfAAnrYJmhDu7YOew5M+bAOBfVC3jO0gCO7Eu1TAbdkoBwvlFErorGlZxGR
+m79BlOmd590Ri+JvLHcek9KG3Yo42M171QfYOK+167SJ+yGmIXadCENAEun7ocoN
+PZm4SMkJC2BfS9k00j83FhVPbE/vz0x8iGlCDQRxO/ML2YvT47bHiXpbDXz/iXE6
+m1YIVhabUW6xLt4LaHxSJ5hf5/4pOF7YCxfgFssL4QQKIxsac3eEOyMpUhudVXhH
+3TBaKipzUdmtY99zhP+u4cKnqAUVk3Esd15IJ3q3eOWd+s4sugTqZQ0eeHlvnRSc
+TF0Fskrxt5uxbmrAHJ/JlgcCAwEAAQ==
-----END PUBLIC KEY-----
diff --git a/userAndAdvertiser/.browserslistrc b/userAndAdvertiser/.browserslistrc
new file mode 100644
index 0000000..427441d
--- /dev/null
+++ b/userAndAdvertiser/.browserslistrc
@@ -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.
diff --git a/userAndAdvertiser/.editorconfig b/userAndAdvertiser/.editorconfig
new file mode 100644
index 0000000..59d9a3a
--- /dev/null
+++ b/userAndAdvertiser/.editorconfig
@@ -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
diff --git a/userAndAdvertiser/.gitignore b/userAndAdvertiser/.gitignore
new file mode 100644
index 0000000..de51f68
--- /dev/null
+++ b/userAndAdvertiser/.gitignore
@@ -0,0 +1,45 @@
+# 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
+
+# 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
diff --git a/userAndAdvertiser/README.md b/userAndAdvertiser/README.md
new file mode 100644
index 0000000..d2ab4a3
--- /dev/null
+++ b/userAndAdvertiser/README.md
@@ -0,0 +1,27 @@
+# UserAndAdvertiser
+
+This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.14.
+
+## Development server
+
+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.
+
+## Code scaffolding
+
+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`.
+
+## Build
+
+Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
+
+## Running unit tests
+
+Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+
+## Running end-to-end tests
+
+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.
+
+## Further help
+
+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.
diff --git a/userAndAdvertiser/angular.json b/userAndAdvertiser/angular.json
new file mode 100644
index 0000000..5e25aaf
--- /dev/null
+++ b/userAndAdvertiser/angular.json
@@ -0,0 +1,113 @@
+{
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "version": 1,
+ "newProjectRoot": "projects",
+ "projects": {
+ "userAndAdvertiser": {
+ "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/userAndAdvertiser",
+ "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": "500kb",
+ "maximumError": "1mb"
+ },
+ {
+ "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": "userAndAdvertiser:build:production"
+ },
+ "development": {
+ "browserTarget": "userAndAdvertiser:build:development"
+ }
+ },
+ "defaultConfiguration": "development"
+ },
+ "extract-i18n": {
+ "builder": "@angular-devkit/build-angular:extract-i18n",
+ "options": {
+ "browserTarget": "userAndAdvertiser: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": "userAndAdvertiser"
+}
diff --git a/userAndAdvertiser/karma.conf.js b/userAndAdvertiser/karma.conf.js
new file mode 100644
index 0000000..e82c4ac
--- /dev/null
+++ b/userAndAdvertiser/karma.conf.js
@@ -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: '',
+ 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/userAndAdvertiser'),
+ 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
+ });
+};
diff --git a/userAndAdvertiser/package.json b/userAndAdvertiser/package.json
new file mode 100644
index 0000000..f5a3d69
--- /dev/null
+++ b/userAndAdvertiser/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "user-and-advertiser",
+ "version": "0.0.0",
+ "scripts": {
+ "ng": "ng",
+ "start": "ng serve",
+ "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",
+ "jquery": "^3.6.0",
+ "ng2-charts": "^2.2.3",
+ "popper": "^1.0.1",
+ "rxjs": "~6.6.0",
+ "tslib": "^2.3.0",
+ "zone.js": "~0.11.4"
+ },
+ "devDependencies": {
+ "@angular-devkit/build-angular": "~12.2.14",
+ "@angular/cli": "~12.2.14",
+ "@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"
+ }
+}
diff --git a/userAndAdvertiser/src/app/advertiser/adList/drag-and-drop/drag-and-drop.component.html b/userAndAdvertiser/src/app/advertiser/adList/drag-and-drop/drag-and-drop.component.html
new file mode 100644
index 0000000..ba7dc4f
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/drag-and-drop/drag-and-drop.component.html
@@ -0,0 +1,33 @@
+
+
+
Images
+

+
Glisser déposer
+
ou
+
Cliquer pour selectionner
+
+
+
+ info
+
+
+
+
+

+
+
+ {{ file?.name }}
+
+
+ {{ formatBytes(file?.size) }}
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/advertiser/adList/drag-and-drop/drag-and-drop.component.scss b/userAndAdvertiser/src/app/advertiser/adList/drag-and-drop/drag-and-drop.component.scss
new file mode 100644
index 0000000..91899f6
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/drag-and-drop/drag-and-drop.component.scss
@@ -0,0 +1,135 @@
+.container {
+ width: 450px;
+ height: 180px;
+ padding: 20px 0px 20px 0px;
+ text-align: center;
+ border: dashed 1px #979797;
+ position: relative;
+ margin: 0 auto;
+}
+
+input {
+ opacity: 0;
+ position: absolute;
+ z-index: 2;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+}
+
+
+.fileover {
+ animation: shake 1s;
+ animation-iteration-count: infinite;
+}
+
+.files-list {
+ margin-top: 1.5rem;
+
+ .single-file {
+ display: flex;
+ padding: 0.5rem;
+ justify-content: space-between;
+ align-items: center;
+ border: dashed 1px #979797;
+ margin-bottom: 1rem;
+
+ img.delete {
+ margin-left: 0.5rem;
+ cursor: pointer;
+ align-self: flex-end;
+ }
+
+
+ display: flex;
+ flex-grow: 1;
+
+ .name {
+ font-size: 14px;
+ font-weight: 500;
+ color: #353f4a;
+ margin: 0;
+ }
+
+ .size {
+ font-size: 12px;
+ font-weight: 500;
+ color: #a4a4a4;
+ margin: 0;
+ margin-bottom: 0.25rem;
+ }
+
+ .info {
+ width: 100%
+ }
+ }
+}
+
+/* Shake animation */
+@keyframes shake {
+ 0% {
+ transform: translate(1px, 1px) rotate(0deg);
+ }
+
+ 10% {
+ transform: translate(-1px, -2px) rotate(-1deg);
+ }
+
+ 20% {
+ transform: translate(-3px, 0px) rotate(1deg);
+ }
+
+ 30% {
+ transform: translate(3px, 2px) rotate(0deg);
+ }
+
+ 40% {
+ transform: translate(1px, -1px) rotate(1deg);
+ }
+
+ 50% {
+ transform: translate(-1px, 2px) rotate(-1deg);
+ }
+
+ 60% {
+ transform: translate(-3px, 1px) rotate(0deg);
+ }
+
+ 70% {
+ transform: translate(3px, 1px) rotate(-1deg);
+ }
+
+ 80% {
+ transform: translate(-1px, -1px) rotate(1deg);
+ }
+
+ 90% {
+ transform: translate(1px, 2px) rotate(0deg);
+ }
+
+ 100% {
+ transform: translate(1px, -2px) rotate(-1deg);
+ }
+}
+
+
+.progress-cont {
+ height: 7px;
+ width: 100%;
+ border-radius: 4px;
+ background-color: #d0d0d0;
+ position: relative;
+
+ .progress {
+ width: 0;
+ height: 100%;
+ position: absolute;
+ z-index: 1;
+ top: 0;
+ left: 0;
+ border-radius: 4px;
+ background-color: #4c97cb;
+ transition: 0.5s all;
+ }
+}
diff --git a/userAndAdvertiser/src/app/advertiser/adList/drag-and-drop/drag-and-drop.component.spec.ts b/userAndAdvertiser/src/app/advertiser/adList/drag-and-drop/drag-and-drop.component.spec.ts
new file mode 100644
index 0000000..e4666b0
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/drag-and-drop/drag-and-drop.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DragAndDropComponent } from './drag-and-drop.component';
+
+describe('DragAndDropComponent', () => {
+ let component: DragAndDropComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ DragAndDropComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DragAndDropComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/advertiser/adList/drag-and-drop/drag-and-drop.component.ts b/userAndAdvertiser/src/app/advertiser/adList/drag-and-drop/drag-and-drop.component.ts
new file mode 100644
index 0000000..e626bef
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/drag-and-drop/drag-and-drop.component.ts
@@ -0,0 +1,93 @@
+import {Component, ElementRef, EventEmitter, OnInit, Output, ViewChild} from '@angular/core';
+
+@Component({
+ selector: 'app-drag-and-drop',
+ templateUrl: './drag-and-drop.component.html',
+ styleUrls: ['./drag-and-drop.component.scss']
+})
+export class DragAndDropComponent
+{
+ @ViewChild("fileDropRef", { static: false }) fileDropEl: ElementRef;
+ info_image = "Vos annonces seront affichées dans un rectangle de rapport 1/5 avec: \n • 1 la largeur du rectangle \n • 5 la hauteur du rectangle" ;
+ files: any[] = [];
+ @Output() eventEmitter = new EventEmitter();
+
+ /**
+ * on file drop handler
+ */
+ onFileDropped($event) {
+ this.prepareFilesList($event);
+ this.eventEmitter.emit(this.files);
+ }
+
+ /**
+ * handle file from browsing
+ */
+ fileBrowseHandler(files) {
+ this.prepareFilesList(files);
+ this.eventEmitter.emit(this.files);
+ }
+
+ /**
+ * Delete file from files list
+ * @param index (File index)
+ */
+ deleteFile(index: number) {
+ if (this.files[index].progress < 100) {
+ console.log("Upload in progress.");
+ return;
+ }
+ this.files.splice(index, 1);
+ this.eventEmitter.emit(this.files);
+ }
+
+ /**
+ * Simulate the upload process
+ */
+ uploadFilesSimulator(index: number) {
+ setTimeout(() => {
+ if (index === this.files.length) {
+ return;
+ } else {
+ const progressInterval = setInterval(() => {
+ if (this.files[index].progress === 100) {
+ clearInterval(progressInterval);
+ this.uploadFilesSimulator(index + 1);
+ } else {
+ this.files[index].progress += 5;
+ }
+ }, 200);
+ }
+ }, 1000);
+ }
+
+ /**
+ * Convert Files list to normal array list
+ * @param files (Files List)
+ */
+ prepareFilesList(files: Array) {
+ for (const item of files) {
+ item.progress = 0;
+ this.files.push(item);
+ }
+ this.fileDropEl.nativeElement.value = "";
+ this.uploadFilesSimulator(0);
+ }
+
+ /**
+ * format bytes
+ * @param bytes (File size in bytes)
+ * @param decimals (Decimals point)
+ */
+ formatBytes(bytes, decimals = 2) {
+ if (bytes === 0) {
+ return "0 Bytes";
+ }
+ const k = 1024;
+ const dm = decimals <= 0 ? 0 : decimals;
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/advertiser/adList/input-interests-ad/input-interests-ad.component.html b/userAndAdvertiser/src/app/advertiser/adList/input-interests-ad/input-interests-ad.component.html
new file mode 100644
index 0000000..6def6c2
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/input-interests-ad/input-interests-ad.component.html
@@ -0,0 +1,43 @@
+
+
+
+
+ Sujets
+
+
+
+
+
+
+ {{interest}}
+
+
+
+
+
+
+
+
+
+
+
+ {{interest}}
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/advertiser/adList/input-interests-ad/input-interests-ad.component.scss b/userAndAdvertiser/src/app/advertiser/adList/input-interests-ad/input-interests-ad.component.scss
new file mode 100644
index 0000000..2c3a84d
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/input-interests-ad/input-interests-ad.component.scss
@@ -0,0 +1,16 @@
+mat-form-field {
+ width: 100%;
+ font-size: small;
+}
+
+mat-chip-list {
+ font-size: small;
+}
+
+mat-chip {
+ font-size: small;
+}
+
+input {
+ font-size: small;
+}
diff --git a/userAndAdvertiser/src/app/advertiser/adList/input-interests-ad/input-interests-ad.component.spec.ts b/userAndAdvertiser/src/app/advertiser/adList/input-interests-ad/input-interests-ad.component.spec.ts
new file mode 100644
index 0000000..deae4d3
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/input-interests-ad/input-interests-ad.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { InputInterestsAdComponent } from './input-interests-ad.component';
+
+describe('BarTagsComponent', () => {
+ let component: InputInterestsAdComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ InputInterestsAdComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(InputInterestsAdComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/advertiser/adList/input-interests-ad/input-interests-ad.component.ts b/userAndAdvertiser/src/app/advertiser/adList/input-interests-ad/input-interests-ad.component.ts
new file mode 100644
index 0000000..7ae81aa
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/input-interests-ad/input-interests-ad.component.ts
@@ -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-ad',
+ templateUrl: './input-interests-ad.component.html',
+ styleUrls: ['./input-interests-ad.component.scss']
+})
+export class InputInterestsAdComponent implements OnInit
+{
+ selectable = true;
+ removable = true;
+ separatorKeysCodes: number[] = [ENTER, COMMA];
+ formControl = new FormControl();
+ filteredInterests: Observable;
+ @Input() myInterests: string[] = [];
+ allInterests: string[] = [];
+ @Output() eventEmitter = new EventEmitter();
+ @ViewChild('tagInput') tagInput: ElementRef;
+ 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));
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component.html b/userAndAdvertiser/src/app/advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component.html
new file mode 100644
index 0000000..78dd263
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component.html
@@ -0,0 +1,185 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Filtre
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ visible
+ non visible
+
+
+
+
+
+
+ Sujets
+
+
+ {{formControlInterests.value ? formControlInterests.value[0] : ''}}
+ 1">
+ (+{{formControlInterests.value.length - 1}} {{formControlInterests.value?.length === 2 ? 'autre' : 'autres'}})
+
+
+ {{topping}}
+
+
+
+
+
+
+
+
+ Période de création:
+
+ Date de début
+
+
+ -
+
+ Date de fin
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ power_settings_new
+ |
+
+
+ |
+
+
+
+
+ Titre |
+
+ {{advert.title}}
+ |
+
+
+
+
+ Sujets |
+
+
+ {{interest}},
+ {{interest}}
+
+ |
+
+
+
+
+ Date de création |
+
+ {{ advert.createdAt | date:'dd/LL/YYYY à HH:mm:ss' }}
+ |
+
+
+
+
+ Dernière modification |
+
+ {{ advert.updatedAt | date:'dd/LL/YYYY à HH:mm:ss' }}
+ |
+
+
+
+
+ Vues |
+
+ {{advert.countViews}}
+ |
+
+
+
+
+ Actions |
+
+
+
+
+ |
+
+
+
+
+
+
+ | Aucune vidéo ne correspond au filtre: "{{input.value}}" |
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component.scss b/userAndAdvertiser/src/app/advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component.scss
new file mode 100644
index 0000000..370e312
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component.scss
@@ -0,0 +1,87 @@
+.myContainer {
+ max-width: 100vw;
+ height: 100vh;
+ overflow-x: hidden;
+ font-size: small;
+}
+
+
+// ----------------------------------------------------------
+
+
+.filtersContainer {
+ width: 95%;
+ 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;
+}
+
+input {
+ width: 30%;
+ font-size: large;
+ border-radius: 5px;
+}
+
+
+// --------------------------------------------------------------------
+
+
+// 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;
+}
+
+
+// -------------------------------------------------------------------------
+
+::ng-deep .mat-pseudo-checkbox-checked {
+ background-color: black !important;
+}
diff --git a/userAndAdvertiser/src/app/advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component.spec.ts b/userAndAdvertiser/src/app/advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component.spec.ts
new file mode 100644
index 0000000..9492c6c
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageAdListAdvertiserComponent } from './page-ad-list-advertiser.component';
+
+describe('PageAdvertiserComponent', () => {
+ let component: PageAdListAdvertiserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageAdListAdvertiserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageAdListAdvertiserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component.ts b/userAndAdvertiser/src/app/advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component.ts
new file mode 100644
index 0000000..8797593
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component.ts
@@ -0,0 +1,304 @@
+import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
+import {MatSort} from "@angular/material/sort";
+import {MatTableDataSource} from "@angular/material/table";
+import {MatDialog} from "@angular/material/dialog";
+import {PopupAddOrUpdateAdComponent} from "../popup-add-or-update-ad/popup-add-or-update-ad.component";
+import {MatSnackBar} from "@angular/material/snack-bar";
+import {PopupDeleteAdAdvertiserComponent} from "../popup-delete-ad-advertiser/popup-delete-ad-advertiser.component";
+import {MatPaginator} from "@angular/material/paginator";
+import {PopupVisualizeImagesAdvertiserComponent} from "../popup-visualize-images-advertiser/popup-visualize-images-advertiser.component";
+import {FormControl} from "@angular/forms";
+import {HttpParams} from "@angular/common/http";
+import {ThemeService} from "../../../utils/theme/theme.service";
+import {MessageService} from "../../../utils/message/message.service";
+
+
+
+@Component({
+ selector: 'app-page-ad-list-advertiser',
+ templateUrl: './page-ad-list-advertiser.component.html',
+ styleUrls: ['./page-ad-list-advertiser.component.scss']
+})
+export class PageAdListAdvertiserComponent implements AfterViewInit
+{
+ displayedColumns: string[] = [ 'isVisible', 'title', 'interests', 'createdAt', 'updatedAt', 'countViews', 'actions' ];
+ tabAdvertWithCountViews: any[] = [];
+ dataSource;
+ @ViewChild(MatSort) sort: MatSort;
+ @ViewChild(MatPaginator) paginator: MatPaginator;
+
+ visible: boolean = true;
+ noVisible: boolean = true;
+ startDate: Date = null;
+ endDate: Date = null;
+ formControlInterests = new FormControl();
+
+ allVideoCategorie = [];
+ allInterests: string[] = [];
+
+
+ constructor( public themeService: ThemeService,
+ public dialog: MatDialog,
+ private snackBar: MatSnackBar,
+ private messageService: MessageService ) { }
+
+
+ ngAfterViewInit(): void
+ {
+ // Ask interests
+ this.messageService
+ .get("misc/getInterests")
+ .subscribe(ret => this.afterReceivingInterests(ret), err => this.afterReceivingInterests(err) );
+
+ // Ask ads
+ let params = new HttpParams();
+ params = params.append("isActive", true);
+ this.messageService
+ .get("ad/findAll", params)
+ .subscribe(ret => this.afterReceivingAds(ret), err => this.afterReceivingAds(err));
+ }
+
+
+ afterReceivingInterests(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log("afterReceivingInterests");
+ console.log(retour);
+ }
+ else {
+ this.allVideoCategorie = retour.data;
+ this.allInterests = retour.data.map(x => x.interest);
+ this.allInterests.sort();
+ }
+ }
+
+
+ afterReceivingAds(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else {
+ if(retour.data.length !== 0)
+ {
+ for(let advert of retour.data) this.tabAdvertWithCountViews.push(this.advertToAdvertWithCountViews(advert));
+ this.dataSource = new MatTableDataSource();
+ this.onFilter();
+ }
+ }
+ }
+
+
+ applyFilter(event: Event): void
+ {
+ const filterValue = (event.target as HTMLInputElement).value;
+ this.dataSource.filter = filterValue.trim().toLowerCase();
+ }
+
+
+ onVisualizeImages(advert: any)
+ {
+ if(advert.images.length !== 0)
+ {
+ const config = {
+ width: '30%',
+ height: '90%',
+ data: { images: advert.images }
+ };
+ this.dialog
+ .open(PopupVisualizeImagesAdvertiserComponent, 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);
+ }
+ }
+
+
+ onAdd(): void
+ {
+ const config = {
+ width: '75%',
+ //height: '80%',
+ panelClass: 'custom-dialog-container',
+ data: {
+ action: "add",
+ advert: null,
+ allVideoCategorie: this.allVideoCategorie,
+ allTitle: this.tabAdvertWithCountViews.map(x => x.title)
+ }
+ };
+ this.dialog
+ .open(PopupAddOrUpdateAdComponent, config)
+ .afterClosed()
+ .subscribe( advertAdded => {
+
+ const config = { duration: 1000, panelClass: "custom-class" };
+ let message = "" ;
+ if((advertAdded === undefined) || (advertAdded === null)) {
+ message = "Opération annulée" ;
+ }
+ else {
+ this.tabAdvertWithCountViews.push(this.advertToAdvertWithCountViews(advertAdded));
+ this.onFilter();
+ message = "L'annonce a bien été ajoutée ✔" ;
+ }
+ this.snackBar.open( message, "", config);
+ });
+ }
+
+
+ onUpdate(advertToUpdate: any): void
+ {
+ const config = {
+ width: '75%',
+ //height: '80%',
+ panelClass: 'custom-dialog-container',
+ data: {
+ action: "update",
+ advert: advertToUpdate,
+ allVideoCategorie: this.allVideoCategorie,
+ allTitle: this.tabAdvertWithCountViews.map(x => x.title)
+ }
+ };
+ this.dialog
+ .open(PopupAddOrUpdateAdComponent, config)
+ .afterClosed()
+ .subscribe( advertUpdated => {
+
+ const config = { duration: 1000, panelClass: "custom-class" };
+ let message = "" ;
+ if((advertUpdated === undefined) || (advertUpdated === null)) {
+ message = "Opération annulée" ;
+ }
+ else {
+ const index = this.tabAdvertWithCountViews.findIndex(elt => (elt.id === advertToUpdate.id));
+ this.tabAdvertWithCountViews.splice(index, 1, this.advertToAdvertWithCountViews(advertUpdated));
+ this.onFilter();
+ message = "L'annonce a bien été modifiée ✔" ;
+ }
+ this.snackBar.open( message, "", config);
+ });
+ }
+
+
+ onDelete(advert: any): void
+ {
+ const config = {
+ data: { advert: advert }
+ };
+ this.dialog
+ .open(PopupDeleteAdAdvertiserComponent, 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
+ {
+ if(this.dataSource === null || this.dataSource === undefined) this.dataSource = new MatTableDataSource();
+ this.dataSource.data = [];
+ for(let advert of this.tabAdvertWithCountViews)
+ {
+ let valide: boolean = true;
+
+ if(advert.isVisible && this.visible) valide = true;
+ else if((!advert.isVisible) && this.noVisible) valide = true;
+ else valide = false;
+
+ if(valide)
+ {
+ if ((advert.createdAt === null) && (this.startDate !== null)) valide = false;
+ else if ((advert.createdAt === null) && (this.endDate !== null)) valide = false;
+ else if (this.startDate !== null)
+ {
+ if(this.startDate.getTime() > advert.createdAt.getTime()) valide = false;
+ else if (this.endDate !== null)
+ {
+ if(this.endDate.getTime() < advert.createdAt.getTime()) valide = false;
+ }
+ }
+ }
+
+ 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;
+ }
+
+
+ onNewStartDate(event): void {
+ this.startDate = new Date(event);
+ }
+
+ onNewEndDate(event): void {
+ this.endDate = new Date(event);
+ }
+
+
+ onSliderIsVisible(advert: any): void
+ {
+ // il faut envoyer la négation de user.isActive
+ this.messageService
+ .put("ad/update/"+advert.id, { isVisible: !advert.isVisible })
+ .subscribe(
+ ret => {},
+ err => {
+ console.log("onSliderIsVisible");
+ console.log(err);
+ }
+ );
+ }
+
+
+ advertToAdvertWithCountViews(advert)
+ {
+ return {
+ id: advert.id,
+ userId: advert.userId,
+ title: advert.title,
+ url: advert.url,
+ images: advert.images,
+ interests: advert.interests.map(x => x.interest),
+ comment: advert.comment,
+ views: advert.views,
+ countViews: advert.views.length,
+ isVisible: advert.isVisible,
+ isActive: advert.isActive,
+ createdAt: advert.createdAt,
+ updatedAt: advert.updatedAt,
+ }
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component.html b/userAndAdvertiser/src/app/advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component.html
new file mode 100644
index 0000000..e3b7986
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component.html
@@ -0,0 +1,83 @@
+
+
+
+
+
{{title}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Titre annonce
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Visible
+
+
+
+
+ Images déjà associées:
+
+
+
+
+ {{image.description}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{errorMessage}}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component.scss b/userAndAdvertiser/src/app/advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component.scss
new file mode 100644
index 0000000..3bb2eed
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component.scss
@@ -0,0 +1,90 @@
+.myContainer1 {
+ padding: 10px 10px 0px 25px;
+ margin: 0px 0px 0px 0px;
+ font-size: small;
+}
+
+
+.myContainer2 {
+ padding: 0px 0px 0px 0px;
+ margin: 0px 0px 0px 0px;
+ overflow-y: hidden;
+ overflow-x: hidden;
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+}
+.myContainer2::-webkit-scrollbar {
+ display: none;
+}
+
+
+
+h1 {
+ text-align: center;
+ font-size: large;
+}
+
+.col-6, .col-8 {
+ border-left: solid 1px #a4a4a4;
+}
+
+
+// -------------------------------------------------------------------------
+
+.titleContainer {
+ width: 100%;
+}
+
+.commentContainer {
+ width: 100%;
+}
+
+.imageContainer {
+ border: solid 1px grey;
+}
+
+mat-dialog-actions {
+ margin-bottom: 0px;
+}
+
+button {
+ font-size: small;
+}
+
+
+// -------------------------------------------------------------------------
+// --- LightTheme ---
+
+// aura
+.lightTheme ::ng-deep .mat-checkbox-ripple .mat-ripple-element {
+ background-color: grey !important;
+}
+
+// contenu coche
+.lightTheme ::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
+ background-color: black !important;
+}
+
+// indeterminate
+.lightTheme ::ng-deep .mat-checkbox .mat-checkbox-frame {
+ border-color: black !important;
+ background-color: white !important;
+}
+
+// --- DarkTheme ---
+
+// aura
+.darTheme ::ng-deep .mat-checkbox-ripple .mat-ripple-element {
+ background-color: grey !important;
+}
+
+// contenu coche
+.darkTheme ::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
+ background-color: black !important;
+}
+
+// indeterminate
+.darkTheme ::ng-deep .mat-checkbox .mat-checkbox-frame {
+ border-color: white !important;
+ //background-color: white !important;
+}
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component.spec.ts b/userAndAdvertiser/src/app/advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component.spec.ts
new file mode 100644
index 0000000..ba74952
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PopupAddOrUpdateAdComponent } from './popup-add-or-update-ad.component';
+
+describe('PopupAddOrUpdateAdComponent', () => {
+ let component: PopupAddOrUpdateAdComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupAddOrUpdateAdComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupAddOrUpdateAdComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component.ts b/userAndAdvertiser/src/app/advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component.ts
new file mode 100644
index 0000000..e927f68
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component.ts
@@ -0,0 +1,220 @@
+import {Component, Inject, OnInit} from '@angular/core';
+import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
+import {MessageService} from "../../../utils/message/message.service";
+import {ThemeService} from "../../../utils/theme/theme.service";
+
+
+
+
+const ADVERT_VIDE = {
+ _id: "",
+ userId: "",
+ title: "",
+ url: "",
+ images: [],
+ interests: [],
+ comment: "",
+ views: [],
+ isVisible: true,
+ isActive: true,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+}
+
+
+@Component({
+ selector: 'app-popup-add-or-update-ad',
+ templateUrl: './popup-add-or-update-ad.component.html',
+ styleUrls: ['./popup-add-or-update-ad.component.scss']
+})
+export class PopupAddOrUpdateAdComponent implements OnInit
+{
+ advert: any;
+ title: string = "" ;
+ allVideoCategorie = [];
+ allTitle = [];
+
+ tabOfNewImagesBase64 = [];
+ tabOfNewImagesName = [];
+
+ hasError: boolean = false;
+ errorMessage: string = "" ;
+
+
+
+ constructor( public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data,
+ private messageService: MessageService,
+ public themeService: ThemeService ) { }
+
+
+ ngOnInit(): void
+ {
+ this.allVideoCategorie = this.data.allVideoCategorie;
+ this.allTitle = this.data.allTitle.slice();
+ if(this.data.action === "add")
+ {
+ this.advert = Object.assign({}, ADVERT_VIDE);
+ this.advert.images = [];
+ this.advert.interests = [];
+ this.title = "Ajouter annonce" ;
+ }
+ else
+ {
+ this.advert = Object.assign({}, this.data.advert);
+ this.advert.interests = this.data.advert.interests.slice();
+ this.title = "Modifier annonce" ;
+ const indexOldTitle = this.allTitle.findIndex(title => title == this.advert.title);
+ this.allTitle.splice(indexOldTitle, 1);
+ }
+ }
+
+
+ onValidate(): void
+ {
+ this.checkField();
+ if(!this.hasError)
+ {
+ // preparation des donnees
+ this.prepareAdvertInterests();
+ this.prepareAdvertImages();
+
+ // si creation
+ if (this.data.action === "add")
+ {
+ this.messageService
+ .post("ad/create", this.advert)
+ .subscribe(ret => this.onCreateCallback(ret), err => this.onCreateCallback(err));
+ }
+ // si update
+ else
+ {
+ const id = this.advert.id;
+ Reflect.deleteProperty(this.advert, "id");
+ Reflect.deleteProperty(this.advert, "_id");
+ this.messageService
+ .put("ad/update/" + id, this.advert)
+ .subscribe(ret => this.onUpdateCallback(ret, id), err => this.onUpdateCallback(err, id));
+ }
+ }
+ }
+
+
+ checkField()
+ {
+ if(this.advert.title.length === 0) {
+ this.errorMessage = "Veuillez remplir le champ 'titre'." ;
+ this.hasError = true;
+ }
+ else if(this.allTitle.includes(this.advert.title)) {
+ this.errorMessage = "Ce titre est déjà pris." ;
+ this.hasError = true;
+ }
+ else if((this.advert.images.length === 0) && (this.tabOfNewImagesName.length === 0)) {
+ this.errorMessage = "Veuillez uploader au moins une image." ;
+ this.hasError = true;
+ }
+ else {
+ this.errorMessage = "";
+ this.hasError = false;
+ }
+ }
+
+
+
+ onCreateCallback(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ this.dialogRef.close();
+ }
+ else {
+ this.dialogRef.close(retour.data);
+ }
+ }
+
+
+ onUpdateCallback(retour: any, id: string): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ this.dialogRef.close();
+ }
+ else {
+ this.advert.id = id;
+ this.dialogRef.close(this.advert);
+ }
+ }
+
+
+ onEventInputTags(myTags: string[]): void
+ {
+ this.advert.interests = myTags;
+ }
+
+
+ onRemoveImgAlreadyPresent(image)
+ {
+ const index = this.advert.images.indexOf(image);
+ this.advert.images.splice(index, 1);
+ }
+
+
+ onReceiveNewImages(files: any): void
+ {
+ this.tabOfNewImagesBase64 = [];
+ this.tabOfNewImagesName = [];
+ if(files)
+ {
+ for(let file of files)
+ {
+ if(file)
+ {
+ const reader = new FileReader();
+ reader.onload = this.handleReaderLoaded.bind(this);
+ this.tabOfNewImagesName.push(file.name)
+ reader.readAsBinaryString(file);
+ }
+ }
+ }
+ }
+ handleReaderLoaded(e)
+ {
+ this.tabOfNewImagesBase64.push('data:image/png;base64,' + btoa(e.target.result))
+ }
+
+
+ // Met bien en forme les "images" avant d'être envoyer
+ prepareAdvertImages(): void
+ {
+ for(let i=0; i
+ Êtes-vous sûr de vouloir supprimer l'annonce {{advert.title}} ?
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-delete-ad-advertiser/popup-delete-ad-advertiser.component.scss b/userAndAdvertiser/src/app/advertiser/adList/popup-delete-ad-advertiser/popup-delete-ad-advertiser.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-delete-ad-advertiser/popup-delete-ad-advertiser.component.spec.ts b/userAndAdvertiser/src/app/advertiser/adList/popup-delete-ad-advertiser/popup-delete-ad-advertiser.component.spec.ts
new file mode 100644
index 0000000..632a177
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-delete-ad-advertiser/popup-delete-ad-advertiser.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PopupDeleteAdAdvertiserComponent } from './popup-delete-ad-advertiser.component';
+
+describe('PopupDeleteAdComponent', () => {
+ let component: PopupDeleteAdAdvertiserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupDeleteAdAdvertiserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupDeleteAdAdvertiserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-delete-ad-advertiser/popup-delete-ad-advertiser.component.ts b/userAndAdvertiser/src/app/advertiser/adList/popup-delete-ad-advertiser/popup-delete-ad-advertiser.component.ts
new file mode 100644
index 0000000..c137557
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-delete-ad-advertiser/popup-delete-ad-advertiser.component.ts
@@ -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-advertiser',
+ templateUrl: './popup-delete-ad-advertiser.component.html',
+ styleUrls: ['./popup-delete-ad-advertiser.component.scss']
+})
+export class PopupDeleteAdAdvertiserComponent implements OnInit
+{
+ advert: any;
+
+
+ constructor( public dialogRef: MatDialogRef,
+ @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 {
+ this.dialogRef.close(true);
+ }
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component.html b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component.html
new file mode 100644
index 0000000..a768258
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component.html
@@ -0,0 +1,71 @@
+
+
+
{{advert.title}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Commentaire:
+
{{advert.comment}}
+
+
+
+
+
+
{{advert.views}}
+
+
+
+
+
+
+ {{ advert.createdAt | date:'dd/LL/YYYY à HH:mm:ss' }}
+
+
+
+
+
+
+
+ {{ advert.updatedAt | date:'dd/LL/YYYY à HH:mm:ss' }}
+
+
+
+
+
+
+
+ checked
+ close
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component.scss b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component.scss
new file mode 100644
index 0000000..3e00dee
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component.scss
@@ -0,0 +1,28 @@
+.lightTheme, .darkTheme {
+ background-image: none;
+}
+
+
+h1 {
+ text-align: center;
+ font-size: xx-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;
+}
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component.spec.ts b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component.spec.ts
new file mode 100644
index 0000000..56aedbc
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PopupVisualizeAdAdvertiserComponent } from './popup-visualize-ad-advertiser.component';
+
+describe('PopupVisualizeAdComponent', () => {
+ let component: PopupVisualizeAdAdvertiserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupVisualizeAdAdvertiserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupVisualizeAdAdvertiserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component.ts b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component.ts
new file mode 100644
index 0000000..80f5e09
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component.ts
@@ -0,0 +1,26 @@
+import {Component, Inject, OnInit} from '@angular/core';
+import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog";
+import {ThemeService} from "../../../utils/theme/theme.service";
+
+
+
+@Component({
+ selector: 'app-popup-visualize-ad-advertiser',
+ templateUrl: './popup-visualize-ad-advertiser.component.html',
+ styleUrls: ['./popup-visualize-ad-advertiser.component.scss']
+})
+export class PopupVisualizeAdAdvertiserComponent implements OnInit
+{
+ advert: any;
+
+ constructor( public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data,
+ public themeService: ThemeService,
+ public dialog: MatDialog ) { }
+
+ ngOnInit(): void
+ {
+ this.advert = this.data.advert;
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component.html b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component.html
new file mode 100644
index 0000000..dfbc2fe
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component.scss b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component.scss
new file mode 100644
index 0000000..eb60d48
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component.scss
@@ -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;
+}
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component.spec.ts b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component.spec.ts
new file mode 100644
index 0000000..25da0db
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PopupVisualizeImagesAdvertiserComponent } from './popup-visualize-images-advertiser.component';
+
+describe('PopupVisualizeImagesComponent', () => {
+ let component: PopupVisualizeImagesAdvertiserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupVisualizeImagesAdvertiserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupVisualizeImagesAdvertiserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component.ts b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component.ts
new file mode 100644
index 0000000..59e7c3d
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component.ts
@@ -0,0 +1,38 @@
+import {Component, Inject, OnInit} from '@angular/core';
+import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
+
+
+
+@Component({
+ selector: 'app-popup-visualize-images-advertiser',
+ templateUrl: './popup-visualize-images-advertiser.component.html',
+ styleUrls: ['./popup-visualize-images-advertiser.component.scss']
+})
+export class PopupVisualizeImagesAdvertiserComponent implements OnInit
+{
+ tabImages = [];
+ index: number = 0;
+ nbImage: number = 0;
+
+
+ constructor( public dialogRef: MatDialogRef,
+ @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;
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component.html b/userAndAdvertiser/src/app/advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component.html
new file mode 100644
index 0000000..9af3317
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
Entreprise:
+
{{advertiser.company}}
+
+
+
+
+
Pseudo:
+
{{advertiser.login}}
+
+
+
+
+
Mail:
+
{{advertiser.email}}
+
+
+
+
+
Date de création:
+
{{advertiser.createdAt | date:'dd/LL/YYYY'}}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component.scss b/userAndAdvertiser/src/app/advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component.scss
new file mode 100644
index 0000000..966c9a2
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component.scss
@@ -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;
+}
diff --git a/userAndAdvertiser/src/app/advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component.spec.ts b/userAndAdvertiser/src/app/advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component.spec.ts
new file mode 100644
index 0000000..ebb9617
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageProfilAdvertiserComponent } from './page-profil-advertiser.component';
+
+describe('PageProfilAdvertiserComponent', () => {
+ let component: PageProfilAdvertiserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageProfilAdvertiserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageProfilAdvertiserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component.ts b/userAndAdvertiser/src/app/advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component.ts
new file mode 100644
index 0000000..ee16328
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component.ts
@@ -0,0 +1,89 @@
+import { Component, OnInit } from '@angular/core';
+import {MatDialog} from "@angular/material/dialog";
+import {MatSnackBar} from "@angular/material/snack-bar";
+import {PopupUpdateAdvertiserComponent} from "../popup-update-advertiser/popup-update-advertiser.component";
+import {ThemeService} from "../../../utils/theme/theme.service";
+import {MessageService} from "../../../utils/message/message.service";
+import {ProfilService} from "../../../utils/profil/profil.service";
+
+
+
+@Component({
+ selector: 'app-page-profil-advertiser',
+ templateUrl: './page-profil-advertiser.component.html',
+ styleUrls: ['./page-profil-advertiser.component.scss']
+})
+export class PageProfilAdvertiserComponent implements OnInit
+{
+ advertiser = {
+ _id: "",
+ login: "",
+ hashPass: "",
+ email: "",
+ role: {
+ name: "advertiser",
+ permission: 5,
+ 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.advertiser = retour.data;
+ }
+ }
+
+
+ onModifier()
+ {
+ const config = {
+ width: '25%',
+ data: { advertiser: this.advertiser }
+ };
+ this.dialog
+ .open(PopupUpdateAdvertiserComponent, 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.advertiser = retour;
+ }
+ });
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component.html b/userAndAdvertiser/src/app/advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component.html
new file mode 100644
index 0000000..4951e5c
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component.html
@@ -0,0 +1,65 @@
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+ Entreprise
+
+
+
+
+
+ Pseudo
+
+
+
+
+
+
+
+
+ Modifier mot de passe:
+
+
+
+
+
+
+
+ Nouveau mot de passe
+
+
+
+
+
+ Confirmation nouveau mot de passe
+
+
+
+
+
+
+
+
+
+
+ {{errorMessage}}
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component.scss b/userAndAdvertiser/src/app/advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component.scss
new file mode 100644
index 0000000..1968e90
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component.scss
@@ -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;
+}
diff --git a/userAndAdvertiser/src/app/advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component.spec.ts b/userAndAdvertiser/src/app/advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component.spec.ts
new file mode 100644
index 0000000..dde7ef9
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PopupUpdateAdvertiserComponent } from './popup-update-advertiser.component';
+
+describe('PopupUpdateAdvertiserComponent', () => {
+ let component: PopupUpdateAdvertiserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupUpdateAdvertiserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupUpdateAdvertiserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component.ts b/userAndAdvertiser/src/app/advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component.ts
new file mode 100644
index 0000000..b93fdef
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component.ts
@@ -0,0 +1,124 @@
+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-advertiser',
+ templateUrl: './popup-update-advertiser.component.html',
+ styleUrls: ['./popup-update-advertiser.component.scss']
+})
+export class PopupUpdateAdvertiserComponent implements OnInit
+{
+ advertiserCopy;
+ newPassword: string = "";
+ confirmNewPassword: string = "" ;
+ changePassword: boolean = false ;
+ hasError: boolean = false;
+ errorMessage: string = "" ;
+
+
+ constructor( public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data,
+ private messageService: MessageService,
+ private profilService: ProfilService ) { }
+
+
+ ngOnInit(): void
+ {
+ const advertiser0 = this.data.advertiser;
+ this.advertiserCopy = {
+ _id: advertiser0._id,
+ login: advertiser0.login,
+ hashPass: advertiser0.hashPass,
+ email: advertiser0.email,
+ role: {
+ name: advertiser0.role.name,
+ permission: advertiser0.role.permission,
+ isAccepted: advertiser0.role.isAccepted,
+ },
+ profileImageUrl: advertiser0.profileImageUrl,
+ dateOfBirth: advertiser0.dateOfBirth,
+ gender: advertiser0.gender,
+ interests: [],
+ company: advertiser0.company,
+ isActive: advertiser0.isActive,
+ createdAt: advertiser0.createdAt,
+ updatedAt: advertiser0.updatedAt,
+ lastConnexion: new Date()
+ };
+ for(let interest of advertiser0.interests) this.advertiserCopy.interests.push(interest);
+ }
+
+
+ onValider()
+ {
+ this.checkField();
+ if(!this.hasError)
+ {
+ if(this.changePassword) this.advertiserCopy.hashPass = this.newPassword;
+ const data = {
+ login: this.advertiserCopy.login,
+ hashPass: this.advertiserCopy.hashPass,
+ email: this.advertiserCopy.email,
+ profileImageUrl: this.advertiserCopy.profileImageUrl,
+ company: this.advertiserCopy.company
+ };
+ 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.advertiserCopy.profileImageUrl);
+ this.dialogRef.close(this.advertiserCopy);
+ }
+ }
+
+
+ checkField()
+ {
+ if(this.advertiserCopy.login.length === 0) {
+ this.errorMessage = "Veuillez remplir le champ 'pseudo'" ;
+ this.hasError = true;
+ }
+ else if(this.advertiserCopy.email.length === 0) {
+ this.errorMessage = "Veuillez remplir le champ 'email'" ;
+ this.hasError = true;
+ }
+ else if(!this.isValidEmail(this.advertiserCopy.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);
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/advertiser/pages-popularity/pages-popularity.component.html b/userAndAdvertiser/src/app/advertiser/pages-popularity/pages-popularity.component.html
new file mode 100644
index 0000000..62ae0e1
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/pages-popularity/pages-popularity.component.html
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
Filtre
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ début
+
+
+ -
+
+
+
+ fin
+
+
+ -
+
+
+
+ pas d'affichage
+
+
+ -
+
+
+
+ unité du pas d'affichage
+
+ jour
+ semaine
+ mois
+
+
+
+
+
+
+
+ 0">
+ {{coupleNameViews.name}},
+
+
+ {{coupleNameViews.name}}
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/advertiser/pages-popularity/pages-popularity.component.scss b/userAndAdvertiser/src/app/advertiser/pages-popularity/pages-popularity.component.scss
new file mode 100644
index 0000000..00fb9e3
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/pages-popularity/pages-popularity.component.scss
@@ -0,0 +1,53 @@
+.myContainer {
+ font-size: small;
+ max-width: 100vw;
+ height: 100vh;
+ overflow-x: hidden;
+ overflow-y: scroll;
+}
+
+input {
+ font-size: small;
+ width: 140px;
+}
+
+.filtersContainer {
+ background-color: white;
+ width: 60%;
+ margin: 50px 50px 50px 50px;
+ padding: 20px 20px 20px 20px;
+}
+
+.chartContainer {
+ background-color: white;
+ border: solid 1px black;
+ padding: 10px 10px 10px 10px;
+ margin: 50px 50px 50px 50px;
+}
+
+
+// ---------------------------------------------
+// periode
+
+.periode {
+ padding: 10px 10px 0px 10px;
+}
+
+.periode .titleContainer {
+ text-align: right;
+ border-right: solid 1px #dcdcdc;
+ font-weight: bold;
+}
+
+.btnToutSelectionner {
+ font-size: small;
+}
+.btnToutDeselectionner {
+ font-size: small;
+}
+
+// -------------------------------------------------------------------------
+
+::ng-deep .mat-pseudo-checkbox-checked {
+ background-color: black !important;
+}
diff --git a/userAndAdvertiser/src/app/advertiser/pages-popularity/pages-popularity.component.spec.ts b/userAndAdvertiser/src/app/advertiser/pages-popularity/pages-popularity.component.spec.ts
new file mode 100644
index 0000000..f9ff236
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/pages-popularity/pages-popularity.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PagesPopularityComponent } from './pages-popularity.component';
+
+describe('SubjectsPopularityComponent', () => {
+ let component: PagesPopularityComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PagesPopularityComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PagesPopularityComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/advertiser/pages-popularity/pages-popularity.component.ts b/userAndAdvertiser/src/app/advertiser/pages-popularity/pages-popularity.component.ts
new file mode 100644
index 0000000..298ec8d
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/pages-popularity/pages-popularity.component.ts
@@ -0,0 +1,304 @@
+import { Component, OnInit } from '@angular/core';
+import {FormControl} from "@angular/forms";
+import {ChartDataSets} from "chart.js";
+import {Label} from "ng2-charts";
+import { Router} from "@angular/router";
+import {HttpParams} from "@angular/common/http";
+import {ThemeService} from "../../utils/theme/theme.service";
+import {MessageService} from "../../utils/message/message.service";
+
+
+
+interface CoupleNameViews {
+ name: string,
+ views: Date[],
+}
+
+
+
+@Component({
+ selector: 'app-subjects-popularity',
+ templateUrl: './pages-popularity.component.html',
+ styleUrls: ['./pages-popularity.component.scss']
+})
+export class PagesPopularityComponent implements OnInit
+{
+ formControl: FormControl = new FormControl();
+ allCoupleNameViews: CoupleNameViews[] = [];
+
+ allInterests: string[] = [];
+
+ startDate: Date = null;
+ endDate: Date = null;
+ step: number = 1;
+ stepUnity: string = "jour" ;
+
+ oneDay: number = 24*60*60*1000;
+ oneWeek: number = 7*24*60*60*1000;
+
+ lineChartData: ChartDataSets[] = [];
+ lineChartLabels: Label[] = [];
+ chartOptions: any = {
+ responsive: true,
+ scales: {
+ yAxes: [{ display: true, scaleLabel: { display: true, labelString: "vues" } }],
+ xAxes: [{ scaleLabel: { display: true, labelString: "temps" } }],
+ }
+ };
+
+ isDisplayable: boolean = false;
+
+
+ constructor( private router: Router,
+ public themeService: ThemeService,
+ private messageService: MessageService ) {}
+
+
+ // -----------------------------------------------------------------------------------------------------
+
+
+ ngOnInit(): void
+ {
+ // Sera excuté si on est sur la page 'adsPopularity'
+ // Remplie l'attribut 'allCoupleNameViews'
+ if(this.router.url.includes("ads"))
+ {
+ let params = new HttpParams();
+ params = params.append("isActive", true);
+ this.messageService
+ .get("ad/findAll", params )
+ .subscribe(ret => this.afterReceivingAds(ret), err => this.afterReceivingAds(err));
+ }
+
+ // Sera excuté si on est sur la page 'subjectsPopularity'
+ // Remplie l'attribut 'allCoupleNameViews'
+ else if(this.router.url.includes("subjects"))
+ {
+ this.messageService
+ .get("misc/getInterests")
+ .subscribe( retour => {
+
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else {
+ this.allInterests = retour.data.map(x => x.interest);
+ this.allInterests.sort();
+ this.messageService
+ .get("video/findAll")
+ .subscribe(ret => this.afterReceivingVideos(ret), err => this.afterReceivingVideos(err));
+ }
+ });
+ }
+ }
+
+
+ // Callback: Sera excuté si on est sur la page 'adsPopularity'
+ afterReceivingAds(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else {
+ const allAdverts = retour.data;
+ for(let advert of allAdverts)
+ {
+ let couple = {name: advert.title, views: advert.views }
+ this.allCoupleNameViews.push(couple);
+ }
+
+ this.formControl = new FormControl(this.allCoupleNameViews);
+ this.onApplyFilter();
+ }
+ }
+
+
+ // Callback: Sera excuté si on est sur la page 'subjectsPopularity'
+ afterReceivingVideos(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else {
+ const allVideos = retour.data;
+ let myMap: Map = new Map();
+
+ // parcours des interest de chaque video
+ for(let video of allVideos)
+ {
+ const key = video.interest;
+ if(!myMap.has(key)) myMap.set(key, video.watchedDates);
+ else {
+ let tabDate = myMap.get(key);
+ for(let date0 of video.watchedDates) tabDate = this.insertInOrder(tabDate, date0);
+ myMap.set(key, tabDate);
+ }
+ }
+
+ // parcours les interest qui n'ont pas p été vu dans les videos
+ for(let interest of this.allInterests)
+ {
+ if(!myMap.has(interest)) myMap.set(interest, []);
+ }
+
+ // parcours de la map pour remplir 'allCoupleNameViews'
+ for(const [key, value] of myMap.entries())
+ {
+ let couple = {name: key, views: value }
+ this.allCoupleNameViews.push(couple);
+ }
+
+ this.formControl = new FormControl(this.allCoupleNameViews);
+ this.onApplyFilter();
+ }
+ }
+
+
+ // -----------------------------------------------------------------------------------------------------
+
+
+ // Applique le filtre
+ onApplyFilter(): void
+ {
+ // --- initialisation ---
+ this.lineChartData = [];
+ this.lineChartLabels = [];
+
+ if(this.step <= 0) this.step = 0;
+ if((this.endDate === null) || (this.endDate === undefined)) this.endDate = new Date();
+ if((this.startDate === null) || (this.startDate === undefined)) this.startDate = new Date(this.endDate.getTime() - this.oneWeek); // date d'il y a une semaine
+
+ const startTime = this.startDate.getTime();
+ const endTime = this.endDate.getTime();
+
+
+ // --- remplissage de 'lineChartLabels' ---
+ let dataWithZeros = [];
+ let time = startTime;
+ const intervals = [];
+ while(time <= endTime)
+ {
+ dataWithZeros.push(0);
+ this.lineChartLabels.push(this.getLabel(new Date(time)));
+ intervals.push(time);
+ time = this.addStep(time);
+ }
+ intervals.push(time);
+
+
+ // --- remplissage de 'lineChartLabels' ---
+ for(let coupleNameViews of this.formControl.value)
+ {
+ let data = dataWithZeros.slice();
+ let label = coupleNameViews.name;
+ let index = 0;
+
+ for(let date0 of coupleNameViews.views)
+ {
+ const time0 = (new Date(date0)).getTime();
+
+ if(time0 > endTime) break;
+
+ if((startTime <= time0) && (time0 <= endTime))
+ {
+ while((index < intervals.length) && (time0 >= intervals[index])) index += 1;
+ index = index - 1;
+ data[index] += 1;
+ }
+ }
+
+ this.lineChartData.push({"data": data.slice(), "label": label});
+ }
+ this.isDisplayable = true;
+ }
+
+
+ onNewStartDate(event): void {
+ this.startDate = new Date(event);
+ }
+
+
+ onNewEndDate(event): void {
+ this.endDate = new Date(event);
+ }
+
+
+ // Renvoie le bon label pour le graph
+ getLabel(date0: Date): string
+ {
+ if((this.stepUnity === 'jour') && (this.step === 1))
+ {
+ return date0.toLocaleDateString();
+ }
+ else {
+ const time2 = this.addStep((new Date(date0)).getTime()) - this.oneDay;
+ let date2 = new Date(time2);
+ return date0.toLocaleDateString() + " à " + date2.toLocaleDateString();
+ }
+ }
+
+
+ // Ajoute le bon pas à la date 'new Date(time)'
+ addStep(time: number): number
+ {
+ let newDate;
+
+ if(this.stepUnity === 'jour') {
+ newDate = new Date(time + this.step*this.oneDay);
+ }
+ else if(this.stepUnity === 'semaine') {
+ newDate = new Date(time + this.step*this.oneWeek);
+ }
+ else
+ {
+ const oldDate = new Date(time);
+
+ let newMonth = oldDate.getMonth() + this.step;
+ const newYear = oldDate.getFullYear() + (newMonth / 12);
+ newMonth = newMonth % 12;
+ const day = this.startDate.getDate();
+
+ if((newMonth === 1) && ([29, 30, 31].includes(day))) { // si fevrier et si jour n'existe pas
+ newDate = new Date(newYear, newMonth, 28);
+ }
+ else if((day === 31) && ([3, 5, 9, 10].includes(newMonth))) { // si 31 et mois à 30 jours
+ newDate = new Date(newYear, newMonth, 30);
+ }
+ else {
+ newDate = new Date(newYear, newMonth, day);
+ }
+ }
+
+ const _1h = 60*60*1000;
+ if(newDate.getHours() === 23) return newDate.getTime() + _1h;
+ else if(newDate.getHours() === 1) return newDate.getTime() - _1h;
+ else return newDate.getTime();
+ }
+
+
+ // Insere la date0 dans le tableau tabDate par ordre croissant
+ insertInOrder(tabDate: Date[], date0: Date): Date[]
+ {
+ let i = 0;
+ let n = tabDate.length;
+ let time0 = (new Date(date0)).getTime();
+
+ while((i (new Date(tabDate[i])).getTime())) i++;
+ if(i === n) tabDate.push(date0);
+ else tabDate.splice(i, 0, date0);
+
+ return tabDate;
+ }
+
+
+ onSelectAll(): void
+ {
+ this.formControl = new FormControl(this.allCoupleNameViews);
+ }
+
+ onDeSelectAll(): void
+ {
+ this.formControl = new FormControl([]);
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/advertiser/utils/dragAndDrop/drag-and-drop.directive.spec.ts b/userAndAdvertiser/src/app/advertiser/utils/dragAndDrop/drag-and-drop.directive.spec.ts
new file mode 100644
index 0000000..60cf3d6
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/utils/dragAndDrop/drag-and-drop.directive.spec.ts
@@ -0,0 +1,8 @@
+import { DragAndDropDirective } from './drag-and-drop.directive';
+
+describe('DragAndDropDirective', () => {
+ it('should create an instance', () => {
+ const directive = new DragAndDropDirective();
+ expect(directive).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/advertiser/utils/dragAndDrop/drag-and-drop.directive.ts b/userAndAdvertiser/src/app/advertiser/utils/dragAndDrop/drag-and-drop.directive.ts
new file mode 100644
index 0000000..b3d1162
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/utils/dragAndDrop/drag-and-drop.directive.ts
@@ -0,0 +1,36 @@
+import {Directive, EventEmitter, HostBinding, HostListener, Output} from '@angular/core';
+
+@Directive({
+ selector: '[appDragAndDrop]'
+})
+export class DragAndDropDirective
+{
+ @HostBinding('class.fileover') fileOver: boolean;
+ @Output() fileDropped = new EventEmitter();
+
+ // Dragover listener
+ @HostListener('dragover', ['$event']) onDragOver(evt) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ this.fileOver = true;
+ }
+
+ // Dragleave listener
+ @HostListener('dragleave', ['$event']) public onDragLeave(evt) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ this.fileOver = false;
+ }
+
+ // Drop listener
+ @HostListener('drop', ['$event']) public ondrop(evt) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ this.fileOver = false;
+ let files = evt.dataTransfer.files;
+ if (files.length > 0) {
+ this.fileDropped.emit(files);
+ }
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/advertiser/utils/navbar-advertiser/navbar-advertiser.component.html b/userAndAdvertiser/src/app/advertiser/utils/navbar-advertiser/navbar-advertiser.component.html
new file mode 100644
index 0000000..da5e898
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/utils/navbar-advertiser/navbar-advertiser.component.html
@@ -0,0 +1,41 @@
+
diff --git a/userAndAdvertiser/src/app/advertiser/utils/navbar-advertiser/navbar-advertiser.component.scss b/userAndAdvertiser/src/app/advertiser/utils/navbar-advertiser/navbar-advertiser.component.scss
new file mode 100644
index 0000000..285d629
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/utils/navbar-advertiser/navbar-advertiser.component.scss
@@ -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;
+}
diff --git a/userAndAdvertiser/src/app/advertiser/utils/navbar-advertiser/navbar-advertiser.component.spec.ts b/userAndAdvertiser/src/app/advertiser/utils/navbar-advertiser/navbar-advertiser.component.spec.ts
new file mode 100644
index 0000000..fb00a09
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/utils/navbar-advertiser/navbar-advertiser.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { NavbarAdvertiserComponent } from './navbar-advertiser.component';
+
+describe('NavbarAdvertiserComponent', () => {
+ let component: NavbarAdvertiserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ NavbarAdvertiserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(NavbarAdvertiserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/advertiser/utils/navbar-advertiser/navbar-advertiser.component.ts b/userAndAdvertiser/src/app/advertiser/utils/navbar-advertiser/navbar-advertiser.component.ts
new file mode 100644
index 0000000..c65b5fe
--- /dev/null
+++ b/userAndAdvertiser/src/app/advertiser/utils/navbar-advertiser/navbar-advertiser.component.ts
@@ -0,0 +1,41 @@
+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-advertiser',
+ templateUrl: './navbar-advertiser.component.html',
+ styleUrls: ['./navbar-advertiser.component.scss']
+})
+export class NavbarAdvertiserComponent
+{
+ routes: string[] = [
+ "/advertiser", // 0
+ "/advertiser/adList", // 1
+ "/advertiser/adsPopularity", // 2
+ "/advertiser/subjectsPopularity", // 3
+ "/advertiser/myProfil" // 4
+ ];
+
+ 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);
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/app-routing.module.ts b/userAndAdvertiser/src/app/app-routing.module.ts
new file mode 100644
index 0000000..d1888ec
--- /dev/null
+++ b/userAndAdvertiser/src/app/app-routing.module.ts
@@ -0,0 +1,41 @@
+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 {PageSearchComponent} from "./user/search/page-search/page-search.component";
+import {PageMyPlaylistsComponent} from "./user/myPlaylists/page-my-playlists/page-my-playlists.component";
+import {PageProfilUserComponent} from "./user/myProfil/page-profil-user/page-profil-user.component";
+import {PageWatchingVideoComponent} from "./user/watching/page-watching-video/page-watching-video.component";
+import {PageHistoryUserComponent} from "./user/history/page-history-user/page-history-user.component";
+import {PageAdListAdvertiserComponent} from "./advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component";
+import {PagesPopularityComponent} from "./advertiser/pages-popularity/pages-popularity.component";
+import {PageProfilAdvertiserComponent} from "./advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component";
+
+
+const routes: Routes = [
+ // Before connexion
+ { path: '', component: PageLoginComponent },
+ { path: 'login', component: PageLoginComponent },
+ { path: 'register', component: PageRegisterComponent },
+
+ // User
+ { path: 'user', component: PageSearchComponent },
+ { path: 'user/search', component: PageSearchComponent },
+ { path: 'user/myPlaylists', component: PageMyPlaylistsComponent },
+ { path: 'user/history', component: PageHistoryUserComponent },
+ { path: 'user/myProfil', component: PageProfilUserComponent },
+ { path: 'user/watching', component: PageWatchingVideoComponent },
+
+ // Advertiser
+ { path: 'advertiser', component: PageAdListAdvertiserComponent },
+ { path: 'advertiser/adList', component: PageAdListAdvertiserComponent },
+ { path: 'advertiser/myProfil', component: PageProfilAdvertiserComponent },
+ { path: 'advertiser/adsPopularity', component: PagesPopularityComponent },
+ { path: 'advertiser/subjectsPopularity', component: PagesPopularityComponent },
+];
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes)],
+ exports: [RouterModule]
+})
+export class AppRoutingModule { }
diff --git a/userAndAdvertiser/src/app/app.component.html b/userAndAdvertiser/src/app/app.component.html
new file mode 100644
index 0000000..d5d92f3
--- /dev/null
+++ b/userAndAdvertiser/src/app/app.component.html
@@ -0,0 +1,2 @@
+
+
diff --git a/userAndAdvertiser/src/app/app.component.scss b/userAndAdvertiser/src/app/app.component.scss
new file mode 100644
index 0000000..22a5665
--- /dev/null
+++ b/userAndAdvertiser/src/app/app.component.scss
@@ -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;
+}
diff --git a/userAndAdvertiser/src/app/app.component.spec.ts b/userAndAdvertiser/src/app/app.component.spec.ts
new file mode 100644
index 0000000..ab837bf
--- /dev/null
+++ b/userAndAdvertiser/src/app/app.component.spec.ts
@@ -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 'userAndAdvertiser'`, () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.componentInstance;
+ expect(app.title).toEqual('userAndAdvertiser');
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement as HTMLElement;
+ expect(compiled.querySelector('.content span')?.textContent).toContain('userAndAdvertiser app is running!');
+ });
+});
diff --git a/userAndAdvertiser/src/app/app.component.ts b/userAndAdvertiser/src/app/app.component.ts
new file mode 100644
index 0000000..945afdf
--- /dev/null
+++ b/userAndAdvertiser/src/app/app.component.ts
@@ -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 = 'userAndAdvertiser';
+}
diff --git a/userAndAdvertiser/src/app/app.module.ts b/userAndAdvertiser/src/app/app.module.ts
new file mode 100644
index 0000000..dc1e214
--- /dev/null
+++ b/userAndAdvertiser/src/app/app.module.ts
@@ -0,0 +1,131 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+import { AppRoutingModule } from './app-routing.module';
+import { AppComponent } from './app.component';
+import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
+import {MatSlideToggleModule} from "@angular/material/slide-toggle";
+import {FormsModule, ReactiveFormsModule} from "@angular/forms";
+import {HttpClientModule} from "@angular/common/http";
+import {MatIconModule} from "@angular/material/icon";
+import {MatDialogModule} from "@angular/material/dialog";
+import {MatButtonModule} from "@angular/material/button";
+import {MatInputModule} from "@angular/material/input";
+import {MatCheckboxModule} from "@angular/material/checkbox";
+import {MatFormFieldModule} from "@angular/material/form-field";
+import {MatDividerModule} from "@angular/material/divider";
+import {MatSnackBarModule} from "@angular/material/snack-bar";
+import {MatGridListModule} from "@angular/material/grid-list";
+import {MatTableModule} from "@angular/material/table";
+import {MatSortModule} from "@angular/material/sort";
+import {MatChipsModule} from "@angular/material/chips";
+import {MatSelectModule} from "@angular/material/select";
+import {MatAutocompleteModule} from "@angular/material/autocomplete";
+import {MatRadioModule} from "@angular/material/radio";
+import {MatPaginatorModule} from "@angular/material/paginator";
+import {MatDatepickerModule} from "@angular/material/datepicker";
+import { ChartsModule } from 'ng2-charts';
+import {DragAndDropComponent} from "./advertiser/adList/drag-and-drop/drag-and-drop.component";
+import {InputInterestsAdComponent} from "./advertiser/adList/input-interests-ad/input-interests-ad.component";
+import {PageAdListAdvertiserComponent} from "./advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component";
+import {PopupAddOrUpdateAdComponent} from "./advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component";
+import {PopupDeleteAdAdvertiserComponent} from "./advertiser/adList/popup-delete-ad-advertiser/popup-delete-ad-advertiser.component";
+import {PopupVisualizeAdAdvertiserComponent} from "./advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component";
+import {PopupVisualizeImagesAdvertiserComponent} from "./advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component";
+import {NavbarAdvertiserComponent} from "./advertiser/utils/navbar-advertiser/navbar-advertiser.component";
+import {DragAndDropDirective} from "./advertiser/utils/dragAndDrop/drag-and-drop.directive";
+import {PageProfilAdvertiserComponent} from "./advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component";
+import {PopupUpdateAdvertiserComponent} from "./advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component";
+import {PagesPopularityComponent} from "./advertiser/pages-popularity/pages-popularity.component";
+import {NavbarUserComponent} from "./user/utils/components/navbar-user/navbar-user.component";
+import {PageHistoryUserComponent} from "./user/history/page-history-user/page-history-user.component";
+import {PageMyPlaylistsComponent} from "./user/myPlaylists/page-my-playlists/page-my-playlists.component";
+import {PlaylistListComponent} from "./user/myPlaylists/playlist-list/playlist-list.component";
+import {PopupCreateOrUpdatePlaylistComponent} from "./user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component";
+import {PopupDeletePlaylistComponent} from "./user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component";
+import {VideoListComponent} from "./user/myPlaylists/video-list/video-list.component";
+import {InputInterestsProfilComponent} from "./user/myProfil/input-interests-profil/input-interests-profil.component";
+import {PageProfilUserComponent} from "./user/myProfil/page-profil-user/page-profil-user.component";
+import {PopupUpdateUserComponent} from "./user/myProfil/popup-update-user/popup-update-user.component";
+import {PageSearchComponent} from "./user/search/page-search/page-search.component";
+import {VideoGridComponent} from "./user/search/video-grid/video-grid.component";
+import {PageWatchingVideoComponent} from "./user/watching/page-watching-video/page-watching-video.component";
+import {AdvertComponent} from "./user/utils/components/advert/advert.component";
+import {PopupAddVideoToPlaylistsComponent} from "./user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component";
+import {PageLoginComponent} from "./beforeConnexion/login/page-login/page-login.component";
+import {PopupForgottenPasswordComponent} from "./beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component";
+import {InputInterestsRegisterComponent} from "./beforeConnexion/register/input-interests-register/input-interests-register.component";
+import {PageRegisterComponent} from "./beforeConnexion/register/page-register/page-register.component";
+import {PopupConfirmationComponent} from "./beforeConnexion/register/popup-confirmation/popup-confirmation.component";
+import {NavbarBeforeConnexionComponent} from "./beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component";
+import {MatStepperModule} from "@angular/material/stepper";
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ DragAndDropDirective,
+ NavbarBeforeConnexionComponent,
+ PageLoginComponent,
+ PopupForgottenPasswordComponent,
+ InputInterestsRegisterComponent,
+ PageRegisterComponent,
+ PopupConfirmationComponent,
+ NavbarAdvertiserComponent,
+ DragAndDropComponent,
+ InputInterestsAdComponent,
+ PageAdListAdvertiserComponent,
+ PopupAddOrUpdateAdComponent,
+ PopupDeleteAdAdvertiserComponent,
+ PopupVisualizeAdAdvertiserComponent,
+ PopupVisualizeImagesAdvertiserComponent,
+ PageProfilAdvertiserComponent,
+ PopupUpdateAdvertiserComponent,
+ PagesPopularityComponent,
+ NavbarUserComponent,
+ PageHistoryUserComponent,
+ PageMyPlaylistsComponent,
+ PlaylistListComponent,
+ PopupCreateOrUpdatePlaylistComponent,
+ PopupDeletePlaylistComponent,
+ VideoListComponent,
+ InputInterestsProfilComponent,
+ PageProfilUserComponent,
+ PopupUpdateUserComponent,
+ PageSearchComponent,
+ VideoGridComponent,
+ PageWatchingVideoComponent,
+ AdvertComponent,
+ PopupAddVideoToPlaylistsComponent
+ ],
+ imports: [
+ BrowserModule,
+ AppRoutingModule,
+ BrowserAnimationsModule,
+ MatSlideToggleModule,
+ FormsModule,
+ HttpClientModule,
+ MatDialogModule,
+ MatButtonModule,
+ MatIconModule,
+ MatInputModule,
+ MatDividerModule,
+ MatCheckboxModule,
+ MatFormFieldModule,
+ MatSnackBarModule,
+ MatGridListModule,
+ MatTableModule,
+ MatSortModule,
+ MatChipsModule,
+ ReactiveFormsModule,
+ MatAutocompleteModule,
+ MatSelectModule,
+ MatRadioModule,
+ MatPaginatorModule,
+ MatDatepickerModule,
+ ChartsModule,
+ MatStepperModule
+ ],
+ providers: [],
+ bootstrap: [AppComponent]
+})
+export class AppModule { }
diff --git a/userAndAdvertiser/src/app/beforeConnexion/login/page-login/page-login.component.html b/userAndAdvertiser/src/app/beforeConnexion/login/page-login/page-login.component.html
new file mode 100644
index 0000000..675270e
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/login/page-login/page-login.component.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
StreamNotFound
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/beforeConnexion/login/page-login/page-login.component.scss b/userAndAdvertiser/src/app/beforeConnexion/login/page-login/page-login.component.scss
new file mode 100644
index 0000000..8924202
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/login/page-login/page-login.component.scss
@@ -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%;
+}
diff --git a/userAndAdvertiser/src/app/beforeConnexion/login/page-login/page-login.component.spec.ts b/userAndAdvertiser/src/app/beforeConnexion/login/page-login/page-login.component.spec.ts
new file mode 100644
index 0000000..a4ee677
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/login/page-login/page-login.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageLoginComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageLoginComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/beforeConnexion/login/page-login/page-login.component.ts b/userAndAdvertiser/src/app/beforeConnexion/login/page-login/page-login.component.ts
new file mode 100644
index 0000000..555e496
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/login/page-login/page-login.component.ts
@@ -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;
+ }
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.html b/userAndAdvertiser/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.html
new file mode 100644
index 0000000..c34b58e
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.html
@@ -0,0 +1,24 @@
+Récupération du mot de passe
+
+
+
+
+
+
+ Email
+
+
+
+
+
+
+ {{errorMessage}}
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.scss b/userAndAdvertiser/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.scss
new file mode 100644
index 0000000..fa75013
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.scss
@@ -0,0 +1,12 @@
+h4 {
+ text-align: center;
+}
+
+.myDiv {
+ text-align: center;
+ font-size: small;
+}
+
+.myError {
+ text-align: center;
+}
diff --git a/userAndAdvertiser/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.spec.ts b/userAndAdvertiser/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.spec.ts
new file mode 100644
index 0000000..ebf101c
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupForgottenPasswordComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupForgottenPasswordComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.ts b/userAndAdvertiser/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.ts
new file mode 100644
index 0000000..1ff70ce
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.ts
@@ -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) {}
+
+
+ // 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);
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.html b/userAndAdvertiser/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.html
new file mode 100644
index 0000000..2a7c484
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.html
@@ -0,0 +1,43 @@
+
+
+
+
+ Centres d'intérêt
+
+
+
+
+
+
+ {{interest}}
+
+
+
+
+
+
+
+
+
+
+
+ {{interest}}
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.scss b/userAndAdvertiser/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/userAndAdvertiser/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.spec.ts b/userAndAdvertiser/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.spec.ts
new file mode 100644
index 0000000..9917b1a
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ InputInterestsRegisterComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(InputInterestsRegisterComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.ts b/userAndAdvertiser/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.ts
new file mode 100644
index 0000000..8dba84e
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.ts
@@ -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;
+ @Input() myInterests: string[] = [];
+ allInterests: string[] = [];
+ @Output() eventEmitter = new EventEmitter();
+ @ViewChild('tagInput') tagInput: ElementRef;
+ 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));
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/beforeConnexion/register/page-register/page-register.component.html b/userAndAdvertiser/src/app/beforeConnexion/register/page-register/page-register.component.html
new file mode 100644
index 0000000..fb6e3ac
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/register/page-register/page-register.component.html
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Entreprise
+
+
+
+
+
+
+ Pseudo
+
+
+
+
+
+
+ Email
+
+
+
+
+
+
+ Mot de passe
+
+
+
+
+
+
+ Confirmation mot de passe
+
+
+
+
diff --git a/userAndAdvertiser/src/app/beforeConnexion/register/page-register/page-register.component.scss b/userAndAdvertiser/src/app/beforeConnexion/register/page-register/page-register.component.scss
new file mode 100644
index 0000000..5f0dc53
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/register/page-register/page-register.component.scss
@@ -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;
+}
diff --git a/userAndAdvertiser/src/app/beforeConnexion/register/page-register/page-register.component.spec.ts b/userAndAdvertiser/src/app/beforeConnexion/register/page-register/page-register.component.spec.ts
new file mode 100644
index 0000000..5cff194
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/register/page-register/page-register.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageRegisterComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageRegisterComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/beforeConnexion/register/page-register/page-register.component.ts b/userAndAdvertiser/src/app/beforeConnexion/register/page-register/page-register.component.ts
new file mode 100644
index 0000000..788c8be
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/register/page-register/page-register.component.ts
@@ -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;
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.html b/userAndAdvertiser/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.html
new file mode 100644
index 0000000..1cd51fe
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.html
@@ -0,0 +1,11 @@
+
+ Votre inscription a bien été effectuée.
+
+
+
+ Votre inscription est en cours de validation.
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.scss b/userAndAdvertiser/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.scss
new file mode 100644
index 0000000..85730e0
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.scss
@@ -0,0 +1,7 @@
+p {
+ font-size: small;
+}
+
+div {
+ font-size: small;
+}
diff --git a/userAndAdvertiser/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.spec.ts b/userAndAdvertiser/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.spec.ts
new file mode 100644
index 0000000..d6f9908
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupConfirmationComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupConfirmationComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.ts b/userAndAdvertiser/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.ts
new file mode 100644
index 0000000..59e3325
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.ts
@@ -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,
+ @Inject(MAT_DIALOG_DATA) public data) {}
+}
diff --git a/userAndAdvertiser/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.html b/userAndAdvertiser/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.html
new file mode 100644
index 0000000..d4ad9f5
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.scss b/userAndAdvertiser/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.scss
new file mode 100644
index 0000000..e1fefaa
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.scss
@@ -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;
+}
diff --git a/userAndAdvertiser/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.spec.ts b/userAndAdvertiser/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.spec.ts
new file mode 100644
index 0000000..f3f7f27
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.spec.ts
@@ -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;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ NavbarBeforeConnexionComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(NavbarBeforeConnexionComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.ts b/userAndAdvertiser/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.ts
new file mode 100644
index 0000000..4a3f05e
--- /dev/null
+++ b/userAndAdvertiser/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.ts
@@ -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";
+}
diff --git a/userAndAdvertiser/src/app/user/history/page-history-user/page-history-user.component.html b/userAndAdvertiser/src/app/user/history/page-history-user/page-history-user.component.html
new file mode 100644
index 0000000..97f7344
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/history/page-history-user/page-history-user.component.html
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Aperçu |
+
+
+ 
+ ![]()
+
+ |
+
+
+
+
+ Titre |
+ {{video.title}} |
+
+
+
+
+ Date |
+
+ {{video.date | date:'dd/LL/YYYY à HH:mm:ss'}}
+ |
+
+
+
+
+ Source |
+ {{video.source}} |
+
+
+
+
+ Action |
+
+
+ |
+
+
+
+
+
+ | Aucune vidéo ne correspond au filtre: "{{input.value}}" |
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/history/page-history-user/page-history-user.component.scss b/userAndAdvertiser/src/app/user/history/page-history-user/page-history-user.component.scss
new file mode 100644
index 0000000..bbd894d
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/history/page-history-user/page-history-user.component.scss
@@ -0,0 +1,46 @@
+.myContainer {
+ max-width: 100vw;
+ height: 100vh;
+ overflow-x: hidden;
+}
+
+table {
+ width: 80%;
+ margin: 0 auto;
+}
+
+
+th.mat-sort-header-sorted {
+ color: black;
+}
+
+
+input {
+ width: 35%;
+ font-size: large;
+}
+
+// -------------------------------------------------------
+
+.imgsContainer {
+ position: relative;
+ width: 20vw;
+ height: 15vh;
+ cursor: pointer;
+}
+
+.imgPlay {
+ position: absolute;
+ margin-left: 9vw;
+ width: 3vw;
+ margin-top: 5vh;
+ height: 6vh;
+ padding: 0px 0px 0px 0px;
+}
+
+.imgVideo {
+ border: solid 1px black;
+ width: 20vw;
+ height: 15vh;
+ padding: 0px 0px 0px 0px;
+}
diff --git a/userAndAdvertiser/src/app/user/history/page-history-user/page-history-user.component.spec.ts b/userAndAdvertiser/src/app/user/history/page-history-user/page-history-user.component.spec.ts
new file mode 100644
index 0000000..9fd31c3
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/history/page-history-user/page-history-user.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageHistoryUserComponent } from './page-history-user.component';
+
+describe('PageHistoriqueComponent', () => {
+ let component: PageHistoryUserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageHistoryUserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageHistoryUserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/history/page-history-user/page-history-user.component.ts b/userAndAdvertiser/src/app/user/history/page-history-user/page-history-user.component.ts
new file mode 100644
index 0000000..5485ec1
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/history/page-history-user/page-history-user.component.ts
@@ -0,0 +1,113 @@
+import {AfterViewInit, Component, ViewChild} from '@angular/core';
+import {MatTableDataSource} from "@angular/material/table";
+import {MatSort} from "@angular/material/sort";
+import {MatPaginator} from "@angular/material/paginator";
+import {Router} from "@angular/router";
+import {MessageService} from "../../../utils/message/message.service";
+import {ThemeService} from "../../../utils/theme/theme.service";
+
+
+
+@Component({
+ selector: 'app-page-history-user',
+ templateUrl: './page-history-user.component.html',
+ styleUrls: ['./page-history-user.component.scss']
+})
+export class PageHistoryUserComponent implements AfterViewInit
+{
+ displayedColumns: string[] = [ 'aperçu', 'title', 'date', 'source', 'action' ];
+ dataSource ;
+ @ViewChild(MatSort) sort: MatSort;
+ @ViewChild(MatPaginator) paginator: MatPaginator;
+
+
+ constructor( public themeService: ThemeService,
+ private messageService: MessageService,
+ private router: Router ) { }
+
+
+ // charge la page
+ ngAfterViewInit(): void
+ {
+ this.messageService
+ .get("user/history")
+ .subscribe(ret => this.ngAfterViewInitCallback(ret), err => this.ngAfterViewInitCallback(err));
+ }
+
+
+ ngAfterViewInitCallback(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else {
+ const tabVideoHistory = retour.data.map( video => {
+ return {
+ _id: video._id,
+ videoId: video.videoId,
+ imageUrl: video.imageUrl,
+ title: video.title,
+ date: video.watchedDate,
+ source: video.source,
+ }
+ });
+ this.dataSource = new MatTableDataSource(tabVideoHistory);
+ this.dataSource.sort = this.sort;
+ this.dataSource.paginator = this.paginator;
+ this.dataSource = this.dataSource;
+ }
+ }
+
+
+ // Applique le filtre
+ applyFilter(event: Event): void
+ {
+ const filterValue = (event.target as HTMLInputElement).value;
+ this.dataSource.filter = filterValue.trim().toLowerCase();
+ }
+
+
+ // Supprime la video
+ onDelete(video: any): void
+ {
+ this.messageService
+ .put("video/update/"+video._id, { watchedDates: []})
+ .subscribe(ret => this.onDeleteCallback(ret, video), err => this.onDeleteCallback(err, video))
+ }
+
+
+ onDeleteCallback(retour: any, video: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else {
+ const index = this.dataSource.data.indexOf(video);
+ this.dataSource.data.splice(index, 1);
+ this.dataSource.data = this.dataSource.data;
+ this.dataSource = this.dataSource;
+ }
+ }
+
+
+ onVideo(video): void
+ {
+ this.messageService
+ .put("video/update/"+video._id, {watchedDate: true})
+ .subscribe(ret => this.onVideoCallback(ret), err => this.onVideoCallback(err));
+
+ const params = {
+ videoId: video.videoId,
+ source: video.source,
+ from: "history",
+ };
+ this.router.navigate(['/user/watching'], { queryParams: params });
+ }
+
+
+ onVideoCallback(retour: any): void
+ {
+ if(retour.status !== "success") console.log(retour);
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/page-my-playlists/page-my-playlists.component.html b/userAndAdvertiser/src/app/user/myPlaylists/page-my-playlists/page-my-playlists.component.html
new file mode 100644
index 0000000..c92a060
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/page-my-playlists/page-my-playlists.component.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/page-my-playlists/page-my-playlists.component.scss b/userAndAdvertiser/src/app/user/myPlaylists/page-my-playlists/page-my-playlists.component.scss
new file mode 100644
index 0000000..fad665f
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/page-my-playlists/page-my-playlists.component.scss
@@ -0,0 +1,48 @@
+.lightTheme {
+ border-color: black;
+}
+.darkTheme {
+ border-color: white;
+}
+.myContainer {
+ text-align: center;
+ max-width: 100vw;
+ height: 100vh;
+ overflow-x: hidden;
+}
+
+// Liste des vidéos -------------------------------------------------
+
+.celluleListeVideo {
+ margin: 0px 0px 0px 0px;
+}
+
+// Liste des playlists ---------------------------------------------
+
+.celluleListePlaylist {
+ margin: 0px 0px 0px 0px;
+}
+
+// Pub -------------------------------------------------------------
+
+.cellulePub {
+ padding: 0px 10px 0px 10px;
+ width: 100%;
+ text-align: center;
+ justify-content: center;
+}
+
+.conteneurPub {
+ //height: 85vh;
+ text-align: center;
+ justify-content: center;
+ vertical-align: middle;
+ display: block;
+ width: 75%;
+ margin-left: auto;
+ margin-right: auto;
+ position: absolute;
+ top: 50%;
+ -ms-transform: translateY(-50%);
+ transform: translateY(-50%);
+}
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/page-my-playlists/page-my-playlists.component.spec.ts b/userAndAdvertiser/src/app/user/myPlaylists/page-my-playlists/page-my-playlists.component.spec.ts
new file mode 100644
index 0000000..2dba23b
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/page-my-playlists/page-my-playlists.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageMyPlaylistsComponent } from './page-my-playlists.component';
+
+describe('PageMesPlaylistsComponent', () => {
+ let component: PageMyPlaylistsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageMyPlaylistsComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageMyPlaylistsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/page-my-playlists/page-my-playlists.component.ts b/userAndAdvertiser/src/app/user/myPlaylists/page-my-playlists/page-my-playlists.component.ts
new file mode 100644
index 0000000..0b94523
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/page-my-playlists/page-my-playlists.component.ts
@@ -0,0 +1,68 @@
+import { Component, OnInit } from '@angular/core';
+import {HttpParams} from "@angular/common/http";
+import {ThemeService} from "../../../utils/theme/theme.service";
+import {MessageService} from "../../../utils/message/message.service";
+
+
+
+@Component({
+ selector: 'app-page-my-playlists',
+ templateUrl: './page-my-playlists.component.html',
+ styleUrls: ['./page-my-playlists.component.scss']
+})
+export class PageMyPlaylistsComponent implements OnInit
+{
+ ad; // pub
+ playlist: any; // la playlist sélectionnée
+
+
+ constructor( public themeService: ThemeService,
+ private messageService: MessageService ) { }
+
+
+ ngOnInit(): void
+ {
+ let params = new HttpParams();
+ params = params.append("quantity", 1);
+ this.messageService
+ .get("user/ad", params)
+ .subscribe(ret => this.adCallback(ret), err => this.adCallback(err));
+ }
+
+
+ adCallback(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else {
+ this.ad = retour.data[0];
+ }
+ }
+
+
+ transmitPlaylistToVideoList(playlist): void
+ {
+ if ((playlist === null) || (playlist === undefined)) {
+ this.playlist = playlist;
+ }
+ else {
+ this.messageService
+ .get("playlist/findOne/" + playlist.id)
+ .subscribe(ret => this.afterReceivingPlaylistWithVideo(ret, playlist), err => this.afterReceivingPlaylistWithVideo(err, playlist));
+ }
+ }
+
+
+ afterReceivingPlaylistWithVideo(retour: any, playlist): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ this.playlist = playlist;
+ }
+ else {
+ this.playlist = retour.data;
+ }
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/playlist-list/playlist-list.component.html b/userAndAdvertiser/src/app/user/myPlaylists/playlist-list/playlist-list.component.html
new file mode 100644
index 0000000..2185a77
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/playlist-list/playlist-list.component.html
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{playlist.name}}
+ {{playlist.videoIds.length}} vidéo
+ 1"> {{playlist.videoIds.length}} vidéos
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/playlist-list/playlist-list.component.scss b/userAndAdvertiser/src/app/user/myPlaylists/playlist-list/playlist-list.component.scss
new file mode 100644
index 0000000..0376ee3
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/playlist-list/playlist-list.component.scss
@@ -0,0 +1,94 @@
+.myContainer {
+ background-color: white ;
+ text-align: center;
+ width: 35vw;
+ margin: 1vh 0vh 3vh 0vh;
+ padding: 0px;
+ border: solid 2px black;
+ border-radius: 10px;
+ box-shadow: 10px 5px 5px black;
+}
+
+// SearchBar -----------------------------------------------------------
+
+.searchBarContainer {
+ text-align: center;
+ margin: 0px 0px 0px 0px;
+ padding: 10px 0px 10px 0px;
+ //background-color: #dcdcdc;
+ background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
+ background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
+ font-size: large;
+ border-bottom: solid 1px black;
+ border-top-left-radius: 10px;
+ border-top-right-radius: 10px;
+}
+
+.inputSearchBar {
+ width: 70%;
+ border-radius: 5px;
+}
+
+
+// Liste des playlists -------------------------------------------------
+
+.playlistListContainer {
+ max-width: 100%;
+ height: 60vh;
+ overflow-y: scroll;
+ padding: 0px;
+ overflow-x: hidden;
+}
+
+.playlistContainer {
+ max-width: 100%;
+ padding: 0px;
+ overflow-x: hidden;
+}
+
+
+.btnPlaylist {
+ background-color: white;
+ padding: 20px;
+ border-bottom: solid 1px black;
+ //width: 100%;
+ width: 35vw;
+ overflow-x: hidden;
+}
+.btnPlaylist:hover {
+ background-color: #f0f0f0;
+}
+
+.btnPlaylistFocus {
+ background-color: #e6e6e6;
+}
+.btnPlaylistFocus:hover {
+ background-color: #e6e6e6;
+}
+
+
+.playListCount {
+ color: gray;
+ font-style: italic;
+}
+
+// Bouton creer playlist -------------------------------------------------
+
+.btnCreerPlaylistContainer {
+ margin: 0px 0px 0px 0px;
+ background-color: #dcdcdc;
+ font-size: large;
+ border-top: solid 1px black;
+ border-bottom-left-radius: 10px;
+ border-bottom-right-radius: 10px;
+}
+
+.btnCreerPlaylist {
+ margin: 0px 0px 0px 0px;
+ border-bottom-left-radius: 10px;
+ border-bottom-right-radius: 10px;
+ background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
+ background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
+ //background: linear-gradient(180deg, #e6e6e6 0%, rgba(0,0,0,0.25) 49%, rgba(38,38,38,0.6) 51%, rgba(0,0,0,0.25) 100%);
+}
+
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/playlist-list/playlist-list.component.spec.ts b/userAndAdvertiser/src/app/user/myPlaylists/playlist-list/playlist-list.component.spec.ts
new file mode 100644
index 0000000..9308f2c
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/playlist-list/playlist-list.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PlaylistListComponent } from './playlist-list.component';
+
+describe('PlaylistListComponent', () => {
+ let component: PlaylistListComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PlaylistListComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PlaylistListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/playlist-list/playlist-list.component.ts b/userAndAdvertiser/src/app/user/myPlaylists/playlist-list/playlist-list.component.ts
new file mode 100644
index 0000000..9d9dbc2
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/playlist-list/playlist-list.component.ts
@@ -0,0 +1,161 @@
+import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {MatDialog} from "@angular/material/dialog";
+import {MatSnackBar} from "@angular/material/snack-bar";
+import {PopupCreateOrUpdatePlaylistComponent} from "../popup-create-or-update-playlist/popup-create-or-update-playlist.component";
+import {PopupDeletePlaylistComponent} from "../popup-delete-playlist/popup-delete-playlist.component";
+import {ThemeService} from "../../../utils/theme/theme.service";
+import {MessageService} from "../../../utils/message/message.service";
+
+
+
+@Component({
+ selector: 'app-playlist-list',
+ templateUrl: './playlist-list.component.html',
+ styleUrls: ['./playlist-list.component.scss']
+})
+export class PlaylistListComponent implements OnInit
+{
+ allPlaylists: any[] = []; // toutes les playlists
+ @Output() eventEmitter = new EventEmitter(); // pour envoyer au parent la playlist selectionner
+ search: string = "" ; // contenu de la barre de recherche
+ tabPlaylist: any[] = []; // playlist affichées
+ playlistFocusedOn: any;
+
+
+ constructor( public themeService: ThemeService,
+ public dialog: MatDialog,
+ public snackBar: MatSnackBar,
+ private messageService: MessageService ) { }
+
+
+ ngOnInit(): void
+ {
+ this.messageService
+ .get("playlist/findAll")
+ .subscribe( retour => this.ngOnInitCallback(retour), err => this.ngOnInitCallback(err) );
+ }
+
+
+ ngOnInitCallback(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ } else {
+ const aux = retour.data.filter( x => x.isActive === true);
+ this.allPlaylists = aux.map(x => {
+ x["_id"] = x.id ;
+ return x;
+ });
+ this.tabPlaylist = [].concat(this.allPlaylists);
+ }
+ }
+
+
+ // s'execute lorsqu'on écrit sur la barre de recherche
+ whileSearch()
+ {
+ this.tabPlaylist = [];
+ for(let playlist of this.allPlaylists)
+ {
+ if(playlist.name.includes(this.search)) this.tabPlaylist.push(playlist);
+ }
+ }
+
+
+ // click sur créer playlist
+ onCreatePlaylist(): void
+ {
+ const config = {
+ data: {
+ action: "create",
+ tabPlaylist: this.tabPlaylist,
+ }
+ };
+ this.dialog
+ .open(PopupCreateOrUpdatePlaylistComponent, config )
+ .afterClosed()
+ .subscribe(playlist => {
+
+ const config = { duration: 1500, panelClass: "custom-class" };
+ if((playlist === null) || (playlist === undefined)) {
+ this.snackBar.open("Opération annulée", "", config);
+ }
+ else {
+ playlist["_id"] = playlist.id;
+ this.allPlaylists.push(playlist);
+ this.tabPlaylist.push(playlist);
+ this.snackBar.open(`La playlist '${playlist.name}' a bien été créée ✔`, "", config);
+ }
+ });
+ }
+
+
+ // click sur update playlist
+ onUpdatePlaylist(playlistToUpdate): void
+ {
+ const config = {
+ data: {
+ action: "update",
+ tabPlaylist: this.tabPlaylist,
+ playlistName: playlistToUpdate.name,
+ playlistId: playlistToUpdate._id
+ }
+ };
+ this.dialog
+ .open(PopupCreateOrUpdatePlaylistComponent, config)
+ .afterClosed()
+ .subscribe(newName => {
+
+ const config = { duration: 1500, panelClass: "custom-class" };
+ if((newName === null) || (newName === undefined)) {
+ this.snackBar.open("Opération annulée", "", config);
+ }
+ else {
+ let index = this.allPlaylists.findIndex( elt => (elt._id === playlistToUpdate._id));
+ this.allPlaylists[index].name = newName;
+ index = this.tabPlaylist.findIndex( elt => (elt._id === playlistToUpdate._id));
+ this.tabPlaylist[index].name = newName;
+ this.snackBar.open(`La playlist '${playlistToUpdate.name}' a bien été mise à jour ✔`, "", config);
+ this.eventEmitter.emit(this.tabPlaylist[index]);
+ this.playlistFocusedOn = this.tabPlaylist[index]
+ }
+ });
+ }
+
+
+ // click sur supprimer playlist
+ onDeletePlaylist(playlist): void
+ {
+ const config = {data: playlist};
+ this.dialog
+ .open(PopupDeletePlaylistComponent, config)
+ .afterClosed()
+ .subscribe(retour => {
+
+ const config = { duration: 1500, panelClass: "custom-class" };
+ if((retour === null) || (retour === undefined)) {
+ this.snackBar.open("Opération annulée", "", config);
+ }
+ else {
+ let index = this.allPlaylists.indexOf(playlist);
+ if(index >= 0) this.allPlaylists.splice(index, 1);
+
+ index = this.tabPlaylist.indexOf(playlist);
+ if(index >= 0) this.tabPlaylist.splice(index, 1);
+
+ this.eventEmitter.emit(null);
+ this.playlistFocusedOn = null;
+ this.snackBar.open(`La playlist '${playlist.name}' a bien été suprimée ✔`, "", config);
+ }
+ });
+ }
+
+
+ // retourne la class CSS de conteneur de playlist
+ getClassOfPlaylistContainer(playlist): string
+ {
+ if(playlist === this.playlistFocusedOn) return "row btnPlaylist btnPlaylistFocus" ;
+ else return "row btnPlaylist" ;
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component.html b/userAndAdvertiser/src/app/user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component.html
new file mode 100644
index 0000000..d88fa34
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component.html
@@ -0,0 +1,19 @@
+
+
+
+
+ Nom de la playlist
+
+
+ {{errorMessage}}
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component.scss b/userAndAdvertiser/src/app/user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component.spec.ts b/userAndAdvertiser/src/app/user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component.spec.ts
new file mode 100644
index 0000000..640bdbc
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PopupCreateOrUpdatePlaylistComponent } from './popup-create-or-update-playlist.component';
+
+describe('PopupCreatePlaylistComponent', () => {
+ let component: PopupCreateOrUpdatePlaylistComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupCreateOrUpdatePlaylistComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupCreateOrUpdatePlaylistComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component.ts b/userAndAdvertiser/src/app/user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component.ts
new file mode 100644
index 0000000..1be479b
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component.ts
@@ -0,0 +1,90 @@
+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-or-update-playlist',
+ templateUrl: './popup-create-or-update-playlist.component.html',
+ styleUrls: ['./popup-create-or-update-playlist.component.scss']
+})
+export class PopupCreateOrUpdatePlaylistComponent implements OnInit
+{
+ name: string = "" ;
+ hasError: boolean = false;
+ tabNomPlaylist: string[] = [];
+ errorMessage: string = "" ;
+ action: string = "";
+
+
+ constructor( public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data,
+ private messageService: MessageService) { }
+
+
+ ngOnInit(): void
+ {
+ this.action = this.data.action;
+ this.tabNomPlaylist = this.data.tabPlaylist.map( playlist0 => playlist0.name );
+ if(this.action === "update") this.name = this.data.playlistName;
+ }
+
+
+ onValider(): void
+ {
+ this.checkError();
+ if(!this.hasError)
+ {
+ if(this.action === "create")
+ {
+ this.messageService
+ .post("playlist/create", {name: this.name})
+ .subscribe(retour => this.onValiderCallback(retour), err => this.onValiderCallback(err));
+ }
+ else if(this.action === "update")
+ {
+ this.messageService
+ .put("playlist/update/"+this.data.playlistId, {name: this.name})
+ .subscribe(retour => this.onValiderCallback(retour), err => this.onValiderCallback(err));
+ }
+ }
+ }
+
+
+ onValiderCallback(retour): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ this.dialogRef.close(null);
+ }
+ else {
+ if(this.action === "create") this.dialogRef.close(retour.data);
+ else if(this.action === "update") this.dialogRef.close(this.name);
+ }
+ }
+
+
+ checkError(): void
+ {
+ if(this.name === "") {
+ this.errorMessage = "Le nom ne peut pas être vide" ;
+ this.hasError = true;
+ }
+ else if(this.tabNomPlaylist.includes(this.name)){
+ this.errorMessage = "Ce nom est déjà utilisé" ;
+ this.hasError = true;
+ }
+ else {
+ this.hasError = false;
+ this.errorMessage = "" ;
+ }
+ }
+
+
+ onAnnuler(): void
+ {
+ this.dialogRef.close(null);
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component.html b/userAndAdvertiser/src/app/user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component.html
new file mode 100644
index 0000000..0335139
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component.html
@@ -0,0 +1,8 @@
+
+ Êtes-vous sûr de vouloir supprimer {{playlist.name}} ?
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component.scss b/userAndAdvertiser/src/app/user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component.spec.ts b/userAndAdvertiser/src/app/user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component.spec.ts
new file mode 100644
index 0000000..83d1cf7
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PopupDeletePlaylistComponent } from './popup-delete-playlist.component';
+
+describe('PopupDeletePlaylistComponent', () => {
+ let component: PopupDeletePlaylistComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupDeletePlaylistComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupDeletePlaylistComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component.ts b/userAndAdvertiser/src/app/user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component.ts
new file mode 100644
index 0000000..dda9f5b
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component.ts
@@ -0,0 +1,41 @@
+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-playlist',
+ templateUrl: './popup-delete-playlist.component.html',
+ styleUrls: ['./popup-delete-playlist.component.scss']
+})
+export class PopupDeletePlaylistComponent implements OnInit
+{
+ playlist;
+
+ constructor( public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data,
+ private messageService: MessageService ) { }
+
+ ngOnInit(): void
+ {
+ this.playlist = this.data;
+ }
+
+ onValidate(): void
+ {
+ this.messageService
+ .delete("playlist/delete/"+this.playlist._id)
+ .subscribe( retour => this.onValidateCallback(retour), err => this.onValidateCallback(err));
+ }
+
+ onValidateCallback(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ this.dialogRef.close(null);
+ }
+ else {
+ this.dialogRef.close(true);
+ }
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/video-list/video-list.component.html b/userAndAdvertiser/src/app/user/myPlaylists/video-list/video-list.component.html
new file mode 100644
index 0000000..1954fee
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/video-list/video-list.component.html
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{playlist.name}}
+
+
+
+
+ Aucune playlist selectionnée
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
![]()
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{video.title}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/video-list/video-list.component.scss b/userAndAdvertiser/src/app/user/myPlaylists/video-list/video-list.component.scss
new file mode 100644
index 0000000..e3af7ce
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/video-list/video-list.component.scss
@@ -0,0 +1,83 @@
+.myContainer {
+ //background-color: white ;
+ background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
+ background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
+ text-align: center;
+ width: 35vw;
+ margin: 1vh 0vh 3vh 0vh;
+ padding: 0px;
+ border: solid 2px black;
+ border-radius: 10px;
+ box-shadow: 10px 5px 5px black;
+}
+
+// TopBorder --------------------------------------------------------
+
+.topBorder {
+ margin: 0px 0px 0px 0px;
+ //background-color: #dcdcdc;
+ background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
+ background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
+ text-align: left;
+ padding: 5px 0px 5px 5px;
+ border-bottom: solid 1px black;
+ border-top-left-radius: 10px;
+ border-top-right-radius: 10px;
+}
+
+.spanPlayListTitle {
+ font-size: large;
+ font-weight: bold;
+}
+
+// Liste des videos ------------------------------------------------
+
+.listVideoContainer {
+ height: 65vh;
+ background-color: white;
+ padding: 0px;
+ overflow-y: scroll;
+}
+
+.videoContainer {
+ border-bottom: solid 1px black;
+ padding: 15px 0px 15px 0px;
+ width: 100%;
+}
+
+.imgsContainer {
+ position: relative;
+ width: 20vw;
+ height: 15vh;
+ cursor: pointer;
+}
+
+.imgPlay {
+ position: absolute;
+ margin-left: 9vw;
+ width: 3vw;
+ margin-top: 5vh;
+ height: 6vh;
+ padding: 0px 0px 0px 0px;
+}
+
+.imgVideo {
+ border: solid 1px black;
+ width: 20vw;
+ height: 15vh;
+ padding: 0px 0px 0px 0px;
+}
+
+// BottomBorder --------------------------------------------------------
+
+.bottomBorder {
+ margin: 0px 0px 0px 0px;
+ //background-color: #dcdcdc;
+ background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
+ background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
+ border-top: solid 1px black;
+ border-bottom: solid 1px black;
+ font-size: large;
+ border-bottom-left-radius: 10px;
+ border-bottom-right-radius: 10px;
+}
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/video-list/video-list.component.spec.ts b/userAndAdvertiser/src/app/user/myPlaylists/video-list/video-list.component.spec.ts
new file mode 100644
index 0000000..403cc76
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/video-list/video-list.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { VideoListComponent } from './video-list.component';
+
+describe('VideoListComponent', () => {
+ let component: VideoListComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ VideoListComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(VideoListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/myPlaylists/video-list/video-list.component.ts b/userAndAdvertiser/src/app/user/myPlaylists/video-list/video-list.component.ts
new file mode 100644
index 0000000..25c4edb
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myPlaylists/video-list/video-list.component.ts
@@ -0,0 +1,92 @@
+import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
+import {AddVideoToPlaylistsService} from "../../utils/services/addVideoToPlaylists/add-video-to-playlists.service";
+import {MatSnackBar} from "@angular/material/snack-bar";
+import {Router} from "@angular/router";
+import {MessageService} from "../../../utils/message/message.service";
+import {ThemeService} from "../../../utils/theme/theme.service";
+import {ProfilService} from "../../../utils/profil/profil.service";
+
+
+
+@Component({
+ selector: 'app-video-list',
+ templateUrl: './video-list.component.html',
+ styleUrls: ['./video-list.component.scss']
+})
+export class VideoListComponent implements OnChanges
+{
+ @Input() playlist: any;
+ videosInPlaylist: any[] = [];
+
+
+ constructor( private messageService: MessageService,
+ public themeService: ThemeService,
+ private addVideoToPlaylistsService: AddVideoToPlaylistsService,
+ private snackBar: MatSnackBar,
+ private profilService: ProfilService,
+ private router: Router ) { }
+
+
+ ngOnChanges(changes: SimpleChanges): void
+ {
+ if((this.playlist !== null) && (this.playlist !== undefined)) this.videosInPlaylist = this.playlist.videos;
+ }
+
+
+ onAddToPlaylist(video: any): void
+ {
+ this.addVideoToPlaylistsService.run(video.videoId, video.source, video.interest);
+ }
+
+
+ onDelete(video0: any, indexVideo: number): void
+ {
+ const data = {
+ videoId: {
+ id: video0._id,
+ action: "delete"
+ }
+ }
+ this.messageService
+ .put("playlist/update/"+this.playlist._id, data)
+ .subscribe( ret => this.onDeleteCallback(ret, indexVideo), err => this.onDeleteCallback(err, indexVideo));
+ }
+
+
+ onDeleteCallback(retour: any, indexVideo: number): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else {
+ this.playlist.videos.splice(indexVideo, 1);
+ this.videosInPlaylist.splice(indexVideo, 1);
+ let message = "La video a bien été supprimé de la playlist";
+ const config = { duration: 1000, panelClass: "custom-class" };
+ this.snackBar.open( message, "", config);
+ }
+ }
+
+
+ onVideo(video: any): void
+ {
+ this.messageService
+ .put("video/update/"+video._id, {watchedDate: true})
+ .subscribe(ret => this.onVideoCallback(ret), err => this.onVideoCallback(err));
+
+ const params = {
+ videoId: video.videoId,
+ source: video.source,
+ _idPlaylist: this.playlist._id,
+ from: "myPlaylists",
+ };
+ this.router.navigate(['/user/watching'], { queryParams: params });
+ }
+
+
+ onVideoCallback(retour: any): void
+ {
+ if(retour.status !== "success") console.log(retour);
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/myProfil/input-interests-profil/input-interests-profil.component.html b/userAndAdvertiser/src/app/user/myProfil/input-interests-profil/input-interests-profil.component.html
new file mode 100644
index 0000000..be2bd07
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myProfil/input-interests-profil/input-interests-profil.component.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+ {{interest}}
+
+
+
+
+
+
+
+
+
+
+
+ {{interest}}
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/myProfil/input-interests-profil/input-interests-profil.component.scss b/userAndAdvertiser/src/app/user/myProfil/input-interests-profil/input-interests-profil.component.scss
new file mode 100644
index 0000000..7628dd4
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myProfil/input-interests-profil/input-interests-profil.component.scss
@@ -0,0 +1,20 @@
+mat-form-field {
+ width: 100%;
+ font-size: small;
+}
+
+mat-chip-list {
+ font-size: small;
+}
+
+mat-chip {
+ font-size: small;
+}
+
+input {
+ font-size: small;
+}
+
+mat-option {
+ font-size: small;
+}
diff --git a/userAndAdvertiser/src/app/user/myProfil/input-interests-profil/input-interests-profil.component.spec.ts b/userAndAdvertiser/src/app/user/myProfil/input-interests-profil/input-interests-profil.component.spec.ts
new file mode 100644
index 0000000..0dd8314
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myProfil/input-interests-profil/input-interests-profil.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { InputInterestsProfilComponent } from './input-interests-profil.component';
+
+describe('InputInterestsComponent', () => {
+ let component: InputInterestsProfilComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ InputInterestsProfilComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(InputInterestsProfilComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/myProfil/input-interests-profil/input-interests-profil.component.ts b/userAndAdvertiser/src/app/user/myProfil/input-interests-profil/input-interests-profil.component.ts
new file mode 100644
index 0000000..873052a
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myProfil/input-interests-profil/input-interests-profil.component.ts
@@ -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-profil',
+ templateUrl: './input-interests-profil.component.html',
+ styleUrls: ['./input-interests-profil.component.scss']
+})
+export class InputInterestsProfilComponent implements OnInit
+{
+ selectable = true;
+ removable = true;
+ separatorKeysCodes: number[] = [ENTER, COMMA];
+ formControl = new FormControl();
+ filteredInterests: Observable;
+ @Input() myInterests: string[] = [];
+ allInterests: string[] = [];
+ @Output() eventEmitter = new EventEmitter();
+ @ViewChild('tagInput') tagInput: ElementRef;
+ 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));
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/myProfil/page-profil-user/page-profil-user.component.html b/userAndAdvertiser/src/app/user/myProfil/page-profil-user/page-profil-user.component.html
new file mode 100644
index 0000000..7e69ded
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myProfil/page-profil-user/page-profil-user.component.html
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Pseudo:
+
{{user.login}}
+
+
+
+
+
Mail:
+
{{user.email}}
+
+
+
+
+
Sexe:
+
+ Homme
+ Femme
+
+
+
+
+
+
Date de création:
+
{{ user.createdAt | date:'dd/LL/YYYY' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Date de naissance:
+
{{ user.dateOfBirth | date:'dd/LL/YYYY' }}
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/myProfil/page-profil-user/page-profil-user.component.scss b/userAndAdvertiser/src/app/user/myProfil/page-profil-user/page-profil-user.component.scss
new file mode 100644
index 0000000..ae34d41
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myProfil/page-profil-user/page-profil-user.component.scss
@@ -0,0 +1,80 @@
+.myContainer {
+ max-width: 100vw;
+ height: 100vh;
+ overflow-x: hidden;
+}
+
+
+.boite {
+ margin-left: auto;
+ margin-right: auto;
+ width: 70%;
+ 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;
+}
+
+// --------------------------------------------------------------------------------------------
+
+.interestsContainer {
+ width: 70%;
+ height: 15vh;
+ overflow-y: scroll;
+ border: 1px solid black;
+}
+
+.interest {
+ border-bottom: 1px solid #dcdcdc;
+ padding: 5px 5px 5px 5px;
+}
+
+// --------------------------------------------------------------------------------------------
+
+.btnContainer {
+ text-align: center;
+ margin-top: 40px;
+}
+.myBtn {
+ border: solid 1px black;
+ background-color: white;
+}
+
+
diff --git a/userAndAdvertiser/src/app/user/myProfil/page-profil-user/page-profil-user.component.spec.ts b/userAndAdvertiser/src/app/user/myProfil/page-profil-user/page-profil-user.component.spec.ts
new file mode 100644
index 0000000..e8722af
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myProfil/page-profil-user/page-profil-user.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageProfilUserComponent } from './page-profil-user.component';
+
+describe('PageProfilUserComponent', () => {
+ let component: PageProfilUserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageProfilUserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageProfilUserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/myProfil/page-profil-user/page-profil-user.component.ts b/userAndAdvertiser/src/app/user/myProfil/page-profil-user/page-profil-user.component.ts
new file mode 100644
index 0000000..be4615f
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myProfil/page-profil-user/page-profil-user.component.ts
@@ -0,0 +1,89 @@
+import { Component, OnInit } from '@angular/core';
+import {MatDialog} from "@angular/material/dialog";
+import {MatSnackBar} from "@angular/material/snack-bar";
+import {PopupUpdateUserComponent} from "../popup-update-user/popup-update-user.component";
+import {ThemeService} from "../../../utils/theme/theme.service";
+import {MessageService} from "../../../utils/message/message.service";
+import {ProfilService} from "../../../utils/profil/profil.service";
+
+
+
+@Component({
+ selector: 'app-page-profil-user',
+ templateUrl: './page-profil-user.component.html',
+ styleUrls: ['./page-profil-user.component.scss']
+})
+export class PageProfilUserComponent implements OnInit
+{
+ 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( 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.user = retour.data;
+ }
+ }
+
+
+ onModifier()
+ {
+ const config = {
+ width: '70%',
+ data: { user: this.user }
+ };
+ this.dialog
+ .open(PopupUpdateUserComponent, 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.user = retour;
+ }
+ });
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/myProfil/popup-update-user/popup-update-user.component.html b/userAndAdvertiser/src/app/user/myProfil/popup-update-user/popup-update-user.component.html
new file mode 100644
index 0000000..1e583c7
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myProfil/popup-update-user/popup-update-user.component.html
@@ -0,0 +1,125 @@
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+
+
+
+
{{errorMessage}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Homme
+ Femme
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/myProfil/popup-update-user/popup-update-user.component.scss b/userAndAdvertiser/src/app/user/myProfil/popup-update-user/popup-update-user.component.scss
new file mode 100644
index 0000000..636928e
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myProfil/popup-update-user/popup-update-user.component.scss
@@ -0,0 +1,81 @@
+.myContainer {
+ 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;
+}
+
+.inputUrlImage {
+ width: 80%;
+ text-align: center;
+ margin-left: 10%;
+ margin-right: 10%;
+ font-size: small;
+ border-radius: 5px;
+}
+
+input {
+ font-size: small;
+}
+
+// -------------------------------------------------------------------------
+
+.myRow {
+ margin: 15px 0px 15px 0px;
+}
+.myLeftLabel {
+ text-align: right;
+ padding: 0px 0px 0px 0px;
+ margin: 0px 0px 0px 0px;
+ font-weight: bold;
+}
+.myRightLabel {
+ text-align: left;
+ padding: 0px 0px 0px 0px;
+ margin: 0px 0px 0px 0px;
+ font-weight: bold;
+}
+.myValue {
+ text-align: left;
+ padding: 0px 0px 0px 0px;
+ margin: 0px 0px 0px 0px;
+}
+
+// -------------------------------------------------------------------------
+
+// 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;
+}
+
+// -------------------------------------------------------------------------
+
+::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;
+}
diff --git a/userAndAdvertiser/src/app/user/myProfil/popup-update-user/popup-update-user.component.spec.ts b/userAndAdvertiser/src/app/user/myProfil/popup-update-user/popup-update-user.component.spec.ts
new file mode 100644
index 0000000..a5126ad
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myProfil/popup-update-user/popup-update-user.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PopupUpdateUserComponent } from './popup-update-user.component';
+
+describe('PopupUpdateUserComponent', () => {
+ let component: PopupUpdateUserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupUpdateUserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupUpdateUserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/myProfil/popup-update-user/popup-update-user.component.ts b/userAndAdvertiser/src/app/user/myProfil/popup-update-user/popup-update-user.component.ts
new file mode 100644
index 0000000..4c91d19
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/myProfil/popup-update-user/popup-update-user.component.ts
@@ -0,0 +1,132 @@
+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-user',
+ templateUrl: './popup-update-user.component.html',
+ styleUrls: ['./popup-update-user.component.scss']
+})
+export class PopupUpdateUserComponent implements OnInit
+{
+ userCopy;
+ newPassword: string = "";
+ confirmNewPassword: string = "" ;
+ changePassword: boolean = false ;
+ hasError: boolean = false;
+ errorMessage: string = "" ;
+
+
+ constructor( public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data,
+ private messageService: MessageService,
+ private profilService: ProfilService ) { }
+
+
+ ngOnInit(): void
+ {
+ const user0 = this.data.user;
+ this.userCopy = {
+ _id: user0._id,
+ login: user0.login,
+ hashPass: user0.hashPass,
+ email: user0.email,
+ role: {
+ name: user0.role.name,
+ permission: user0.role.permission,
+ isAccepted: user0.role.isAccepted,
+ },
+ profileImageUrl: user0.profileImageUrl,
+ dateOfBirth: user0.dateOfBirth,
+ gender: user0.gender,
+ interests: [],
+ company: "",
+ isActive: user0.isActive,
+ createdAt: user0.createdAt,
+ updatedAt: user0.updatedAt,
+ lastConnexion: new Date()
+ };
+ for(let interest of user0.interests) this.userCopy.interests.push(interest);
+ }
+
+
+ onValider()
+ {
+ this.checkField();
+ if(!this.hasError)
+ {
+ if(this.changePassword) this.userCopy.hashPass = this.newPassword;
+ const data = {
+ login: this.userCopy.login,
+ hashPass: this.userCopy.hashPass,
+ email: this.userCopy.email,
+ profileImageUrl: this.userCopy.profileImageUrl,
+ dateOfBirth: this.userCopy.dateOfBirth,
+ gender: this.userCopy.gender,
+ interests: this.userCopy.interests,
+ };
+ 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.userCopy.profileImageUrl);
+ this.dialogRef.close(this.userCopy);
+ }
+ }
+
+
+ checkField()
+ {
+ if(this.userCopy.login.length === 0) {
+ this.errorMessage = "Veuillez remplir le champ 'pseudo'." ;
+ this.hasError = true;
+ }
+ else if(this.userCopy.email.length === 0) {
+ this.errorMessage = "Veuillez remplir le champ 'email'." ;
+ this.hasError = true;
+ }
+ else if(!this.isValidEmail(this.userCopy.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);
+ }
+
+
+ onEventInputInterests(myInterets: string[])
+ {
+ this.userCopy.interests = myInterets;
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/search/page-search/page-search.component.html b/userAndAdvertiser/src/app/user/search/page-search/page-search.component.html
new file mode 100644
index 0000000..0039bdd
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/search/page-search/page-search.component.html
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/search/page-search/page-search.component.scss b/userAndAdvertiser/src/app/user/search/page-search/page-search.component.scss
new file mode 100644
index 0000000..f80fc45
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/search/page-search/page-search.component.scss
@@ -0,0 +1,90 @@
+.lightTheme {
+ color: black;
+ border-color: black;
+}
+.darkTheme {
+ color: white;
+ border-color: white;
+}
+.myContainer {
+ text-align: center;
+ max-width: 100vw;
+ height: 100vh;
+ overflow-x: hidden;
+ overflow-y: scroll;
+}
+
+//--------------------------------------------------------------------------------------------
+
+.inputSearchBar {
+ width: 40%;
+ font-size: large;
+ border-bottom: 10px;
+ border-radius: 5px;
+}
+
+//--------------------------------------------------------------------------------------------
+
+.celluleGrilleVideo {
+ border: solid 2px;
+ border-radius: 5px;
+ width: 100%;
+}
+.lightTheme .celluleGrilleVideo{
+ border-color: black;
+ background-color: #f0f0f0;
+}
+.darkTheme .celluleGrilleVideo{
+ border-color: white;
+ background-color: #646464;
+}
+
+.conteneurVideosGrid {
+ height: 75vh;
+ width: 100%;
+}
+
+//--------------------------------------------------------------------------------------------
+
+.cellulePub {
+ padding: 0px 10px 0px 10px;
+ width: 100%;
+ text-align: center;
+ justify-content: center;
+}
+
+.conteneurPub {
+ height: 75vh;
+ text-align: center;
+ justify-content: center;
+ vertical-align: middle;
+ display: block;
+ width: 75%;
+ margin-left: auto;
+ margin-right: auto;
+ position: absolute;
+ top: 50%;
+ -ms-transform: translateY(-50%);
+ transform: translateY(-50%);
+}
+
+
+
+// -------------------------------------------------------------------------
+
+
+// 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;
+}
diff --git a/userAndAdvertiser/src/app/user/search/page-search/page-search.component.spec.ts b/userAndAdvertiser/src/app/user/search/page-search/page-search.component.spec.ts
new file mode 100644
index 0000000..79e1a03
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/search/page-search/page-search.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageSearchComponent } from './page-search.component';
+
+describe('PageSearchComponent', () => {
+ let component: PageSearchComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageSearchComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageSearchComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/search/page-search/page-search.component.ts b/userAndAdvertiser/src/app/user/search/page-search/page-search.component.ts
new file mode 100644
index 0000000..60b9826
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/search/page-search/page-search.component.ts
@@ -0,0 +1,118 @@
+import { Component, OnInit } from '@angular/core';
+import {HttpParams} from "@angular/common/http";
+import {ActivatedRoute} from "@angular/router";
+import {MessageService} from "../../../utils/message/message.service";
+import {ThemeService} from "../../../utils/theme/theme.service";
+
+
+
+let TAB_PLATEFORM = [
+ { name: "Youtube", isSelected: true },
+ { name: "Dailymotion", isSelected: true }
+];
+
+
+
+@Component({
+ selector: 'app-page-search',
+ templateUrl: './page-search.component.html',
+ styleUrls: ['./page-search.component.scss']
+})
+export class PageSearchComponent implements OnInit
+{
+ tabPlateform = TAB_PLATEFORM;
+ tabVideo: any[] = [];
+ search: string = "";
+ ad1: any;
+ ad2: any;
+ sources: string = "" ;
+ indexPage: number = 0;
+
+
+ constructor( private messageService: MessageService,
+ public themeService: ThemeService,
+ private activatedRoute: ActivatedRoute ) { }
+
+
+ ngOnInit(): void
+ {
+ // parametre de la route
+ this.activatedRoute
+ .queryParams
+ .subscribe(paramsFromOldPage => {
+ if(paramsFromOldPage.hasOwnProperty("search")) this.search = paramsFromOldPage.search;
+ if(paramsFromOldPage.hasOwnProperty("sources"))
+ {
+ this.sources = paramsFromOldPage.sources;
+ if(this.sources === "yt") {
+ this.tabPlateform[0].isSelected = true;
+ this.tabPlateform[1].isSelected = false;
+ }
+ else if(this.sources === "dm") {
+ this.tabPlateform[0].isSelected = false;
+ this.tabPlateform[1].isSelected = true;
+ }
+ else if(this.sources === "yt,dm") {
+ this.tabPlateform[0].isSelected = true;
+ this.tabPlateform[1].isSelected = true;
+ }
+ }
+ if(paramsFromOldPage.hasOwnProperty("indexPage")) this.indexPage = parseInt(paramsFromOldPage.indexPage, 10);
+ this.onSearch();
+ });
+
+ // Ask for ads
+ let params = new HttpParams();
+ params = params.append("quantity", 2);
+ this.messageService
+ .get("user/ad", params)
+ .subscribe(ret => this.adCallback(ret), err => this.adCallback(err));
+ }
+
+
+ adCallback(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else {
+ this.ad1 = retour.data[0];
+ this.ad2 = retour.data[1];
+ }
+ }
+
+
+ onSearch()
+ {
+ let params = new HttpParams();
+ params = params.append('q', this.search);
+
+ if(this.tabPlateform[0].isSelected && this.tabPlateform[1].isSelected) this.sources = "yt,dm" ;
+ else if((!this.tabPlateform[0].isSelected) && this.tabPlateform[1].isSelected) this.sources = "dm" ;
+ else if(this.tabPlateform[0].isSelected && (!this.tabPlateform[1].isSelected)) this.sources = "yt" ;
+ else this.sources = "" ;
+ params = params.append('sources', this.sources);
+
+ this.messageService
+ .get("video/search", params)
+ .subscribe(ret => this.onSearchCallback(ret), err => this.onSearchCallback(err));
+ }
+
+
+ onSearchCallback(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else {
+ this.tabVideo = retour.data;
+ }
+ }
+
+
+ onEnterOnSearchBar(event)
+ {
+ if(event.key === 'Enter') this.onSearch();
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/search/video-grid/video-grid.component.html b/userAndAdvertiser/src/app/user/search/video-grid/video-grid.component.html
new file mode 100644
index 0000000..841d5c3
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/search/video-grid/video-grid.component.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+

+
![]()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{tronquage(tabVideo[indexPage+k].title)}}
+
+
+ {{tabVideo[indexPage+k].views | number: '1.0-0'}} vues.
+ Il y a {{dateToElapsedTime(tabVideo[indexPage+k].publishedAt)}}.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{page}}
+
+
+ {{page}}
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/search/video-grid/video-grid.component.scss b/userAndAdvertiser/src/app/user/search/video-grid/video-grid.component.scss
new file mode 100644
index 0000000..6819fd8
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/search/video-grid/video-grid.component.scss
@@ -0,0 +1,84 @@
+mat-grid-list {
+ margin: 0px 0px 0px 0px;
+ padding: 0px 0px 0px 0px;
+ border: none;
+}
+
+mat-grid-tile {
+ margin: 0px 0px 0px 0px;
+ padding: 0px 0px 0px 0px;
+}
+
+.myCell {
+ margin: 7px 0px 0px 0px;
+ padding: 0px 0px 0px 0px;
+ background-color: white;
+ border: solid 1px black;
+ border-bottom-left-radius: 2px;
+ border-bottom-right-radius: 2px;
+ cursor: pointer;
+}
+.myCell:hover {
+ background-color: #d2d2d2;
+}
+
+
+// ---------------------------------------------------------------------------------------------
+
+
+.imgsContainer {
+ //width: 20vw;
+ width: 18vw;
+ height: 15vh;
+}
+
+.imgPlay {
+ position: absolute;
+ margin-left: 8vw;
+ width: 2.5vw;
+ margin-top: 5vh;
+ height: 5vh;
+ padding: 0px 0px 0px 0px;
+}
+
+.imgVideo {
+ width: 18vw;
+ height: 15vh;
+ padding: 0px 0px 0px 0px;
+}
+
+
+// ---------------------------------------------------------------------------------------------
+
+
+.mat-grid-list-info-video {
+ margin: 0px 0px 0px 0px;
+ font-size: small;
+}
+
+.mat-grid-tile-info-video {
+ border: none;
+ font-size: x-small;
+ //background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
+ //background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
+}
+
+mat-icon {
+ text-align: right;
+}
+
+
+// ---------------------------------------------------------------------------------------------
+
+
+.paginatorContainer {
+ margin: 0px 0px 0px 0px;
+ padding: 5px 0px 0px 0px;
+ text-align: center;
+ background-color: white;
+}
+
+.btnPaginator {
+ margin: 0px 0px 0px 0px;
+ padding: 0px 0px 0px 0px;
+}
diff --git a/userAndAdvertiser/src/app/user/search/video-grid/video-grid.component.spec.ts b/userAndAdvertiser/src/app/user/search/video-grid/video-grid.component.spec.ts
new file mode 100644
index 0000000..17cea62
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/search/video-grid/video-grid.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { VideoGridComponent } from './video-grid.component';
+
+describe('VideoGridComponent', () => {
+ let component: VideoGridComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ VideoGridComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(VideoGridComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/search/video-grid/video-grid.component.ts b/userAndAdvertiser/src/app/user/search/video-grid/video-grid.component.ts
new file mode 100644
index 0000000..11dea65
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/search/video-grid/video-grid.component.ts
@@ -0,0 +1,128 @@
+import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
+import {AddVideoToPlaylistsService} from "../../utils/services/addVideoToPlaylists/add-video-to-playlists.service";
+import {Router} from "@angular/router";
+import {MessageService} from "../../../utils/message/message.service";
+
+
+
+@Component({
+ selector: 'app-video-grid',
+ templateUrl: './video-grid.component.html',
+ styleUrls: ['./video-grid.component.scss']
+})
+export class VideoGridComponent implements OnChanges
+{
+ @Input() tabVideo: any[] = [];
+ @Input() search: string = "";
+ @Input() sources: string = "";
+ @Input() indexPage: number = 0;
+ tabPage: number[] = [];
+
+
+ constructor( private addVideoToPlaylistsService: AddVideoToPlaylistsService,
+ private router: Router,
+ private messageService: MessageService ) {}
+
+
+ ngOnChanges(changes: SimpleChanges): void
+ {
+ if(this.tabVideoExists())
+ {
+ const nbVideo = this.tabVideo.length;
+ let nbPage = Math.floor(nbVideo/9);
+ if((nbVideo%9) !== 0) nbPage += 1;
+ this.tabPage = [];
+ for(let i=1 ; i<=nbPage ; i++) this.tabPage.push(i);
+ }
+ }
+
+
+ onAddToPlaylist(video): void
+ {
+ this.addVideoToPlaylistsService.run(video.videoId, video.source, video.interest);
+ }
+
+
+ tronquage(str: string)
+ {
+ if(str.length < 30) return str;
+ else return str.substring(0, 27) + "..." ;
+ }
+
+
+ onVideo(video): void
+ {
+ const data = { source: video.source, interest: video.interest };
+ this.messageService
+ .post("video/create/"+video.videoId, data)
+ .subscribe(ret => this.onVideoCallback(ret,video), err => this.onVideoCallback(err,video));
+ }
+
+
+ onVideoCallback(retour: any, video): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else {
+ const params = {
+ videoId: video.videoId,
+ source: video.source,
+ from: "search",
+ search: this.search,
+ sources: this.sources,
+ indexPage: this.indexPage
+ };
+ this.router.navigate(['/user/watching'], { queryParams: params });
+ }
+ }
+
+
+ dateToElapsedTime(date0): string
+ {
+ const ellapsedTimeInMilliSeconds = (new Date()).getTime() - (new Date(date0)).getTime();
+
+ // seconde
+ const ellapsedTimeInSeconds = Math.trunc(ellapsedTimeInMilliSeconds / 1000);
+ if(ellapsedTimeInSeconds < 60) {
+ if(ellapsedTimeInSeconds <= 1)return ellapsedTimeInSeconds + " seconde" ;
+ else return ellapsedTimeInSeconds + " secondes" ;
+ }
+ // minute
+ const ellapsedTimeInMinutes = Math.trunc(ellapsedTimeInSeconds / 60);
+ if(ellapsedTimeInMinutes < 60) {
+ if(ellapsedTimeInMinutes <= 1) return ellapsedTimeInMinutes + " minute" ;
+ else return ellapsedTimeInMinutes + " minutes" ;
+ }
+ // heure
+ const ellapsedTimeInHours = Math.trunc(ellapsedTimeInMinutes / 60);
+ if(ellapsedTimeInHours < 24) {
+ if(ellapsedTimeInHours <= 1) return ellapsedTimeInHours + " heure" ;
+ else return ellapsedTimeInHours + " heures" ;
+ }
+ // jour
+ const ellapsedTimeInDays = Math.trunc(ellapsedTimeInHours / 24);
+ if(ellapsedTimeInDays < 31) {
+ if(ellapsedTimeInDays <= 1) return ellapsedTimeInDays + " jour" ;
+ else return ellapsedTimeInDays + " jours" ;
+ }
+ // mois
+ const ellapsedTimeInMonths = Math.trunc(ellapsedTimeInDays / 31);
+ if(ellapsedTimeInMonths < 12) {
+ return ellapsedTimeInMonths + " mois" ;
+ }
+ // an
+ const ellapsedTimeInYears = Math.trunc(ellapsedTimeInMonths / 12);
+ if(ellapsedTimeInYears <= 1) return ellapsedTimeInYears + " an" ;
+ else return ellapsedTimeInYears + " ans" ;
+ }
+
+
+ tabVideoExists(): boolean
+ {
+ if((this.tabVideo === null) || (this.tabVideo === undefined)) return false;
+ else if(this.tabVideo.length === 0) return false;
+ else return true;
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/utils/components/advert/advert.component.html b/userAndAdvertiser/src/app/user/utils/components/advert/advert.component.html
new file mode 100644
index 0000000..b1c034a
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/components/advert/advert.component.html
@@ -0,0 +1,26 @@
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/utils/components/advert/advert.component.scss b/userAndAdvertiser/src/app/user/utils/components/advert/advert.component.scss
new file mode 100644
index 0000000..ab03155
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/components/advert/advert.component.scss
@@ -0,0 +1,41 @@
+.myContainer {
+ margin: 0;
+ position: absolute;
+ top: 50%;
+ -ms-transform: translateY(-50%);
+ transform: translateY(-50%);
+}
+
+#imgFromSearchOrMyPlaylists {
+ max-width: 100%;
+ max-height: 100%;
+ border: solid 3px black;
+ vertical-align: middle;
+ cursor: pointer;
+}
+
+.helper {
+ display: inline-block;
+ height: 100%;
+ vertical-align: middle;
+}
+
+// ------------------------------------------------------------------------------
+
+#imgFromWatchingLeft {
+ width: 14vw;
+ height: 70vh;
+ border: solid 3px black;
+ position: fixed;
+ left: 1vw;
+ cursor: pointer;
+}
+
+#imgFromWatchingRight {
+ width: 15vw;
+ height: 70vh;
+ border: solid 3px black;
+ position: fixed;
+ right: 1vw;
+ cursor: pointer;
+}
diff --git a/userAndAdvertiser/src/app/user/utils/components/advert/advert.component.spec.ts b/userAndAdvertiser/src/app/user/utils/components/advert/advert.component.spec.ts
new file mode 100644
index 0000000..08b7e86
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/components/advert/advert.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AdvertComponent } from './advert.component';
+
+describe('PubComponent', () => {
+ let component: AdvertComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ AdvertComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AdvertComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/utils/components/advert/advert.component.ts b/userAndAdvertiser/src/app/user/utils/components/advert/advert.component.ts
new file mode 100644
index 0000000..00b8b44
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/components/advert/advert.component.ts
@@ -0,0 +1,40 @@
+import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
+
+
+
+@Component({
+ selector: 'app-advert',
+ templateUrl: './advert.component.html',
+ styleUrls: ['./advert.component.scss']
+})
+export class AdvertComponent implements OnChanges
+{
+ @Input() ad: any;
+ @Input() from: string = "search";
+ image: any;
+ imageExist: boolean = false;
+
+
+ constructor() { }
+
+
+ ngOnChanges(changes: SimpleChanges): void
+ {
+ if((this.ad !== null) && (this.ad !== undefined))
+ {
+ const nbImages = this.ad.images.length;
+ const indexImage = Math.floor(Math.random() * nbImages);
+ this.image = this.ad.images[indexImage];
+ this.imageExist = true;
+ }
+ }
+
+
+ onClick(): void
+ {
+ if((this.ad.url !== "") && (this.ad.url !== null) && (this.ad.url !== undefined)) {
+ document.location.href = this.ad.url;
+ }
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/utils/components/navbar-user/navbar-user.component.html b/userAndAdvertiser/src/app/user/utils/components/navbar-user/navbar-user.component.html
new file mode 100644
index 0000000..605e192
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/components/navbar-user/navbar-user.component.html
@@ -0,0 +1,41 @@
+
diff --git a/userAndAdvertiser/src/app/user/utils/components/navbar-user/navbar-user.component.scss b/userAndAdvertiser/src/app/user/utils/components/navbar-user/navbar-user.component.scss
new file mode 100644
index 0000000..285d629
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/components/navbar-user/navbar-user.component.scss
@@ -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;
+}
diff --git a/userAndAdvertiser/src/app/user/utils/components/navbar-user/navbar-user.component.spec.ts b/userAndAdvertiser/src/app/user/utils/components/navbar-user/navbar-user.component.spec.ts
new file mode 100644
index 0000000..5d03960
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/components/navbar-user/navbar-user.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { NavbarUserComponent } from './navbar-user.component';
+
+describe('NavbarUserComponent', () => {
+ let component: NavbarUserComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ NavbarUserComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(NavbarUserComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/utils/components/navbar-user/navbar-user.component.ts b/userAndAdvertiser/src/app/user/utils/components/navbar-user/navbar-user.component.ts
new file mode 100644
index 0000000..ec8576e
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/components/navbar-user/navbar-user.component.ts
@@ -0,0 +1,41 @@
+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-user',
+ templateUrl: './navbar-user.component.html',
+ styleUrls: ['./navbar-user.component.scss']
+})
+export class NavbarUserComponent
+{
+ routes: string[] = [
+ "/user", // 0
+ "/user/search", // 1
+ "/user/myPlaylists", // 2
+ "/user/history", // 3
+ "/user/myProfil" // 4
+ ];
+
+ 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);
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component.html b/userAndAdvertiser/src/app/user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component.html
new file mode 100644
index 0000000..115b281
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component.html
@@ -0,0 +1,42 @@
+ Ajouter dans
+
+
+
+
+
+
+
+ {{playlist.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Nom playlist
+
+
+
+
+
+
+
+
+
+ {{errorMessage}}
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component.scss b/userAndAdvertiser/src/app/user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component.scss
new file mode 100644
index 0000000..a6f9d32
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component.scss
@@ -0,0 +1,39 @@
+h3 {
+ text-align: center;
+ margin-bottom: 10px
+}
+
+.conteneurPlaylists {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+.conteneurBtnCreerPlaylist {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+.conteneurInputNewPlaylist {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+
+// -------------------------------------------------------------------------
+
+
+// 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-color: black !important;
+ background-color: white !important;
+}
diff --git a/userAndAdvertiser/src/app/user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component.spec.ts b/userAndAdvertiser/src/app/user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component.spec.ts
new file mode 100644
index 0000000..5ace846
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PopupAddVideoToPlaylistsComponent } from './popup-add-video-to-playlists.component';
+
+describe('PopupAddVideoToPlaylistsComponent', () => {
+ let component: PopupAddVideoToPlaylistsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PopupAddVideoToPlaylistsComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopupAddVideoToPlaylistsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component.ts b/userAndAdvertiser/src/app/user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component.ts
new file mode 100644
index 0000000..cfa0119
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component.ts
@@ -0,0 +1,141 @@
+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-add-video-to-playlists',
+ templateUrl: './popup-add-video-to-playlists.component.html',
+ styleUrls: ['./popup-add-video-to-playlists.component.scss']
+})
+export class PopupAddVideoToPlaylistsComponent implements OnInit
+{
+ _idVideo: string = "";
+ videoId: string = "";
+ source: string = "";
+ interest: string = "";
+
+ tabPlaylistAndBool = [];
+
+ goToCreatePlaylist = false;
+ newPlaylistName = "";
+ hasError: boolean = false;
+ tabNomPlaylist: string[] = [];
+ errorMessage: string = "" ;
+
+
+ constructor( public dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data,
+ private messageService: MessageService) { }
+
+
+ ngOnInit(): void
+ {
+ this._idVideo = this.data._idVideo;
+ this.videoId = this.data.videoId;
+ this.source = this.data.source;
+ this.interest = this.data.interest;
+
+ for(let playlist of this.data.playlists)
+ {
+ if(playlist.videoIds.includes(this._idVideo)) playlist["isSelected"] = true;
+ else playlist["isSelected"] = false;
+ this.tabPlaylistAndBool.push(playlist);
+ this.tabNomPlaylist.push(playlist.name);
+ }
+ }
+
+
+ onValider(): void
+ {
+ this.checkError();
+ if(!this.hasError)
+ {
+ // --- Existing playlists ---
+ let listeDesPlaylistsSelected = "" ;
+ let listeDesPlaylistsNotSelected = "" ;
+ for(let playlist of this.tabPlaylistAndBool)
+ {
+ if(playlist.isSelected) listeDesPlaylistsSelected += playlist.id + "," ;
+ else listeDesPlaylistsNotSelected += playlist.id + "," ;
+ }
+ if(listeDesPlaylistsSelected.endsWith(",")) listeDesPlaylistsSelected = listeDesPlaylistsSelected.slice(0, listeDesPlaylistsSelected.length-1);
+ if(listeDesPlaylistsNotSelected.endsWith(",")) listeDesPlaylistsNotSelected = listeDesPlaylistsNotSelected.slice(0, listeDesPlaylistsNotSelected.length-1);
+
+ if(listeDesPlaylistsSelected !== "")
+ {
+ const data1 = { videoId: { id: this._idVideo, action: "add" } };
+ this.messageService
+ .put( "playlist/update/"+listeDesPlaylistsSelected, data1)
+ .subscribe( ret => this.callbackForExistingPlaylists(ret), err => this.callbackForExistingPlaylists(err));
+ }
+ if(listeDesPlaylistsNotSelected !== "")
+ {
+ const data2 = { videoId: { id: this._idVideo, action: "delete" } };
+ this.messageService
+ .put( "playlist/update/"+listeDesPlaylistsNotSelected, data2)
+ .subscribe( ret => this.callbackForExistingPlaylists(ret), err => this.callbackForExistingPlaylists(err));
+ }
+
+
+ // --- New playlists ---
+ if(this.goToCreatePlaylist)
+ {
+ const data3 = {
+ name: this.newPlaylistName,
+ video: {videoId: this.videoId, interest: this.interest, source: this.source}
+ };
+ this.messageService
+ .post("playlist/create", data3)
+ .subscribe( ret => this.callbackForNewPlaylist(ret), err => this.callbackForNewPlaylist(err));
+ }
+
+
+ // --- Finalement ---
+ this.dialogRef.close("success");
+ }
+ }
+
+
+ callbackForExistingPlaylists(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ this.dialogRef.close(null);
+ }
+ }
+
+
+ callbackForNewPlaylist(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ this.dialogRef.close(null);
+ }
+ }
+
+
+ onAnnuler(): void
+ {
+ this.dialogRef.close("annulation");
+ }
+
+
+ checkError(): void
+ {
+ if(this.goToCreatePlaylist && (this.newPlaylistName === "")) {
+ this.errorMessage = "Le nom ne peut pas être vide" ;
+ this.hasError = true;
+ }
+ else if(this.goToCreatePlaylist && this.tabNomPlaylist.includes(this.newPlaylistName)){
+ this.errorMessage = "Ce nom est déjà utilisé" ;
+ this.hasError = true;
+ }
+ else {
+ this.hasError = false;
+ this.errorMessage = "" ;
+ }
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/utils/services/addVideoToPlaylists/add-video-to-playlists.service.spec.ts b/userAndAdvertiser/src/app/user/utils/services/addVideoToPlaylists/add-video-to-playlists.service.spec.ts
new file mode 100644
index 0000000..6097218
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/services/addVideoToPlaylists/add-video-to-playlists.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { AddVideoToPlaylistsService } from './add-video-to-playlists.service';
+
+describe('PlaylistService', () => {
+ let service: AddVideoToPlaylistsService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(AddVideoToPlaylistsService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/utils/services/addVideoToPlaylists/add-video-to-playlists.service.ts b/userAndAdvertiser/src/app/user/utils/services/addVideoToPlaylists/add-video-to-playlists.service.ts
new file mode 100644
index 0000000..e9df3c4
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/utils/services/addVideoToPlaylists/add-video-to-playlists.service.ts
@@ -0,0 +1,100 @@
+import { Injectable } from '@angular/core';
+import {MatDialog} from "@angular/material/dialog";
+import {PopupAddVideoToPlaylistsComponent} from "../../components/popup-add-video-to-playlists/popup-add-video-to-playlists.component";
+import {MatSnackBar} from "@angular/material/snack-bar";
+import {MessageService} from "../../../../utils/message/message.service";
+
+
+
+@Injectable({
+ providedIn: 'root'
+})
+export class AddVideoToPlaylistsService
+{
+ private _idVideo: string = "" ;
+ private videoId: string = "" ;
+ private source: string = "" ;
+ private interest: string = "" ;
+
+
+ constructor( private messageService: MessageService,
+ public dialog: MatDialog,
+ private snackBar: MatSnackBar ) { }
+
+
+ run(videoId: string, source: string, interest: string): void
+ {
+ this.videoId = videoId;
+ this.source = source;
+ this.interest = interest;
+
+ const data = { source: this.source, interest: this.interest };
+ this.messageService
+ .post("video/create/"+this.videoId, data)
+ .subscribe(ret => this.afterCreatingVideo(ret), err => this.afterCreatingVideo(err));
+ }
+
+
+ private afterCreatingVideo(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else {
+ this._idVideo = retour.data.id;
+ this.messageService
+ .get('playlist/findAll')
+ .subscribe( ret => this.afterReceivingPlaylists(ret), ret => this.afterReceivingPlaylists(ret) );
+ }
+ }
+
+
+
+ private afterReceivingPlaylists(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log(retour);
+ }
+ else
+ {
+ const config = {
+ width: '30%',
+ data: {
+ _idVideo: this._idVideo,
+ videoId: this.videoId,
+ source: this.source,
+ interest: this.interest,
+ playlists: retour.data.filter(x => x.isActive === true)
+ }
+ };
+ this.dialog
+ .open(PopupAddVideoToPlaylistsComponent, config)
+ .afterClosed()
+ .subscribe(retour => this.afterClosingDialog(retour));
+ }
+ }
+
+
+
+ private afterClosingDialog(retour): void
+ {
+ let message = "" ;
+ switch (retour)
+ {
+ case "error":
+ message = "Echec de l'opération ❌" ;
+ break;
+ case "success":
+ message = "La vidéo a bien été ajoutée ✔" ;
+ break;
+ case "annulation":
+ case null:
+ case undefined:
+ message = "Opération annulée" ;
+ break;
+ }
+ const config = { duration: 1000, panelClass: "custom-class" };
+ this.snackBar.open( message, "", config);
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/user/watching/page-watching-video/page-watching-video.component.html b/userAndAdvertiser/src/app/user/watching/page-watching-video/page-watching-video.component.html
new file mode 100644
index 0000000..9cf8392
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/watching/page-watching-video/page-watching-video.component.html
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+

+
{{video.title}}
+
+
+
+
+
+
+
+
+
+ Vues:
+ {{video.views | number: '1.0-0'}}
+
+
+
+
+ Date de publication:
+ {{ video.publishedAt | date:'dd/LL/YYYY à HH:mm:ss' }}
+
+
+
+
+
+ Description
+ expand_more
+ expand_less
+
+
+ {{video.description}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{playlist.name}}
+
+
+
+
+
+
+
+
+

+
![]()
+
+
+
{{video0.title}}
+
+
+ {{video.views | number: '1.0-0'}} vues
+
+
+ Publiée le {{ video.publishedAt | date:'dd/LL/YYYY à HH:mm:ss' }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/app/user/watching/page-watching-video/page-watching-video.component.scss b/userAndAdvertiser/src/app/user/watching/page-watching-video/page-watching-video.component.scss
new file mode 100644
index 0000000..d3f10e8
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/watching/page-watching-video/page-watching-video.component.scss
@@ -0,0 +1,159 @@
+.lightTheme {
+ color: black;
+ border-color: black;
+}
+.darkTheme {
+ color: white;
+ border-color: white;
+}
+.myContainer {
+ text-align: center;
+ max-width: 100vw;
+ height: 100vh;
+ overflow-x: hidden;
+ overflow-y: scroll;
+}
+
+//--------------------------------------------------------------------------------------------
+
+.inputSearchBar {
+ width: 40%;
+ font-size: large;
+ border-bottom: 10px;
+ border-radius: 5px;
+}
+
+//--------------------------------------------------------------------------------------------
+// --- Playlist ---
+
+.playlistContainer {
+ border: solid 1px black;
+ width: 98%;
+ border-top-right-radius: 10px;
+ border-top-left-radius: 10px;
+}
+
+.topBorder {
+ margin: 0px 0px 0px 0px;
+ //background-color: #dcdcdc;
+ background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
+ background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
+ text-align: left;
+ padding: 5px 0px 5px 5px;
+ border-bottom: solid 1px black;
+ border-top-right-radius: 10px;
+ border-top-left-radius: 10px;
+}
+
+.listVideoContainer {
+ height: 70vh;
+ background-color: white;
+ overflow-y: scroll;
+}
+
+.videoCell {
+ margin: 0px 0px 0px 0px;
+ padding: 10px 0px 10px 10px;
+ border-bottom: solid 1px black;
+ cursor: pointer;
+}
+.videoCell:hover {
+ background-color: #f0f0f0;
+}
+
+.videoCellFocus {
+ background-color: #e6e6e6;
+}
+.videoCellFocus:hover {
+ background-color: #e6e6e6;
+}
+
+
+// ----
+
+.imgsContainer {
+ position: relative;
+ width: 13vw;
+ height: 10vh;
+ float: left;
+}
+
+.imgVideo {
+ border: solid 1px black;
+ width: 13vw;
+ height: 10vh;
+ padding: 0px 0px 0px 0px;
+}
+
+.imgPlay {
+ position: absolute;
+ margin-left: 6vw;
+ width: 2vw;
+ margin-top: 3vh;
+ height: 4vh;
+ padding: 0px 0px 0px 0px;
+}
+
+// ----
+
+.infoContainer {
+ display: table-cell;
+ margin-left: 13vw;
+ height: 10vh;
+ padding-left: 5px;
+ vertical-align: middle;
+}
+
+.titleContainer {
+ font-weight: bold;
+ text-align: left;
+}
+
+.viewsContainer {
+ text-align: left;
+ display: flex;
+ align-items: center;
+ font-size: x-small;
+ color: grey;
+}
+mat-icon {
+ vertical-align: middle;
+ //font-size: x-small;
+}
+
+.publishedAtContainer {
+ text-align: left;
+ font-size: x-small;
+ color: grey;
+}
+
+
+// ----
+
+.bottomBorder {
+ margin: 0px 0px 0px 0px;
+ background-color: #dcdcdc;
+ border-top: solid 1px black;
+ border-bottom: solid 1px black;
+ font-size: 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 {
+ border: solid 1px black !important;
+ background-color: white !important;
+}
diff --git a/userAndAdvertiser/src/app/user/watching/page-watching-video/page-watching-video.component.spec.ts b/userAndAdvertiser/src/app/user/watching/page-watching-video/page-watching-video.component.spec.ts
new file mode 100644
index 0000000..6790456
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/watching/page-watching-video/page-watching-video.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageWatchingVideoComponent } from './page-watching-video.component';
+
+describe('PageWatchingVideoComponent', () => {
+ let component: PageWatchingVideoComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageWatchingVideoComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageWatchingVideoComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/userAndAdvertiser/src/app/user/watching/page-watching-video/page-watching-video.component.ts b/userAndAdvertiser/src/app/user/watching/page-watching-video/page-watching-video.component.ts
new file mode 100644
index 0000000..4115a7a
--- /dev/null
+++ b/userAndAdvertiser/src/app/user/watching/page-watching-video/page-watching-video.component.ts
@@ -0,0 +1,258 @@
+import { Component, OnInit } from '@angular/core';
+import {ActivatedRoute, Router} from "@angular/router";
+import {AddVideoToPlaylistsService} from "../../utils/services/addVideoToPlaylists/add-video-to-playlists.service";
+import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser";
+import {HttpParams} from "@angular/common/http";
+import {MessageService} from "../../../utils/message/message.service";
+import {ThemeService} from "../../../utils/theme/theme.service";
+
+
+
+let TAB_PLATEFORM = [
+ { name: "youtube", isSelected: false },
+ { name: "dailymotion", isSelected: false }
+];
+
+
+
+@Component({
+ selector: 'app-page-watching-video',
+ templateUrl: './page-watching-video.component.html',
+ styleUrls: ['./page-watching-video.component.scss']
+})
+export class PageWatchingVideoComponent implements OnInit
+{
+ tabPlateform = TAB_PLATEFORM;
+ sources: string = "";
+ video = {
+ title: "",
+ videoId: "",
+ views: 0,
+ publishedAt: null,
+ description: "",
+ source: "",
+ interest: ""
+ };
+ search: string = "";
+
+ ad1: any;
+ ad2: any;
+ from: string = "";
+
+ playlist: any;
+ videosInPlaylist: any[] = [];
+
+ paramsFromOldPage ;
+
+ hiddenDescription: boolean = true;
+ iframeStyle: string = "";
+ containerStyle: string = "";
+
+
+ constructor( private messageService: MessageService,
+ public themeService: ThemeService,
+ private activatedRoute: ActivatedRoute,
+ private router: Router,
+ private _sanitizer: DomSanitizer,
+ private addVideoToPlaylistsService: AddVideoToPlaylistsService ) { }
+
+
+
+ ngOnInit(): void
+ {
+ // Ask for videos
+ this.activatedRoute
+ .queryParams
+ .subscribe(paramsFromOldPage => {
+
+ this.paramsFromOldPage = paramsFromOldPage;
+ const videoId = paramsFromOldPage.videoId;
+ let source = paramsFromOldPage.source;
+
+ let params = new HttpParams();
+ if(source === "Youtube") source = "yt";
+ else if(source === "Dailymotion") source = "dm" ;
+ params = params.append("source", source);
+ this.messageService
+ .get("video/get/"+videoId, params)
+ .subscribe(ret => this.findVideoCallback(ret), err => this.findVideoCallback(err));
+ });
+
+
+ // Ask for adverts
+ let params = new HttpParams();
+ params = params.append("quantity", 2);
+ this.messageService
+ .get("user/ad", params)
+ .subscribe(ret => this.findAdCallback(ret), err => this.findAdCallback(err));
+
+
+ // Si on vient de la page "search"
+ if(this.router.url.includes("search"))
+ {
+ this.from = "search" ;
+ this.activatedRoute
+ .queryParams
+ .subscribe(paramsFromOldPage => {
+ if(paramsFromOldPage.hasOwnProperty("search")) this.search = paramsFromOldPage.search;
+ if(paramsFromOldPage.hasOwnProperty("sources")) {
+ this.sources = paramsFromOldPage.sources;
+ if(this.sources === "yt") {
+ this.tabPlateform[0].isSelected = true;
+ this.tabPlateform[1].isSelected = false;
+ }
+ else if(this.sources === "dm") {
+ this.tabPlateform[0].isSelected = false;
+ this.tabPlateform[1].isSelected = true;
+ }
+ else if(this.sources === "yt,dm") {
+ this.tabPlateform[0].isSelected = true;
+ this.tabPlateform[1].isSelected = true;
+ }
+ }
+ });
+ }
+ // si on vient de la page "myPlaylists"
+ else if(this.router.url.includes("myPlaylists"))
+ {
+ this.from = "myPlaylists";
+ this.activatedRoute
+ .queryParams
+ .subscribe(paramsFromOldPage => {
+ const _idPlaylist = paramsFromOldPage._idPlaylist;
+ this.messageService
+ .get("playlist/findOne/"+_idPlaylist)
+ .subscribe(ret => this.afterReceivingPlaylistWithVideo(ret), err => this.afterReceivingPlaylistWithVideo(err));
+ });
+
+ }
+ // si on vient de la page "history"
+ else if(this.router.url.includes("history")) this.from = "history";
+
+
+ // style
+ if(this.from === 'search' || this.from === 'history') {
+ this.containerStyle = "margin: 0 auto; width: 64vw;" ;
+ this.iframeStyle = "width: 64vw; height: 60vh;" ;
+ }
+ else {
+ this.containerStyle = "margin: 0 auto; width: 48vw;" ;
+ this.iframeStyle = "width: 48vw; height: 45vh;" ;
+ }
+ }
+
+
+
+ findVideoCallback(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log("findVideoCallback: ");
+ console.log(retour);
+ }
+ else {
+ this.video = retour.data;
+ }
+ }
+
+
+ findAdCallback(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log("findAdCallback: ");
+ console.log(retour);
+ }
+ else {
+ this.ad1 = retour.data[0];
+ this.ad2 = retour.data[1];
+ }
+ }
+
+
+ afterReceivingPlaylistWithVideo(retour: any): void
+ {
+ if(retour.status !== "success") {
+ console.log("afterReceivingPlaylistWithVideo");
+ console.log(retour);
+ }
+ else {
+ this.playlist = retour.data;
+ this.videosInPlaylist = retour.data.videos;
+ }
+ }
+
+
+ onSearch()
+ {
+ if(this.tabPlateform[0].isSelected && this.tabPlateform[1].isSelected) this.sources = "yt,dm" ;
+ else if((!this.tabPlateform[0].isSelected) && this.tabPlateform[1].isSelected) this.sources = "dm" ;
+ else if(this.tabPlateform[0].isSelected && (!this.tabPlateform[1].isSelected)) this.sources = "yt" ;
+ else this.sources = "" ;
+ let options = {
+ queryParams: {
+ search: this.search,
+ sources: this.sources,
+ indexPage: 0,
+ }
+ };
+ this.router.navigate(['/user/search'], options);
+ }
+
+
+ onAddToPlaylist(): void
+ {
+ this.addVideoToPlaylistsService.run(this.video.videoId, this.video.source, this.video.interest);
+ }
+
+
+ onRetour(): void
+ {
+ let url: string[] = [];
+ let options = {};
+
+ if(this.from === 'search')
+ {
+ url = ['/user/search'];
+ options = {
+ queryParams: {
+ search: this.paramsFromOldPage.search,
+ sources: this.paramsFromOldPage.sources,
+ indexPage: this.paramsFromOldPage.indexPage,
+ }
+ };
+ }
+ else if(this.from === 'myPlaylists') url = ["/user/myPlaylists"];
+ else if(this.from === 'history') url = ["/user/history"];
+
+ this.router.navigate(url, options);
+ }
+
+
+ safeUrl(videoId: string, source: string): SafeResourceUrl
+ {
+ let videoUrl = "" ;
+ if(source === 'Youtube') videoUrl = "https://www.youtube.com/embed/" + videoId + "?autoplay=1";
+ else if(source === 'Dailymotion') videoUrl = "https://www.dailymotion.com/embed/video/" + videoId + "?autoplay=true";
+ return this._sanitizer.bypassSecurityTrustResourceUrl(videoUrl);
+ }
+
+
+ // retourne la classe CSS de videoCell
+ getClassOfVideoCell(video0): string
+ {
+ if(video0 === this.video) return "videoCell videoCellFocus" ;
+ else return "videoCell" ;
+ }
+
+
+ onEnterOnSearchBar(event)
+ {
+ if(event.key === 'Enter') this.onSearch();
+ }
+
+
+ playlistExists(): boolean
+ {
+ return ((this.playlist !== null) && (this.playlist !== undefined));
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/utils/message/message.service.spec.ts b/userAndAdvertiser/src/app/utils/message/message.service.spec.ts
new file mode 100644
index 0000000..1db761b
--- /dev/null
+++ b/userAndAdvertiser/src/app/utils/message/message.service.spec.ts
@@ -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();
+ });
+});
diff --git a/userAndAdvertiser/src/app/utils/message/message.service.ts b/userAndAdvertiser/src/app/utils/message/message.service.ts
new file mode 100644
index 0000000..c20d5b1
--- /dev/null
+++ b/userAndAdvertiser/src/app/utils/message/message.service.ts
@@ -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
+ {
+ const urlComplete = environment.debutUrl + url ;
+ return this.http.post(urlComplete, data, {withCredentials: true});
+ }
+
+ get(url: string, params:HttpParams = new HttpParams()): Observable
+ {
+ const urlComplete = environment.debutUrl + url ;
+ return this.http.get(urlComplete,{ withCredentials: true, params: params });
+ }
+
+ put(url: string, data: any): Observable
+ {
+ const urlComplete = environment.debutUrl + url ;
+ return this.http.put(urlComplete, data, {withCredentials: true});
+ }
+
+ delete(url: string): Observable
+ {
+ const urlComplete = environment.debutUrl + url ;
+ return this.http.delete(urlComplete,{withCredentials: true});
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/utils/profil/profil.service.spec.ts b/userAndAdvertiser/src/app/utils/profil/profil.service.spec.ts
new file mode 100644
index 0000000..5cee000
--- /dev/null
+++ b/userAndAdvertiser/src/app/utils/profil/profil.service.spec.ts
@@ -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();
+ });
+});
diff --git a/userAndAdvertiser/src/app/utils/profil/profil.service.ts b/userAndAdvertiser/src/app/utils/profil/profil.service.ts
new file mode 100644
index 0000000..4bbe5ea
--- /dev/null
+++ b/userAndAdvertiser/src/app/utils/profil/profil.service.ts
@@ -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);
+ }
+
+}
diff --git a/userAndAdvertiser/src/app/utils/theme/theme.service.spec.ts b/userAndAdvertiser/src/app/utils/theme/theme.service.spec.ts
new file mode 100644
index 0000000..1c2957b
--- /dev/null
+++ b/userAndAdvertiser/src/app/utils/theme/theme.service.spec.ts
@@ -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();
+ });
+});
diff --git a/userAndAdvertiser/src/app/utils/theme/theme.service.ts b/userAndAdvertiser/src/app/utils/theme/theme.service.ts
new file mode 100644
index 0000000..00768e5
--- /dev/null
+++ b/userAndAdvertiser/src/app/utils/theme/theme.service.ts
@@ -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"
+ }
+
+}
diff --git a/userAndAdvertiser/src/assets/.gitkeep b/userAndAdvertiser/src/assets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/userAndAdvertiser/src/assets/darkBackground.webp b/userAndAdvertiser/src/assets/darkBackground.webp
new file mode 100644
index 0000000..0d0692b
Binary files /dev/null and b/userAndAdvertiser/src/assets/darkBackground.webp differ
diff --git a/userAndAdvertiser/src/assets/lightBackground.jpg b/userAndAdvertiser/src/assets/lightBackground.jpg
new file mode 100644
index 0000000..164cb51
Binary files /dev/null and b/userAndAdvertiser/src/assets/lightBackground.jpg differ
diff --git a/userAndAdvertiser/src/assets/logo.png b/userAndAdvertiser/src/assets/logo.png
new file mode 100644
index 0000000..93b9375
Binary files /dev/null and b/userAndAdvertiser/src/assets/logo.png differ
diff --git a/userAndAdvertiser/src/assets/logo_plateforms/dailymotion.png b/userAndAdvertiser/src/assets/logo_plateforms/dailymotion.png
new file mode 100644
index 0000000..d35ee8a
Binary files /dev/null and b/userAndAdvertiser/src/assets/logo_plateforms/dailymotion.png differ
diff --git a/userAndAdvertiser/src/assets/logo_plateforms/youtube.png b/userAndAdvertiser/src/assets/logo_plateforms/youtube.png
new file mode 100644
index 0000000..5924c8d
Binary files /dev/null and b/userAndAdvertiser/src/assets/logo_plateforms/youtube.png differ
diff --git a/userAndAdvertiser/src/assets/play.png b/userAndAdvertiser/src/assets/play.png
new file mode 100644
index 0000000..194f73b
Binary files /dev/null and b/userAndAdvertiser/src/assets/play.png differ
diff --git a/userAndAdvertiser/src/assets/profil.png b/userAndAdvertiser/src/assets/profil.png
new file mode 100644
index 0000000..b35b2e4
Binary files /dev/null and b/userAndAdvertiser/src/assets/profil.png differ
diff --git a/userAndAdvertiser/src/assets/uploadFile.png b/userAndAdvertiser/src/assets/uploadFile.png
new file mode 100644
index 0000000..cff9f38
Binary files /dev/null and b/userAndAdvertiser/src/assets/uploadFile.png differ
diff --git a/userAndAdvertiser/src/environments/environment.prod.ts b/userAndAdvertiser/src/environments/environment.prod.ts
new file mode 100644
index 0000000..8d9f516
--- /dev/null
+++ b/userAndAdvertiser/src/environments/environment.prod.ts
@@ -0,0 +1,4 @@
+export const environment = {
+ production: true,
+ debutUrl: "https://polynotfound.herokuapp.com/api/"
+};
diff --git a/userAndAdvertiser/src/environments/environment.ts b/userAndAdvertiser/src/environments/environment.ts
new file mode 100644
index 0000000..6c4970f
--- /dev/null
+++ b/userAndAdvertiser/src/environments/environment.ts
@@ -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.
diff --git a/userAndAdvertiser/src/favicon.ico b/userAndAdvertiser/src/favicon.ico
new file mode 100644
index 0000000..997406a
Binary files /dev/null and b/userAndAdvertiser/src/favicon.ico differ
diff --git a/userAndAdvertiser/src/index.html b/userAndAdvertiser/src/index.html
new file mode 100644
index 0000000..0e892e2
--- /dev/null
+++ b/userAndAdvertiser/src/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+ StreamNotFound
+
+
+
+
+
+
+
+
diff --git a/userAndAdvertiser/src/main.ts b/userAndAdvertiser/src/main.ts
new file mode 100644
index 0000000..c7b673c
--- /dev/null
+++ b/userAndAdvertiser/src/main.ts
@@ -0,0 +1,12 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+ enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+ .catch(err => console.error(err));
diff --git a/userAndAdvertiser/src/polyfills.ts b/userAndAdvertiser/src/polyfills.ts
new file mode 100644
index 0000000..373f538
--- /dev/null
+++ b/userAndAdvertiser/src/polyfills.ts
@@ -0,0 +1,65 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/**
+ * IE11 requires the following for NgClass support on SVG elements
+ */
+// import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/**
+ * Web Animations `@angular/platform-browser/animations`
+ * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
+ * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
+ */
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ * because those flags need to be set before `zone.js` being loaded, and webpack
+ * will put import in the top of bundle, so user need to create a separate file
+ * in this directory (for example: zone-flags.ts), and put the following flags
+ * into that file, and then add the following code before importing zone.js.
+ * import './zone-flags';
+ *
+ * The flags allowed in zone-flags.ts are listed here.
+ *
+ * The following flags will work for all browsers.
+ *
+ * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+ *
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ *
+ * (window as any).__Zone_enable_cross_context_check = true;
+ *
+ */
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js'; // Included with Angular CLI.
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/userAndAdvertiser/src/styles.scss b/userAndAdvertiser/src/styles.scss
new file mode 100644
index 0000000..90d4ee0
--- /dev/null
+++ b/userAndAdvertiser/src/styles.scss
@@ -0,0 +1 @@
+/* You can add global styles to this file, and also import other style files */
diff --git a/userAndAdvertiser/src/test.ts b/userAndAdvertiser/src/test.ts
new file mode 100644
index 0000000..b4dd603
--- /dev/null
+++ b/userAndAdvertiser/src/test.ts
@@ -0,0 +1,27 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/testing';
+import { getTestBed } from '@angular/core/testing';
+import {
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+declare const require: {
+ context(path: string, deep?: boolean, filter?: RegExp): {
+ keys(): string[];
+ (id: string): T;
+ };
+};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting(),
+ { teardown: { destroyAfterEach: true }},
+);
+
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);
diff --git a/userAndAdvertiser/tsconfig.app.json b/userAndAdvertiser/tsconfig.app.json
new file mode 100644
index 0000000..82d91dc
--- /dev/null
+++ b/userAndAdvertiser/tsconfig.app.json
@@ -0,0 +1,15 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./out-tsc/app",
+ "types": []
+ },
+ "files": [
+ "src/main.ts",
+ "src/polyfills.ts"
+ ],
+ "include": [
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/userAndAdvertiser/tsconfig.json b/userAndAdvertiser/tsconfig.json
new file mode 100644
index 0000000..4a4dc62
--- /dev/null
+++ b/userAndAdvertiser/tsconfig.json
@@ -0,0 +1,23 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "baseUrl": "./",
+ "outDir": "./dist/out-tsc",
+ "sourceMap": true,
+ "declaration": false,
+ "downlevelIteration": true,
+ "experimentalDecorators": true,
+ "moduleResolution": "node",
+ "importHelpers": true,
+ "target": "es2015",
+ "module": "es2020",
+ "lib": [
+ "es2018",
+ "dom"
+ ]
+ },
+ "angularCompilerOptions": {
+ "enableI18nLegacyMessageIdFormat": false
+ }
+}
diff --git a/userAndAdvertiser/tsconfig.spec.json b/userAndAdvertiser/tsconfig.spec.json
new file mode 100644
index 0000000..092345b
--- /dev/null
+++ b/userAndAdvertiser/tsconfig.spec.json
@@ -0,0 +1,18 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./out-tsc/spec",
+ "types": [
+ "jasmine"
+ ]
+ },
+ "files": [
+ "src/test.ts",
+ "src/polyfills.ts"
+ ],
+ "include": [
+ "src/**/*.spec.ts",
+ "src/**/*.d.ts"
+ ]
+}