diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000..427441d --- /dev/null +++ b/.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/.editorconfig b/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/.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/.gitignore b/.gitignore new file mode 100644 index 0000000..abe66d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc +# Only exists if Bazel was run +/bazel-out + +# dependencies +/node_modules +package-lock.json + +# profiling files +chrome-profiler-events*.json + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..063b78f --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: npm start diff --git a/README.md b/README.md index 9ab8554..0e31037 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,28 @@ -# PolyNotFound +# Admin -Le projet est séparé en 3 parties : +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.10. -- 2 Frontend : - - 1 partie Administrateur - - 1 partie pour les utilisateurs et les publicitaires -- 1 Backend +## Development server -Nous avons décidé que chaque partie du projet a sa propre branche git et non leur propre répertoire git. +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. -En effet, les branches concernées par le projet est : -- Frontend partie Administrateur : `front-admin` -- Frontend partie pour les utilisateurs et les publicitaires : `front-user-advertiser` -- Backend : `backend` +## Code scaffolding -Nous pouvons récupérer une branche git avec `git checkout `. +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. -# Lancer le projet en Local +## Build -Dans un premier temps, il est obligatoire d'avoir NodeJS (version >12 au minimum), [téléchargeable sur cette page](https://nodejs.org/en/download/). +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. +Then run `npm start` -Pour lancer le projet en local dans son ensemble, il faut impérativement avoir les branches concernées dans leur propre dossier. -Il faudra donc __clone__ le projet 3 fois. +## Running unit tests -Dans un dossier nommé par exemple `Polynotfound`: -- frontend-admin : `git clone --branch frontend-admin https://github.com/NyxiumYuuki/PolyNotFound.git polynotfound-frontend-admin` -- front-user-advertiser : `git clone --branch front-user-advertiser https://github.com/NyxiumYuuki/PolyNotFound.git polynotfound-front-user-advertiser` -- backend : `git clone --branch backend https://github.com/NyxiumYuuki/PolyNotFound.git polynotfound-backend` +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). -Un README est disponible pour chaque branche pour lancer le projet en local soit en mode **production** soit en mode **développement**. +## Running end-to-end tests -# Lancer le projet en ligne avec Heroku +Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. -Nous avons déployé le projet en ligne avec Heroku. +## Further help -Le projet est disponible sur ces URL : -- Partie Utilisateurs et Publicitaires : https://polynotfound.herokuapp.com/ -- Partie Administrateur : https://admin-polynotfound.herokuapp.com/ -- API : https://api-polynotfound.herokuapp.com/ \ No newline at end of file +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/angular.json b/angular.json new file mode 100644 index 0000000..ec92bc3 --- /dev/null +++ b/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/frontend-admin", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", + "src/styles.scss", + "node_modules/bootstrap/scss/bootstrap.scss" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "1mb", + "maximumError": "2mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "browserTarget": "admin:build:production" + }, + "development": { + "browserTarget": "admin:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "admin:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.spec.json", + "karmaConfig": "karma.conf.js", + "inlineStyleLanguage": "scss", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "src/styles.scss" + ], + "scripts": [] + } + } + } + } + }, + "defaultProject": "admin" +} diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js new file mode 100644 index 0000000..361e7f0 --- /dev/null +++ b/e2e/protractor.conf.js @@ -0,0 +1,37 @@ +// @ts-check +// Protractor configuration file, see link for more information +// https://github.com/angular/protractor/blob/master/lib/config.ts + +const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); + +/** + * @type { import("protractor").Config } + */ +exports.config = { + allScriptsTimeout: 11000, + specs: [ + './src/**/*.e2e-spec.ts' + ], + capabilities: { + browserName: 'chrome' + }, + directConnect: true, + SELENIUM_PROMISE_MANAGER: false, + baseUrl: 'http://localhost:4200/', + framework: 'jasmine', + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000, + print: function() {} + }, + onPrepare() { + require('ts-node').register({ + project: require('path').join(__dirname, './tsconfig.json') + }); + jasmine.getEnv().addReporter(new SpecReporter({ + spec: { + displayStacktrace: StacktraceOption.PRETTY + } + })); + } +}; \ No newline at end of file diff --git a/e2e/src/app.e2e-spec.ts b/e2e/src/app.e2e-spec.ts new file mode 100644 index 0000000..b4a36ca --- /dev/null +++ b/e2e/src/app.e2e-spec.ts @@ -0,0 +1,23 @@ +import { browser, logging } from 'protractor'; +import { AppPage } from './app.po'; + +describe('workspace-project App', () => { + let page: AppPage; + + beforeEach(() => { + page = new AppPage(); + }); + + it('should display welcome message', async () => { + await page.navigateTo(); + expect(await page.getTitleText()).toEqual('frontend app is running!'); + }); + + afterEach(async () => { + // Assert that there are no errors emitted from the browser + const logs = await browser.manage().logs().get(logging.Type.BROWSER); + expect(logs).not.toContain(jasmine.objectContaining({ + level: logging.Level.SEVERE, + } as logging.Entry)); + }); +}); diff --git a/e2e/src/app.po.ts b/e2e/src/app.po.ts new file mode 100644 index 0000000..c9c85ab --- /dev/null +++ b/e2e/src/app.po.ts @@ -0,0 +1,11 @@ +import { browser, by, element } from 'protractor'; + +export class AppPage { + async navigateTo(): Promise { + return browser.get(browser.baseUrl); + } + + async getTitleText(): Promise { + return element(by.css('app-root .content span')).getText(); + } +} diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json new file mode 100644 index 0000000..0782539 --- /dev/null +++ b/e2e/tsconfig.json @@ -0,0 +1,13 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/e2e", + "module": "commonjs", + "target": "es2018", + "types": [ + "jasmine", + "node" + ] + } +} diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..2c2c9f1 --- /dev/null +++ b/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: 'admin', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma') + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, './coverage/admin'), + subdir: '.', + reporters: [ + { type: 'html' }, + { type: 'text-summary' } + ] + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..7bce3d9 --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "frontend-admin", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "node server.js", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "private": true, + "dependencies": { + "@angular/animations": "~12.2.0", + "@angular/cdk": "^13.1.1", + "@angular/common": "~12.2.0", + "@angular/compiler": "~12.2.0", + "@angular/core": "~12.2.0", + "@angular/forms": "~12.2.0", + "@angular/material": "^13.1.1", + "@angular/platform-browser": "~12.2.0", + "@angular/platform-browser-dynamic": "~12.2.0", + "@angular/router": "~12.2.0", + "bootstrap": "^5.1.3", + "cors": "^2.8.5", + "jquery": "^3.6.0", + "popper": "^1.0.1", + "rxjs": "~6.6.0", + "tslib": "^2.3.0", + "zone.js": "~0.11.4" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~12.2.10", + "@angular/cli": "~12.2.10", + "@angular/compiler-cli": "~12.2.0", + "@types/jasmine": "~3.8.0", + "@types/node": "^12.11.1", + "jasmine-core": "~3.8.0", + "karma": "~6.3.0", + "karma-chrome-launcher": "~3.1.0", + "karma-coverage": "~2.0.3", + "karma-jasmine": "~4.0.0", + "karma-jasmine-html-reporter": "~1.7.0", + "typescript": "~4.3.5" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..d5e0d27 --- /dev/null +++ b/server.js @@ -0,0 +1,13 @@ +const path = require('path'); +const express = require('express'); +const app = express(); +const port = process.env.PORT || 3000; + +app.use(express.static(__dirname + '/dist/frontend-admin')); +app.get('/*', function(req,res) { + res.sendFile(path.join(__dirname+ '/dist/frontend-admin/index.html')); +}); + +app.listen(port, '0.0.0.0',() => { + console.log (`listening on port ${port}`); +}); diff --git a/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.html b/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.html new file mode 100644 index 0000000..638658c --- /dev/null +++ b/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.html @@ -0,0 +1,172 @@ +
+
+ + +

+ + + + +
+ + +
+ Filtre +
+ + + + +
+ +
+ + +
+ + +
+ visible
+ non visible +
+ + +
+ + Sujets + + + {{formControlInterests.value ? formControlInterests.value[0] : ''}} + + (+{{formControlInterests.value.length - 1}} {{formControlInterests.value?.length === 2 ? 'autre' : 'autres'}}) + + + {{topping}} + + + +
+ + +
+ + Période de date de création + + + + + + + + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Titre + {{advert.title}} + Entreprise + {{advert.company}} + Email + {{advert.email}} + 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 + + +
+
+ +
+

+ +
+
diff --git a/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.scss b/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.scss new file mode 100644 index 0000000..2485d21 --- /dev/null +++ b/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/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.spec.ts b/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/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/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.ts b/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.ts new file mode 100644 index 0000000..cca6c09 --- /dev/null +++ b/src/app/admin/adList/page-ad-list-admin/page-ad-list-admin.component.ts @@ -0,0 +1,286 @@ +import {AfterViewInit, Component, ViewChild} from '@angular/core'; +import {MatSort} from "@angular/material/sort"; +import {MatPaginator} from "@angular/material/paginator"; +import {MatDialog} from "@angular/material/dialog"; +import {MatSnackBar} from "@angular/material/snack-bar"; +import {MatTableDataSource} from "@angular/material/table"; +import {PopupDeleteAdAdminComponent} from "../popup-delete-ad-admin/popup-delete-ad-admin.component"; +import {PopupVisualizeImagesAdminComponent} from "../popup-visualize-images-admin/popup-visualize-images-admin.component"; +import {FormControl, FormGroup} from "@angular/forms"; +import {HttpParams} from "@angular/common/http"; +import {ThemeService} from "../../../utils/theme/theme.service"; +import {MessageService} from "../../../utils/message/message.service"; +import {DatePipe} from "@angular/common"; + + + +export interface AdvertWithCountViewsAndCompany { + id: string, + userId: string, + company: string, + email: string, + title: string, + url: string, + images: { + url: string, + description: string, + }[], + interests: string[], + comment: string, + views: Date[], + countViews: number, + isVisible: boolean, + isActive: boolean, + createdAt: Date, + updatedAt: Date, +} + + +@Component({ + selector: 'app-page-ad-list-admin', + templateUrl: './page-ad-list-admin.component.html', + styleUrls: ['./page-ad-list-admin.component.scss'] +}) +export class PageAdListAdminComponent implements AfterViewInit +{ + tabAdvertWithCountViews: AdvertWithCountViewsAndCompany[] = []; + tabAdvertiser: any[]; + displayedColumns: string[] = [ 'title', 'company', 'email', 'interests', 'createdAt', 'updatedAt', 'countViews', 'isVisible', 'actions' ]; + dataSource ; + @ViewChild(MatSort) sort: MatSort; + @ViewChild(MatPaginator) paginator: MatPaginator; + + filteredText: string = "" ; + visible: boolean = true; + noVisible: boolean = true; + campaignOne = new FormGroup({ + start: new FormControl(null), + end: new FormControl(null), + }); + formControlInterests = new FormControl(); + allInterests: string[] = []; + + + constructor( public themeService: ThemeService, + public dialog: MatDialog, + private snackBar: MatSnackBar, + private messageService: MessageService) { } + + + ngAfterViewInit(): void + { + // Ask for ads and then for advertiser + let params = new HttpParams(); + params = params.append("isActive", true); + this.messageService + .get("ad/findAll", params) + .subscribe(ret => this.afterReceivingAds(ret), err => this.afterReceivingAds(err) ); + + // Ask for interest + this.messageService + .get("misc/getInterests") + .subscribe(ret => this.afterReceivingInterests(ret), err => this.afterReceivingInterests(err) ); + } + + + afterReceivingAds(retour: any): void + { + if(retour.status !== "success") { + console.log(retour); + } + else { + const tabAdvert = retour.data; + this.messageService + .get("user/findAll") + .subscribe(ret => this.afterReceivingAdvertiser(ret, tabAdvert), err => this.afterReceivingAdvertiser(err, tabAdvert) ); + } + } + + + afterReceivingAdvertiser(retour: any, tabAdvert): void + { + if(retour.status !== "success") { + console.log(retour); + } + else { + this.tabAdvertiser = retour.data.filter(x => x.role.name === "advertiser"); + for(let advert of tabAdvert) this.tabAdvertWithCountViews.push(this.advertToAdvertWithCountViewsAndCompany(advert)); + this.dataSource = new MatTableDataSource(); + this.onFilter(); + } + } + + + afterReceivingInterests(retour: any): void + { + if(retour.status !== "success") { + console.log(retour); + } + else { + this.allInterests = retour.data.map(x => x.interest); + this.allInterests.sort(); + } + } + + + onVisualizeImages(advert: AdvertWithCountViewsAndCompany) + { + if(advert.images.length !== 0) + { + const config = { + width: '30%', + height: '90%', + data: { images: advert.images } + }; + this.dialog + .open(PopupVisualizeImagesAdminComponent, config) + .afterClosed() + .subscribe(retour => {}); + } + else { + const config = { duration: 2000, panelClass: "custom-class" }; + const message = "Cette annonce ne contient aucune image" ; + this.snackBar.open( message, "", config); + } + } + + + onDelete(advert: AdvertWithCountViewsAndCompany): void + { + const config = { + data: { advert: advert } + }; + this.dialog + .open(PopupDeleteAdAdminComponent, config) + .afterClosed() + .subscribe( retour => { + + const config = { duration: 1000, panelClass: "custom-class" }; + let message = "" ; + if((retour === undefined) || (retour === null)) { + message = "Opération annulée" ; + } + else { + const index = this.dataSource.data.findIndex( elt => (elt.id === advert.id)); + this.dataSource.data.splice(index, 1); + this.dataSource.data = this.dataSource.data; + this.dataSource = this.dataSource; + message = advert.title + " a bien été supprimée ✔" ; + } + this.snackBar.open( message, "", config); + }); + } + + + onFilter(): void + { + const startDate = this.campaignOne.get("start").value; + const endDate = this.campaignOne.get("end").value; + + this.dataSource.data = []; + for(let advert of this.tabAdvertWithCountViews) + { + // filtre textuelle + let valide: boolean = this.isTextFiltrationValid(advert);; + + // filtre actif + if(valide) + { + if(advert.isVisible && this.visible) valide = true; + else if((!advert.isVisible) && this.noVisible) valide = true; + else valide = false; + } + + // filtre date + if(valide) + { + if ((advert.createdAt === null) && (startDate !== null)) valide = false; + else if ((advert.createdAt === null) && (endDate !== null)) valide = false; + else if (startDate !== null) + { + let timeCreatedAt = 0; + if(advert.createdAt !== null) timeCreatedAt = (new Date(advert.createdAt)).getTime(); + + if(startDate.getTime() > timeCreatedAt) valide = false; + else if (endDate !== null) + { + if(endDate.getTime() < timeCreatedAt) valide = false; + } + } + } + + // filtre interests + if(valide) { + if(this.formControlInterests.value !== null) { + for (let interest of this.formControlInterests.value) { + if (advert.interests.indexOf(interest) === -1) { + valide = false; + break; + } + } + } + } + + if(valide) this.dataSource.data.push(advert); + } + + this.dataSource = new MatTableDataSource(this.dataSource.data); + this.dataSource.sort = this.sort; + this.dataSource.paginator = this.paginator; + } + + + isTextFiltrationValid(advert): boolean + { + let datePipe = new DatePipe('en-GB'); + if(advert.title.includes(this.filteredText)) return true; + if(advert.company.includes(this.filteredText)) return true; + if(advert.email.includes(this.filteredText)) return true; + const createdAt = datePipe.transform(new Date(advert.createdAt), 'dd/MM/yyyy à HH:mm:ss'); + if(createdAt.includes(this.filteredText)) return true; + const updatedAt = datePipe.transform(new Date(advert.updatedAt), 'dd/MM/yyyy à HH:mm:ss'); + if(updatedAt.includes(this.filteredText)) return true; + if(advert.countViews.toString().includes(this.filteredText)) return true; + return false; + } + + + advertToAdvertWithCountViewsAndCompany(advert): AdvertWithCountViewsAndCompany + { + let company0 = "company" ; + let email0 = "email" ; + for(let advertiser of this.tabAdvertiser) + { + if(advert.userId === advertiser.id) { + company0 = advertiser.company; + email0 = advertiser.email; + break; + } + } + + return { + id: advert.id, + userId: advert.userId, + title: advert.title, + company: company0, + email: email0, + url: advert.url, + images: advert.images, + interests: advert.interests, + comment: advert.comment, + views: advert.views, + countViews: advert.views.length, + isVisible: advert.isVisible, + isActive: advert.isActive, + createdAt: advert.createdAt, + updatedAt: advert.updatedAt, + } + } + + + onEffacerDate(): void { + this.campaignOne.setValue({start: null, end: null }); + this.onFilter(); + } + +} diff --git a/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.html b/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.html new file mode 100644 index 0000000..d92e686 --- /dev/null +++ b/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/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.scss b/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.spec.ts b/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/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/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.ts b/src/app/admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component.ts new file mode 100644 index 0000000..8470921 --- /dev/null +++ b/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/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.html b/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.html new file mode 100644 index 0000000..dfbc2fe --- /dev/null +++ b/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.html @@ -0,0 +1,20 @@ +
+

+ +
+ + + + + + + + + + + + + + diff --git a/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.scss b/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.scss new file mode 100644 index 0000000..eb60d48 --- /dev/null +++ b/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/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.spec.ts b/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/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/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.ts b/src/app/admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component.ts new file mode 100644 index 0000000..634d051 --- /dev/null +++ b/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/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.html b/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.html new file mode 100644 index 0000000..7e025c5 --- /dev/null +++ b/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/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.scss b/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.scss new file mode 100644 index 0000000..966c9a2 --- /dev/null +++ b/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/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.spec.ts b/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.spec.ts new file mode 100644 index 0000000..39fbdd9 --- /dev/null +++ b/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/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.ts b/src/app/admin/myProfil/page-profil-admin/page-profil-admin.component.ts new file mode 100644 index 0000000..a282706 --- /dev/null +++ b/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/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.html b/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.html new file mode 100644 index 0000000..38cf7e5 --- /dev/null +++ b/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/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.scss b/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.scss new file mode 100644 index 0000000..1968e90 --- /dev/null +++ b/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/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.spec.ts b/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.spec.ts new file mode 100644 index 0000000..9f1a0f5 --- /dev/null +++ b/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/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.ts b/src/app/admin/myProfil/popup-update-admin/popup-update-admin.component.ts new file mode 100644 index 0000000..2e11b68 --- /dev/null +++ b/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/src/app/admin/userList/input-interests-admin/input-interests-admin.component.html b/src/app/admin/userList/input-interests-admin/input-interests-admin.component.html new file mode 100644 index 0000000..2a7c484 --- /dev/null +++ b/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/src/app/admin/userList/input-interests-admin/input-interests-admin.component.scss b/src/app/admin/userList/input-interests-admin/input-interests-admin.component.scss new file mode 100644 index 0000000..c7acb4b --- /dev/null +++ b/src/app/admin/userList/input-interests-admin/input-interests-admin.component.scss @@ -0,0 +1,3 @@ +mat-form-field { + width: 100%; +} diff --git a/src/app/admin/userList/input-interests-admin/input-interests-admin.component.spec.ts b/src/app/admin/userList/input-interests-admin/input-interests-admin.component.spec.ts new file mode 100644 index 0000000..62f9051 --- /dev/null +++ b/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/src/app/admin/userList/input-interests-admin/input-interests-admin.component.ts b/src/app/admin/userList/input-interests-admin/input-interests-admin.component.ts new file mode 100644 index 0000000..c0b3560 --- /dev/null +++ b/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/src/app/admin/userList/page-user-list/page-user-list.component.html b/src/app/admin/userList/page-user-list/page-user-list.component.html new file mode 100644 index 0000000..754a8df --- /dev/null +++ b/src/app/admin/userList/page-user-list/page-user-list.component.html @@ -0,0 +1,201 @@ +
+
+ + + +

+ + + + +
+ + +
+
+ + +
+ Filtre +
+ + + + +
+ +
+ + +
+ + +
+ + + Utilisateur +
+ + Annonceur +
+ + Admin + +
+
+ + +
+ actif
+ non actif +
+ + +
+ + Période de dernière connexion + + + + + + + + + +
+ +
+ +
+
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ power_settings_new + + + + Pseudo + {{user.login}} + Email + {{user.email}} + Entreprise + {{user.company}} + 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é + +
+
+ +
+

+ +
+
diff --git a/src/app/admin/userList/page-user-list/page-user-list.component.scss b/src/app/admin/userList/page-user-list/page-user-list.component.scss new file mode 100644 index 0000000..bbeac05 --- /dev/null +++ b/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/src/app/admin/userList/page-user-list/page-user-list.component.spec.ts b/src/app/admin/userList/page-user-list/page-user-list.component.spec.ts new file mode 100644 index 0000000..edbbffe --- /dev/null +++ b/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/src/app/admin/userList/page-user-list/page-user-list.component.ts b/src/app/admin/userList/page-user-list/page-user-list.component.ts new file mode 100644 index 0000000..22441e9 --- /dev/null +++ b/src/app/admin/userList/page-user-list/page-user-list.component.ts @@ -0,0 +1,242 @@ +import {AfterViewInit, Component, ViewChild} from '@angular/core'; +import {MatSort} from "@angular/material/sort"; +import {MatPaginator} from "@angular/material/paginator"; +import {MatDialog} from "@angular/material/dialog"; +import {MatSnackBar} from "@angular/material/snack-bar"; +import {MatTableDataSource} from "@angular/material/table"; +import {PopupCreateUserComponent} from "../popup-create-user/popup-create-user.component"; +import {ThemeService} from "../../../utils/theme/theme.service"; +import {MessageService} from "../../../utils/message/message.service"; +import {FormControl, FormGroup} from "@angular/forms"; +import {DatePipe} from "@angular/common"; + + + +@Component({ + selector: 'app-page-user-list', + templateUrl: './page-user-list.component.html', + styleUrls: ['./page-user-list.component.scss'] +}) +export class PageUserListComponent implements AfterViewInit +{ + displayedColumns: string[]; + displayedColumnsUser: string[] = [ 'isActive', 'login', 'email', 'dateOfBirth', 'age', 'sexe', 'interests', 'createdAt', 'lastConnexion' ]; + displayedColumnsAdvertiser: string[] = [ 'isActive', 'login', 'email', 'company', 'createdAt', 'lastConnexion', 'isAccepted' ]; + displayedColumnsAdmin: string[] = [ 'isActive', 'login', 'email', 'createdAt', 'lastConnexion' ]; + + tabUser: any[] = []; + tabAdvertiser: any[] = []; + tabAdmin: any[] = []; + + roleName: string = "user" ; + dataSource ; + @ViewChild(MatSort) sort: MatSort; + @ViewChild(MatPaginator) paginator: MatPaginator; + + filteredText: string = "" ; + active: boolean = true; + noActive: boolean = false; + campaignOne = new FormGroup({ + start: new FormControl(null), + end: new FormControl(null), + }); + + + + constructor( public themeService: ThemeService, + public dialog: MatDialog, + private snackBar: MatSnackBar, + private messageService: MessageService ) { } + + + ngAfterViewInit(): void + { + this.messageService + .get("user/findAll") + .subscribe(ret => this.ngAfterViewInitCallback(ret), err => this.ngAfterViewInitCallback(err)); + } + + + ngAfterViewInitCallback(retour: any): void + { + if(retour.status !== "success") { + console.log(retour); + } + else { + for(let person of retour.data) + { + if(person.role.name === "user") { + person["age"] = this.getAge(person.dateOfBirth); + delete person.profileImageUrl; + this.tabUser.push(person); + } + else if(person.role.name === "advertiser") this.tabAdvertiser.push(person); + else this.tabAdmin.push(person); + } + this.onFilter(); + } + } + + + onCreateUser(): void + { + const config = { width: '50%' }; + this.dialog + .open(PopupCreateUserComponent, config) + .afterClosed() + .subscribe( retour => { + + const config = { duration: 1000, panelClass: "custom-class" }; + if((retour === null) || (retour === undefined)) { + this.snackBar.open( "Opération annulée", "", config); + } + else { + this.snackBar.open( "L'utilisateur a bien été créé", "", config); + if(retour.role.name === "user") this.tabUser.push(retour); + else if(retour.role.name === "advertiser") this.tabAdvertiser.push(retour); + else if(retour.role.name === "admin") this.tabAdmin.push(retour); + this.onFilter(); + } + }); + } + + + onSliderIsActive(user: any): void + { + // il faut envoyer la négation de user.isActive + this.messageService + .put("user/update/"+user.id, { isActive: !user.isActive }) + .subscribe( + ret => {}, + err => { + console.log("onSliderIsActive"); + console.log(err); + } + ); + } + + + onSlideIsAccepted(user: any): void + { + // il faut envoyer la négation de user.role.isAccepted + const role0 = { + name: user.role.name, + permission: user.role.permission, + isAccepted: !user.role.isAccepted, + }; + this.messageService + .put("user/update/"+user.id, {role: role0}) + .subscribe( + ret => {}, + err => { + console.log("onSlideIsAccepted"); + console.log(err); + } + ); + } + + + getAge(date: Date): number + { + if((date === null) || (date === undefined)) return -1; + else { + const diff = Date.now() - (new Date(date)).getTime(); + const age = new Date(diff); + return Math.abs(age.getUTCFullYear() - 1970); + } + } + + + onFilter(): void + { + const startDate = this.campaignOne.get("start").value; + const endDate = this.campaignOne.get("end").value; + + let tab1 = []; + if(this.roleName === "user") { + this.displayedColumns = this.displayedColumnsUser; + tab1 = this.tabUser; + } + else if(this.roleName === "advertiser") { + this.displayedColumns = this.displayedColumnsAdvertiser; + tab1 = this.tabAdvertiser; + } + else if(this.roleName === "admin") { + this.displayedColumns = this.displayedColumnsAdmin; + tab1 = this.tabAdmin; + } + + let tab2 = []; + for(let user of tab1) + { + // filtre textuelle + let valide: boolean = this.isTextFiltrationValid(user);; + + // filtre actif + if(valide) + { + if(user.isActive && this.active) valide = true; + else if((!user.isActive) && this.noActive) valide = true; + else valide = false; + } + + // filtre date + if(valide) + { + if ((user.lastConnexion === null) && (startDate !== null)) valide = false; + else if ((user.lastConnexion === null) && (endDate !== null)) valide = false; + else if (startDate !== null) + { + let timeLastConnexion = 0; + if(user.lastConnexion !== null) timeLastConnexion = (new Date(user.lastConnexion)).getTime(); + + if(startDate.getTime() > timeLastConnexion) valide = false; + else if (endDate !== null) + { + if(endDate.getTime() < timeLastConnexion) valide = false; + } + } + } + + if(valide) tab2.push(user); + } + + this.dataSource = new MatTableDataSource(tab2); + this.dataSource.sort = this.sort; + this.dataSource.paginator = this.paginator; + } + + + isTextFiltrationValid(user): boolean + { + let datePipe = new DatePipe('en-GB'); + if(user.login.includes(this.filteredText)) return true; + if(user.email.includes(this.filteredText)) return true; + const createdAt = datePipe.transform(new Date(user.createdAt), 'dd/MM/yyyy à HH:mm:ss'); + if(createdAt.includes(this.filteredText)) return true; + const lastConnexion = datePipe.transform(new Date(user.lastConnexion), 'dd/MM/yyyy à HH:mm:ss'); + if(lastConnexion.includes(this.filteredText)) return true; + + if(this.roleName === 'user') + { + const dateOfBirth = datePipe.transform(new Date(user.dateOfBirth), 'dd/MM/yyyy à HH:mm:ss'); + if(dateOfBirth.includes(this.filteredText)) return true; + if(user.age.toString().includes(this.filteredText)) return true; + if((user.sexe === 'man') && (this.filteredText === 'M')) return true; + if((user.sexe === 'woman') && (this.filteredText === 'F')) return true; + if(user.interests.toString().includes(this.filteredText)) return true; + } + else if(this.roleName === 'advertiser') + { + if(user.company.includes(this.filteredText)) return true; + } + return false; + } + + + onEffacerDate(): void { + this.campaignOne.setValue({start: null, end: null }); + this.onFilter(); + } + +} diff --git a/src/app/admin/userList/popup-create-user/popup-create-user.component.html b/src/app/admin/userList/popup-create-user/popup-create-user.component.html new file mode 100644 index 0000000..dc7ac87 --- /dev/null +++ b/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/src/app/admin/userList/popup-create-user/popup-create-user.component.scss b/src/app/admin/userList/popup-create-user/popup-create-user.component.scss new file mode 100644 index 0000000..4c8a0c6 --- /dev/null +++ b/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/src/app/admin/userList/popup-create-user/popup-create-user.component.spec.ts b/src/app/admin/userList/popup-create-user/popup-create-user.component.spec.ts new file mode 100644 index 0000000..9c57fcc --- /dev/null +++ b/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/src/app/admin/userList/popup-create-user/popup-create-user.component.ts b/src/app/admin/userList/popup-create-user/popup-create-user.component.ts new file mode 100644 index 0000000..b5714a2 --- /dev/null +++ b/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/src/app/admin/utils/navbar-admin/navbar-admin.component.html b/src/app/admin/utils/navbar-admin/navbar-admin.component.html new file mode 100644 index 0000000..a73059e --- /dev/null +++ b/src/app/admin/utils/navbar-admin/navbar-admin.component.html @@ -0,0 +1,37 @@ + diff --git a/src/app/admin/utils/navbar-admin/navbar-admin.component.scss b/src/app/admin/utils/navbar-admin/navbar-admin.component.scss new file mode 100644 index 0000000..285d629 --- /dev/null +++ b/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/src/app/admin/utils/navbar-admin/navbar-admin.component.spec.ts b/src/app/admin/utils/navbar-admin/navbar-admin.component.spec.ts new file mode 100644 index 0000000..44f2cf6 --- /dev/null +++ b/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/src/app/admin/utils/navbar-admin/navbar-admin.component.ts b/src/app/admin/utils/navbar-admin/navbar-admin.component.ts new file mode 100644 index 0000000..2474a48 --- /dev/null +++ b/src/app/admin/utils/navbar-admin/navbar-admin.component.ts @@ -0,0 +1,39 @@ +import { Component } from '@angular/core'; +import {Router} from "@angular/router"; +import {ProfilService} from "../../../utils/profil/profil.service"; +import {MessageService} from "../../../utils/message/message.service"; + +@Component({ + selector: 'app-navbar-admin', + templateUrl: './navbar-admin.component.html', + styleUrls: ['./navbar-admin.component.scss'] +}) +export class NavbarAdminComponent +{ + routes: string[] = [ + "/admin", // 0 + "/admin/userList", // 1 + "/admin/adList", // 2 + "/admin/myProfil", // 3 + ]; + + url = this.router.url; + + constructor( private router: Router, + public profilService: ProfilService, + private messageService: MessageService ) { } + + onDeconnexion(): void + { + this.messageService + .delete('user/logout') + .subscribe(retour => this.onDeconnexionCallback(retour), err => this.onDeconnexionCallback(err)); + } + + onDeconnexionCallback(retour: any): void + { + if(retour.status !== "success") console.log(retour); + this.profilService.setId(""); + } + +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts new file mode 100644 index 0000000..1dff43c --- /dev/null +++ b/src/app/app-routing.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import {PageLoginComponent} from "./beforeConnexion/login/page-login/page-login.component"; +import {PageRegisterComponent} from "./beforeConnexion/register/page-register/page-register.component"; +import {PageAdListAdminComponent} from "./admin/adList/page-ad-list-admin/page-ad-list-admin.component"; +import {PageProfilAdminComponent} from "./admin/myProfil/page-profil-admin/page-profil-admin.component"; +import {PageUserListComponent} from "./admin/userList/page-user-list/page-user-list.component"; +import {MyGuardGuard} from "./utils/my-guard/my-guard.guard"; + +const routes: Routes = [ + + // Before connexion + { path: '', component: PageLoginComponent }, + { path: 'login', component: PageLoginComponent }, + { path: 'register', component: PageRegisterComponent }, + + // Admin + { path: 'admin', component: PageUserListComponent, canActivate: [MyGuardGuard] }, + { path: 'admin/userList', component: PageUserListComponent, canActivate: [MyGuardGuard] }, + { path: 'admin/adList', component: PageAdListAdminComponent, canActivate: [MyGuardGuard] }, + { path: 'admin/myProfil', component: PageProfilAdminComponent, canActivate: [MyGuardGuard] }, +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +export class AppRoutingModule { } diff --git a/src/app/app.component.html b/src/app/app.component.html new file mode 100644 index 0000000..d5d92f3 --- /dev/null +++ b/src/app/app.component.html @@ -0,0 +1,2 @@ + + diff --git a/src/app/app.component.scss b/src/app/app.component.scss new file mode 100644 index 0000000..22a5665 --- /dev/null +++ b/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/src/app/app.component.spec.ts b/src/app/app.component.spec.ts new file mode 100644 index 0000000..1391984 --- /dev/null +++ b/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/src/app/app.component.ts b/src/app/app.component.ts new file mode 100644 index 0000000..e81341b --- /dev/null +++ b/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/src/app/app.module.ts b/src/app/app.module.ts new file mode 100644 index 0000000..92645e2 --- /dev/null +++ b/src/app/app.module.ts @@ -0,0 +1,95 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { AppRoutingModule } from './app-routing.module'; +import { AppComponent } from './app.component'; +import { PageLoginComponent } from './beforeConnexion/login/page-login/page-login.component'; +import { PopupForgottenPasswordComponent } from './beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component'; +import {BrowserAnimationsModule} from "@angular/platform-browser/animations"; +import {HttpClientModule} from "@angular/common/http"; +import {FormsModule, ReactiveFormsModule} from "@angular/forms"; +import {MatTableModule} from "@angular/material/table"; +import {MatSortModule} from "@angular/material/sort"; +import {MatRadioModule} from "@angular/material/radio"; +import {MatDialogModule} from "@angular/material/dialog"; +import {MatSnackBarModule} from "@angular/material/snack-bar"; +import {MatButtonModule} from "@angular/material/button"; +import {MatCheckboxModule} from "@angular/material/checkbox"; +import { PageRegisterComponent } from './beforeConnexion/register/page-register/page-register.component'; +import { PopupConfirmationComponent } from './beforeConnexion/register/popup-confirmation/popup-confirmation.component'; +import { InputInterestsRegisterComponent } from './beforeConnexion/register/input-interests-register/input-interests-register.component'; +import {MatFormFieldModule} from "@angular/material/form-field"; +import {MatChipsModule} from "@angular/material/chips"; +import {MatIconModule} from "@angular/material/icon"; +import {MatAutocompleteModule} from "@angular/material/autocomplete"; +import {MatStepperModule} from "@angular/material/stepper"; +import { NavbarBeforeConnexionComponent } from './beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component'; +import { PageProfilAdminComponent } from './admin/myProfil/page-profil-admin/page-profil-admin.component'; +import { PopupUpdateAdminComponent } from './admin/myProfil/popup-update-admin/popup-update-admin.component'; +import { PageAdListAdminComponent } from './admin/adList/page-ad-list-admin/page-ad-list-admin.component'; +import { PopupDeleteAdAdminComponent } from './admin/adList/popup-delete-ad-admin/popup-delete-ad-admin.component'; +import { PopupVisualizeImagesAdminComponent } from './admin/adList/popup-visualize-images-admin/popup-visualize-images-admin.component'; +import {MatDividerModule} from "@angular/material/divider"; +import {MatSelectModule} from "@angular/material/select"; +import {MatPaginatorModule} from "@angular/material/paginator"; +import {MatGridListModule} from "@angular/material/grid-list"; +import { PageUserListComponent } from './admin/userList/page-user-list/page-user-list.component'; +import { InputInterestsAdminComponent } from './admin/userList/input-interests-admin/input-interests-admin.component'; +import { PopupCreateUserComponent } from './admin/userList/popup-create-user/popup-create-user.component'; +import {MatSlideToggleModule} from "@angular/material/slide-toggle"; +import { NavbarAdminComponent } from './admin/utils/navbar-admin/navbar-admin.component'; +import {MatInputModule} from "@angular/material/input"; +import {MatDatepickerModule} from "@angular/material/datepicker"; +import {MAT_DATE_LOCALE, MatNativeDateModule} from "@angular/material/core"; + +@NgModule({ + declarations: [ + AppComponent, + PageLoginComponent, + PopupForgottenPasswordComponent, + PageRegisterComponent, + PopupConfirmationComponent, + InputInterestsRegisterComponent, + NavbarBeforeConnexionComponent, + PageProfilAdminComponent, + PopupUpdateAdminComponent, + PageAdListAdminComponent, + PopupDeleteAdAdminComponent, + PopupVisualizeImagesAdminComponent, + PageUserListComponent, + InputInterestsAdminComponent, + PopupCreateUserComponent, + NavbarAdminComponent + ], + imports: [ + BrowserModule, + AppRoutingModule, + BrowserAnimationsModule, + MatTableModule, + MatSortModule, + MatRadioModule, + FormsModule, + HttpClientModule, + MatDialogModule, + MatButtonModule, + MatCheckboxModule, + MatSnackBarModule, + MatFormFieldModule, + MatInputModule, + MatChipsModule, + MatIconModule, + MatAutocompleteModule, + ReactiveFormsModule, + MatStepperModule, + MatDividerModule, + MatSelectModule, + MatPaginatorModule, + MatGridListModule, + MatSlideToggleModule, + MatDatepickerModule, + MatNativeDateModule, + ], + providers: [{ provide: MAT_DATE_LOCALE, useValue: 'en-GB' }], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/src/app/beforeConnexion/login/page-login/page-login.component.html b/src/app/beforeConnexion/login/page-login/page-login.component.html new file mode 100644 index 0000000..675270e --- /dev/null +++ b/src/app/beforeConnexion/login/page-login/page-login.component.html @@ -0,0 +1,36 @@ +
+
+ + + +
+
+ + +
+

StreamNotFound

+ User Icon +
+ + +
+ + + +
+ {{errorMessage}} +
+ +
+ + + + +
+
+ + +
+
diff --git a/src/app/beforeConnexion/login/page-login/page-login.component.scss b/src/app/beforeConnexion/login/page-login/page-login.component.scss new file mode 100644 index 0000000..8924202 --- /dev/null +++ b/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/src/app/beforeConnexion/login/page-login/page-login.component.spec.ts b/src/app/beforeConnexion/login/page-login/page-login.component.spec.ts new file mode 100644 index 0000000..a4ee677 --- /dev/null +++ b/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/src/app/beforeConnexion/login/page-login/page-login.component.ts b/src/app/beforeConnexion/login/page-login/page-login.component.ts new file mode 100644 index 0000000..555e496 --- /dev/null +++ b/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/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.html b/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.html new file mode 100644 index 0000000..c34b58e --- /dev/null +++ b/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/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.scss b/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.scss new file mode 100644 index 0000000..fa75013 --- /dev/null +++ b/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/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.spec.ts b/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.spec.ts new file mode 100644 index 0000000..ebf101c --- /dev/null +++ b/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/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.ts b/src/app/beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component.ts new file mode 100644 index 0000000..1ff70ce --- /dev/null +++ b/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/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.html b/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.html new file mode 100644 index 0000000..2a7c484 --- /dev/null +++ b/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/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.scss b/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.spec.ts b/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.spec.ts new file mode 100644 index 0000000..9917b1a --- /dev/null +++ b/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/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.ts b/src/app/beforeConnexion/register/input-interests-register/input-interests-register.component.ts new file mode 100644 index 0000000..8dba84e --- /dev/null +++ b/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/src/app/beforeConnexion/register/page-register/page-register.component.html b/src/app/beforeConnexion/register/page-register/page-register.component.html new file mode 100644 index 0000000..fb6e3ac --- /dev/null +++ b/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/src/app/beforeConnexion/register/page-register/page-register.component.scss b/src/app/beforeConnexion/register/page-register/page-register.component.scss new file mode 100644 index 0000000..5f0dc53 --- /dev/null +++ b/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/src/app/beforeConnexion/register/page-register/page-register.component.spec.ts b/src/app/beforeConnexion/register/page-register/page-register.component.spec.ts new file mode 100644 index 0000000..5cff194 --- /dev/null +++ b/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/src/app/beforeConnexion/register/page-register/page-register.component.ts b/src/app/beforeConnexion/register/page-register/page-register.component.ts new file mode 100644 index 0000000..788c8be --- /dev/null +++ b/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/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.html b/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.html new file mode 100644 index 0000000..1cd51fe --- /dev/null +++ b/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/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.scss b/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.scss new file mode 100644 index 0000000..85730e0 --- /dev/null +++ b/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/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.spec.ts b/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.spec.ts new file mode 100644 index 0000000..d6f9908 --- /dev/null +++ b/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/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.ts b/src/app/beforeConnexion/register/popup-confirmation/popup-confirmation.component.ts new file mode 100644 index 0000000..59e3325 --- /dev/null +++ b/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/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.html b/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.html new file mode 100644 index 0000000..d4ad9f5 --- /dev/null +++ b/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.html @@ -0,0 +1,40 @@ + +
+ +
+ + + + + + +
+ +
diff --git a/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.scss b/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.scss new file mode 100644 index 0000000..e1fefaa --- /dev/null +++ b/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/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.spec.ts b/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.spec.ts new file mode 100644 index 0000000..f3f7f27 --- /dev/null +++ b/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/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.ts b/src/app/beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component.ts new file mode 100644 index 0000000..4a3f05e --- /dev/null +++ b/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/src/app/utils/message/message.service.spec.ts b/src/app/utils/message/message.service.spec.ts new file mode 100644 index 0000000..1db761b --- /dev/null +++ b/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/src/app/utils/message/message.service.ts b/src/app/utils/message/message.service.ts new file mode 100644 index 0000000..c20d5b1 --- /dev/null +++ b/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/src/app/utils/my-guard/my-guard.guard.spec.ts b/src/app/utils/my-guard/my-guard.guard.spec.ts new file mode 100644 index 0000000..e194256 --- /dev/null +++ b/src/app/utils/my-guard/my-guard.guard.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { MyGuardGuard } from './my-guard.guard'; + +describe('MyGuardGuard', () => { + let guard: MyGuardGuard; + + beforeEach(() => { + TestBed.configureTestingModule({}); + guard = TestBed.inject(MyGuardGuard); + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); +}); diff --git a/src/app/utils/my-guard/my-guard.guard.ts b/src/app/utils/my-guard/my-guard.guard.ts new file mode 100644 index 0000000..4434b3f --- /dev/null +++ b/src/app/utils/my-guard/my-guard.guard.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@angular/core'; +import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router'; +import { Observable } from 'rxjs'; +import {ProfilService} from "../profil/profil.service"; + +@Injectable({ + providedIn: 'root' +}) +export class MyGuardGuard implements CanActivate +{ + + constructor(private profilService: ProfilService, private router: Router) {} + + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean | UrlTree + { + if(this.profilService.getId() !== "") return true; + else { + this.router.navigateByUrl("login"); + return false; + } + } + +} diff --git a/src/app/utils/profil/profil.service.spec.ts b/src/app/utils/profil/profil.service.spec.ts new file mode 100644 index 0000000..5cee000 --- /dev/null +++ b/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/src/app/utils/profil/profil.service.ts b/src/app/utils/profil/profil.service.ts new file mode 100644 index 0000000..4bbe5ea --- /dev/null +++ b/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/src/app/utils/theme/theme.service.spec.ts b/src/app/utils/theme/theme.service.spec.ts new file mode 100644 index 0000000..1c2957b --- /dev/null +++ b/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/src/app/utils/theme/theme.service.ts b/src/app/utils/theme/theme.service.ts new file mode 100644 index 0000000..00768e5 --- /dev/null +++ b/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/src/assets/.gitkeep b/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/assets/darkBackground.webp b/src/assets/darkBackground.webp new file mode 100644 index 0000000..0d0692b Binary files /dev/null and b/src/assets/darkBackground.webp differ diff --git a/src/assets/lightBackground.jpg b/src/assets/lightBackground.jpg new file mode 100644 index 0000000..164cb51 Binary files /dev/null and b/src/assets/lightBackground.jpg differ diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000..93b9375 Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/assets/logo_plateforms/dailymotion.png b/src/assets/logo_plateforms/dailymotion.png new file mode 100644 index 0000000..d35ee8a Binary files /dev/null and b/src/assets/logo_plateforms/dailymotion.png differ diff --git a/src/assets/logo_plateforms/youtube.png b/src/assets/logo_plateforms/youtube.png new file mode 100644 index 0000000..5924c8d Binary files /dev/null and b/src/assets/logo_plateforms/youtube.png differ diff --git a/src/assets/play.png b/src/assets/play.png new file mode 100644 index 0000000..194f73b Binary files /dev/null and b/src/assets/play.png differ diff --git a/src/assets/profil.png b/src/assets/profil.png new file mode 100644 index 0000000..b35b2e4 Binary files /dev/null and b/src/assets/profil.png differ diff --git a/src/assets/uploadFile.png b/src/assets/uploadFile.png new file mode 100644 index 0000000..cff9f38 Binary files /dev/null and b/src/assets/uploadFile.png differ diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts new file mode 100644 index 0000000..eb1488d --- /dev/null +++ b/src/environments/environment.prod.ts @@ -0,0 +1,4 @@ +export const environment = { + production: true, + debutUrl: "https://api-polynotfound.herokuapp.com/api/" +}; diff --git a/src/environments/environment.ts b/src/environments/environment.ts new file mode 100644 index 0000000..6c4970f --- /dev/null +++ b/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/src/favicon.ico b/src/favicon.ico new file mode 100644 index 0000000..997406a Binary files /dev/null and b/src/favicon.ico differ diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..0e892e2 --- /dev/null +++ b/src/index.html @@ -0,0 +1,13 @@ + + + + + StreamNotFound + + + + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..c7b673c --- /dev/null +++ b/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/src/polyfills.ts b/src/polyfills.ts new file mode 100644 index 0000000..373f538 --- /dev/null +++ b/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/src/styles.scss b/src/styles.scss new file mode 100644 index 0000000..90d4ee0 --- /dev/null +++ b/src/styles.scss @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/src/test.ts b/src/test.ts new file mode 100644 index 0000000..b4dd603 --- /dev/null +++ b/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/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..82d91dc --- /dev/null +++ b/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/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..4a4dc62 --- /dev/null +++ b/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/tsconfig.spec.json b/tsconfig.spec.json new file mode 100644 index 0000000..092345b --- /dev/null +++ b/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" + ] +}