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] : ''}} + + (+{{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}} +
+ + +
+ + +
+ +
+ + + + + + + + + +
+
+ +

+ +
+ + +
+ + + + Email + +
+ + + + Pseudo + +
+ + + + Mot de passe + +
+ + + + Confirmation mot de passe + + + +
+ + +
+ + + + Date de naissance + +
+ + + + Homme     + Femme + +

+ + + +
+ +
+
+ + + + + + + + + + +
+
+ +

+ + + +
+ + +
+ + + Email + +
+ + + Pseudo + +
+ + + Entreprise + +
+
+ + +
+ + + Mot de passe + +
+ + + Confirmation nouveau mot de passe + + +
+ +
+ + +
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

+ User Icon +
+ + +
+ + + +
+ {{errorMessage}} +
+ +
+ + + + +
+
+ + +
+
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 @@ +
+
+ + + + + + + + + +
+ + + Utilisateur standard    + Annonceur + + + +
+ +
+
+
+ + + + +
+ + + + +
+ {{errorMessage}} +
+ + +
+ + +
+
+
+ +
+ +
+
+ + + + + + + +
+
+ + +
+

Compte

+ + + + Pseudo + + +
+ + + + Mot de passe + + +
+ + + + Confirmation mot de passe + + +
+ + +
+

Informations personelles

+ + + + Email + + +
+ + + + Homme     + Femme + +

+ + + + Date de naissance + + + + + +
+ +
+
+ + + + + + + + + + + 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 +
+

+ {{ 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] : ''}} + + (+{{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 + + + + + + + + + Commentaire + +
+ + + + URL + +
+ + + 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}}

+ + + + + + + +
+
Images:
+
+
{{image.url}}
+
+
+ + +
+
Centre d'intérêt :
+
+
• {{tag}}
+
+
+ + +
+
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 + +
+ + + + + + + {{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

+ User Icon +
+ + +
+ + + +
+ {{errorMessage}} +
+ +
+ + + + +
+
+ + +
+
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 @@ +
+
+ + + + + + + + + +
+ + + Utilisateur standard    + Annonceur + + + +
+ +
+
+
+ + + + +
+ + + + +
+ {{errorMessage}} +
+ + +
+ + +
+
+
+ +
+ +
+
+ + + + + + + +
+
+ + +
+

Compte

+ + + + Pseudo + + +
+ + + + Mot de passe + + +
+ + + + Confirmation mot de passe + + +
+ + +
+

Informations personelles

+ + + + Email + + +
+ + + + Homme     + Femme + +

+ + + + Date de naissance + + + + + +
+ +
+
+ + + + + + + + + + + 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 + {{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}} + + +
+
+ + + +
+
+ +
+
+ + + + + + +
+
a
+
+ +
+
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' }}
+
+ + +
+
Centres d'intérêt:
+
+
+
{{interest}}
+
+
+
+ +
+
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 @@ +
+
+ + +
+ +
+ + + + + + +
+ + +
+
+ + +
+
+ + +
+ + +   + + logo + +   + + + +   + + logo + +   + +
+ +
+ + + + + + + + + + +
+ +
+
+ + + +
+ +
+
+ + + +
+ +
+
+ +
+

+ +
+
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 @@ + + + + +
+ + +
+ + + +
+ + +
+ + + + + + + + ytb + dlm + + + + +
+ {{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 @@ + + + +
+
+ + +
+ +
+ + + + +
+ + +
+
+ + +
+
+ + +
+ + +   + + logo + +   + + + +   + + logo + +   + +
+ +
+ + + + +
+ + +
+ + + +

+ +
+
+ + + + + + + +
+ + +
+ +
+ + +
+
+
+ + +
+ +
+ +
+
+ + + + + + + +
+ + +
+ +
+ + +
+
+
+ + +
+
+
+ +
+
+ + + + + + + + + +
+ + +
+ +
+ + +
+ +
+ +
+ + + +
+ + +
+ ytb + dlm +
  {{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" + ] +}