Update: Environment for Heroku Production
This commit is contained in:
parent
5fbdb7098e
commit
13daf10ca8
201 changed files with 34 additions and 2736 deletions
|
|
@ -0,0 +1,33 @@
|
|||
<div title="bobo" class="container" appDragAndDrop (fileDropped)="onFileDropped($event)">
|
||||
<input type="file" #fileDropRef id="fileDropRef" multiple (change)="fileBrowseHandler($event.target.files)" />
|
||||
<div style="font-weight: bold">Images</div>
|
||||
<img src="/assets/uploadFile.png" width="50" height="50" style="margin-top: 5px">
|
||||
<div style="font-style: italic; margin-top: 10px">Glisser déposer</div>
|
||||
<div style="font-style: italic">ou</div>
|
||||
<div style="font-style: italic" for="fileDropRef">Cliquer pour selectionner</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<mat-icon [title]=info_image>info</mat-icon>
|
||||
</div>
|
||||
|
||||
<div class="files-list">
|
||||
<div class="single-file" *ngFor="let file of files; let i = index">
|
||||
<img src="/assets/uploadFile.png" width="45px" alt="file">
|
||||
<div class="info">
|
||||
<h4 class="name">
|
||||
{{ file?.name }}
|
||||
</h4>
|
||||
<p class="size">
|
||||
{{ formatBytes(file?.size) }}
|
||||
</p>
|
||||
<div class="progress-cont">
|
||||
<div class="progress" [style.width]="file?.progress + '%'">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button mat-icon-button class="delete" width="20px" alt="file" (click)="deleteFile(i)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
.container {
|
||||
width: 450px;
|
||||
height: 180px;
|
||||
padding: 20px 0px 20px 0px;
|
||||
text-align: center;
|
||||
border: dashed 1px #979797;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
input {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
|
||||
.fileover {
|
||||
animation: shake 1s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.files-list {
|
||||
margin-top: 1.5rem;
|
||||
|
||||
.single-file {
|
||||
display: flex;
|
||||
padding: 0.5rem;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border: dashed 1px #979797;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
img.delete {
|
||||
margin-left: 0.5rem;
|
||||
cursor: pointer;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
||||
.name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #353f4a;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.size {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #a4a4a4;
|
||||
margin: 0;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.info {
|
||||
width: 100%
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Shake animation */
|
||||
@keyframes shake {
|
||||
0% {
|
||||
transform: translate(1px, 1px) rotate(0deg);
|
||||
}
|
||||
|
||||
10% {
|
||||
transform: translate(-1px, -2px) rotate(-1deg);
|
||||
}
|
||||
|
||||
20% {
|
||||
transform: translate(-3px, 0px) rotate(1deg);
|
||||
}
|
||||
|
||||
30% {
|
||||
transform: translate(3px, 2px) rotate(0deg);
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: translate(1px, -1px) rotate(1deg);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translate(-1px, 2px) rotate(-1deg);
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: translate(-3px, 1px) rotate(0deg);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: translate(3px, 1px) rotate(-1deg);
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: translate(-1px, -1px) rotate(1deg);
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: translate(1px, 2px) rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(1px, -2px) rotate(-1deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.progress-cont {
|
||||
height: 7px;
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
background-color: #d0d0d0;
|
||||
position: relative;
|
||||
|
||||
.progress {
|
||||
width: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 4px;
|
||||
background-color: #4c97cb;
|
||||
transition: 0.5s all;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DragAndDropComponent } from './drag-and-drop.component';
|
||||
|
||||
describe('DragAndDropComponent', () => {
|
||||
let component: DragAndDropComponent;
|
||||
let fixture: ComponentFixture<DragAndDropComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ DragAndDropComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DragAndDropComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
import {Component, ElementRef, EventEmitter, OnInit, Output, ViewChild} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-drag-and-drop',
|
||||
templateUrl: './drag-and-drop.component.html',
|
||||
styleUrls: ['./drag-and-drop.component.scss']
|
||||
})
|
||||
export class DragAndDropComponent
|
||||
{
|
||||
@ViewChild("fileDropRef", { static: false }) fileDropEl: ElementRef;
|
||||
info_image = "Vos annonces seront affichées dans un rectangle de rapport 1/5 avec: \n • 1 la largeur du rectangle \n • 5 la hauteur du rectangle" ;
|
||||
files: any[] = [];
|
||||
@Output() eventEmitter = new EventEmitter<any>();
|
||||
|
||||
/**
|
||||
* on file drop handler
|
||||
*/
|
||||
onFileDropped($event) {
|
||||
this.prepareFilesList($event);
|
||||
this.eventEmitter.emit(this.files);
|
||||
}
|
||||
|
||||
/**
|
||||
* handle file from browsing
|
||||
*/
|
||||
fileBrowseHandler(files) {
|
||||
this.prepareFilesList(files);
|
||||
this.eventEmitter.emit(this.files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete file from files list
|
||||
* @param index (File index)
|
||||
*/
|
||||
deleteFile(index: number) {
|
||||
if (this.files[index].progress < 100) {
|
||||
console.log("Upload in progress.");
|
||||
return;
|
||||
}
|
||||
this.files.splice(index, 1);
|
||||
this.eventEmitter.emit(this.files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate the upload process
|
||||
*/
|
||||
uploadFilesSimulator(index: number) {
|
||||
setTimeout(() => {
|
||||
if (index === this.files.length) {
|
||||
return;
|
||||
} else {
|
||||
const progressInterval = setInterval(() => {
|
||||
if (this.files[index].progress === 100) {
|
||||
clearInterval(progressInterval);
|
||||
this.uploadFilesSimulator(index + 1);
|
||||
} else {
|
||||
this.files[index].progress += 5;
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Files list to normal array list
|
||||
* @param files (Files List)
|
||||
*/
|
||||
prepareFilesList(files: Array<any>) {
|
||||
for (const item of files) {
|
||||
item.progress = 0;
|
||||
this.files.push(item);
|
||||
}
|
||||
this.fileDropEl.nativeElement.value = "";
|
||||
this.uploadFilesSimulator(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* format bytes
|
||||
* @param bytes (File size in bytes)
|
||||
* @param decimals (Decimals point)
|
||||
*/
|
||||
formatBytes(bytes, decimals = 2) {
|
||||
if (bytes === 0) {
|
||||
return "0 Bytes";
|
||||
}
|
||||
const k = 1024;
|
||||
const dm = decimals <= 0 ? 0 : decimals;
|
||||
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<mat-form-field class="example-chip-list" appearance="fill">
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------ -->
|
||||
|
||||
<mat-label>Sujets</mat-label>
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------ -->
|
||||
|
||||
<mat-chip-list #chipList aria-label="Fruit selection">
|
||||
|
||||
<mat-chip
|
||||
*ngFor="let interest of myInterests"
|
||||
[selectable]="selectable"
|
||||
[removable]="removable"
|
||||
(removed)="remove(interest)">
|
||||
{{interest}}
|
||||
<button matChipRemove *ngIf="removable">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
</mat-chip>
|
||||
|
||||
<input
|
||||
placeholder="Tapez un centre d'intérêt et pressez 'Entrer' pour l'inserer"
|
||||
#tagInput
|
||||
[formControl]="formControl"
|
||||
[matAutocomplete]="auto"
|
||||
[matChipInputFor]="chipList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
(matChipInputTokenEnd)="add($event)">
|
||||
|
||||
</mat-chip-list>
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------ -->
|
||||
|
||||
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
|
||||
<mat-option *ngFor="let interest of filteredInterests | async" [value]="interest">
|
||||
{{interest}}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------ -->
|
||||
|
||||
</mat-form-field>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
mat-form-field {
|
||||
width: 100%;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
mat-chip-list {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
mat-chip {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: small;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { InputInterestsAdComponent } from './input-interests-ad.component';
|
||||
|
||||
describe('BarTagsComponent', () => {
|
||||
let component: InputInterestsAdComponent;
|
||||
let fixture: ComponentFixture<InputInterestsAdComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ InputInterestsAdComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(InputInterestsAdComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
|
||||
import {COMMA, ENTER} from "@angular/cdk/keycodes";
|
||||
import {FormControl} from "@angular/forms";
|
||||
import {Observable} from "rxjs";
|
||||
import {map, startWith} from "rxjs/operators";
|
||||
import {MatChipInputEvent} from "@angular/material/chips";
|
||||
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-input-interests-ad',
|
||||
templateUrl: './input-interests-ad.component.html',
|
||||
styleUrls: ['./input-interests-ad.component.scss']
|
||||
})
|
||||
export class InputInterestsAdComponent implements OnInit
|
||||
{
|
||||
selectable = true;
|
||||
removable = true;
|
||||
separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||
formControl = new FormControl();
|
||||
filteredInterests: Observable<string[]>;
|
||||
@Input() myInterests: string[] = [];
|
||||
allInterests: string[] = [];
|
||||
@Output() eventEmitter = new EventEmitter<string[]>();
|
||||
@ViewChild('tagInput') tagInput: ElementRef<HTMLInputElement>;
|
||||
interestsNotSelected: string[] = [];
|
||||
|
||||
|
||||
constructor( private messageService: MessageService ) {}
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.filteredInterests = this.formControl.valueChanges.pipe(
|
||||
startWith(null),
|
||||
map((fruit: string | null) => fruit ? this._filter(fruit) : this.interestsNotSelected.slice()));
|
||||
|
||||
this.messageService
|
||||
.get("misc/getInterests")
|
||||
.subscribe( retour => {
|
||||
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.allInterests = [];
|
||||
for(let elt of retour.data)
|
||||
{
|
||||
this.allInterests.push(elt.interest);
|
||||
this.interestsNotSelected.push(elt.interest);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
add(event: MatChipInputEvent): void
|
||||
{
|
||||
const value = (event.value || '').trim();
|
||||
const index = this.interestsNotSelected.indexOf(value);
|
||||
if (value && (index !== -1) && (!this.myInterests.includes(value)))
|
||||
{
|
||||
this.myInterests.push(value);
|
||||
event.chipInput!.clear();
|
||||
this.formControl.setValue(null);
|
||||
this.eventEmitter.emit(this.myInterests);
|
||||
this.interestsNotSelected.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
remove(interest: string): void
|
||||
{
|
||||
// supprimer 'interest' de 'myInterest'
|
||||
const index = this.myInterests.indexOf(interest);
|
||||
if (index >= 0) this.myInterests.splice(index, 1);
|
||||
this.eventEmitter.emit(this.myInterests);
|
||||
|
||||
// remmettre 'interest' dans 'interestsNotSelected'
|
||||
if(!this.interestsNotSelected.includes(interest))
|
||||
{
|
||||
const indexOfAutres = this.interestsNotSelected.indexOf("Autres");
|
||||
if(indexOfAutres !== -1)
|
||||
{
|
||||
this.interestsNotSelected.splice(indexOfAutres, 1);
|
||||
if(interest !== "Autres") this.interestsNotSelected.push(interest);
|
||||
this.interestsNotSelected.sort();
|
||||
this.interestsNotSelected.push("Autres");
|
||||
}
|
||||
else {
|
||||
this.interestsNotSelected.push(interest);
|
||||
if(interest !== "Autres") this.interestsNotSelected.sort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
selected(event: MatAutocompleteSelectedEvent): void
|
||||
{
|
||||
const value = event.option.viewValue;
|
||||
if(!this.myInterests.includes(value))
|
||||
{
|
||||
this.myInterests.push(value);
|
||||
const index = this.interestsNotSelected.indexOf(value);
|
||||
this.interestsNotSelected.splice(index, 1);
|
||||
}
|
||||
this.tagInput.nativeElement.value = '';
|
||||
this.formControl.setValue(null);
|
||||
this.eventEmitter.emit(this.myInterests);
|
||||
}
|
||||
|
||||
|
||||
private _filter(value: string): string[]
|
||||
{
|
||||
const filterValue = value.toLowerCase();
|
||||
return this.interestsNotSelected.filter(fruit => fruit.toLowerCase().includes(filterValue));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
|
||||
<!-- Navbar -->
|
||||
<app-navbar-advertiser></app-navbar-advertiser><br><br>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- filtre + btnAddUser -->
|
||||
<div class="row" style="margin: 20px 3% 20px 3%">
|
||||
|
||||
<!-- filtre -->
|
||||
<div class="col-10" style="padding: 0px 0px 0px 0px;">
|
||||
<div class="filtersContainer mat-elevation-z8">
|
||||
|
||||
<!-- titre -->
|
||||
<div style="font-weight: bold; margin-bottom: 10px;">
|
||||
Filtre
|
||||
</div>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<!-- filtre textuelle-->
|
||||
<div style="margin: 10px 0px 20px 2%;">
|
||||
<input class="textFilter" (keyup)="applyFilter($event)" placeholder="filtre...">
|
||||
</div>
|
||||
|
||||
<!-- role + actif + période -->
|
||||
<div class="row myRow">
|
||||
|
||||
|
||||
<!-- visible -->
|
||||
<div class="col-2">
|
||||
<mat-checkbox [(ngModel)]="visible" (change)="onFilter()">visible</mat-checkbox><br>
|
||||
<mat-checkbox [(ngModel)]="noVisible" (change)="onFilter()">non visible</mat-checkbox>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- sujets -->
|
||||
<div class="col-4">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Sujets</mat-label>
|
||||
<mat-select [formControl]="formControlInterests" multiple>
|
||||
<mat-select-trigger>
|
||||
{{formControlInterests.value ? formControlInterests.value[0] : ''}}
|
||||
<span *ngIf="formControlInterests.value?.length > 1">
|
||||
(+{{formControlInterests.value.length - 1}} {{formControlInterests.value?.length === 2 ? 'autre' : 'autres'}})
|
||||
</span>
|
||||
</mat-select-trigger>
|
||||
<mat-option *ngFor="let topping of allInterests" [value]="topping">{{topping}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<button mat-icon-button (click)="onFilter()">
|
||||
<mat-icon>keyboard_tab</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- période -->
|
||||
<div class="col-6" style="text-align: right;">
|
||||
Période de création:
|
||||
<mat-form-field appearance="fill" style="width: 140px;">
|
||||
<mat-label>Date de début</mat-label>
|
||||
<input matInput type="date"
|
||||
style="font-size: small; width: 140px;"
|
||||
[ngModel] ="startDate | date:'yyyy-MM-dd'"
|
||||
(ngModelChange)="onNewStartDate($event); onFilter();">
|
||||
</mat-form-field>
|
||||
-
|
||||
<mat-form-field appearance="fill" style="width: 140px;">
|
||||
<mat-label>Date de fin</mat-label>
|
||||
<input matInput type="date"
|
||||
style="font-size: small; width: 140px;"
|
||||
[ngModel] ="endDate | date:'yyyy-MM-dd'"
|
||||
(ngModelChange)="onNewEndDate($event); onFilter();">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- btnAdd -->
|
||||
<div class="col-2" style="text-align: right; position: relative;">
|
||||
<button mat-button class="btnAjouter" (click)="onAdd()" style="position: absolute; bottom: 0; right: 0;">
|
||||
<mat-icon>add_circle</mat-icon> Ajouter une annonce
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Table -->
|
||||
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
|
||||
|
||||
<!-- IsActive Column -->
|
||||
<ng-container matColumnDef="isVisible">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>
|
||||
<mat-icon>power_settings_new</mat-icon>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let advert">
|
||||
<mat-slide-toggle [(ngModel)]="advert.isVisible" (click)="onSliderIsVisible(advert)"></mat-slide-toggle>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Title Column -->
|
||||
<ng-container matColumnDef="title">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Titre </th>
|
||||
<td mat-cell *matCellDef="let advert">
|
||||
{{advert.title}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Subject Column -->
|
||||
<ng-container matColumnDef="interests">
|
||||
<th mat-header-cell *matHeaderCellDef> Sujets </th>
|
||||
<td mat-cell *matCellDef="let advert">
|
||||
<span *ngFor="let interest of advert.interests; let isLast = last;">
|
||||
<span *ngIf="!isLast"> {{interest}}, </span>
|
||||
<span *ngIf="isLast"> {{interest}} </span>
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- CreatedAt Column -->
|
||||
<ng-container matColumnDef="createdAt">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Date de création </th>
|
||||
<td mat-cell *matCellDef="let advert">
|
||||
{{ advert.createdAt | date:'dd/LL/YYYY à HH:mm:ss' }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- LastUpdate Column -->
|
||||
<ng-container matColumnDef="updatedAt">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Dernière modification </th>
|
||||
<td mat-cell *matCellDef="let advert">
|
||||
{{ advert.updatedAt | date:'dd/LL/YYYY à HH:mm:ss' }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Views Column -->
|
||||
<ng-container matColumnDef="countViews">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Vues </th>
|
||||
<td mat-cell *matCellDef="let advert">
|
||||
{{advert.countViews}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Actions Column -->
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<td mat-cell *matCellDef="let advert">
|
||||
<button mat-icon-button (click)="onVisualizeImages(advert)">
|
||||
<mat-icon> insert_photo</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="onUpdate(advert)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button (click)="onDelete(advert)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Directives -->
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
<tr class="mat-row" *matNoDataRow>
|
||||
<td class="mat-cell" colspan="4"> Aucune vidéo ne correspond au filtre: "{{input.value}}" </td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<div style="width: 94%; margin: auto auto">
|
||||
<mat-paginator [pageSizeOptions]="[10, 20, 50, 100]" showFirstLastButtons aria-label="Select page of periodic elements"></mat-paginator>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
.myContainer {
|
||||
max-width: 100vw;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
|
||||
.filtersContainer {
|
||||
width: 95%;
|
||||
background-color: white;
|
||||
padding: 10px 10px 10px 10px;
|
||||
}
|
||||
|
||||
.myRow {
|
||||
margin-left: 1%;
|
||||
}
|
||||
|
||||
.textFilter {
|
||||
width: 50%;
|
||||
font-size: medium;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.btnAjouter {
|
||||
background-color: white;
|
||||
border: solid 1px black;
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
||||
|
||||
table {
|
||||
margin: 0 auto;
|
||||
width: 94%;
|
||||
font-size: small;
|
||||
}
|
||||
.darkTheme table { border: solid 2px white; }
|
||||
|
||||
th.mat-sort-header-sorted {
|
||||
color: black;
|
||||
}
|
||||
|
||||
td {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 30%;
|
||||
font-size: large;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
|
||||
// rong gauche
|
||||
::ng-deep .mat-slide-toggle-thumb {
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
// trait droite
|
||||
::ng-deep .mat-slide-toggle-bar {
|
||||
background-color: gray !important;
|
||||
}
|
||||
|
||||
// rond droite
|
||||
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-thumb {
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
// trait gauche
|
||||
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-bar {
|
||||
background-color: cornflowerblue !important;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
::ng-deep .mat-pseudo-checkbox-checked {
|
||||
background-color: black !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PageAdListAdvertiserComponent } from './page-ad-list-advertiser.component';
|
||||
|
||||
describe('PageAdvertiserComponent', () => {
|
||||
let component: PageAdListAdvertiserComponent;
|
||||
let fixture: ComponentFixture<PageAdListAdvertiserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PageAdListAdvertiserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PageAdListAdvertiserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
|
||||
import {MatSort} from "@angular/material/sort";
|
||||
import {MatTableDataSource} from "@angular/material/table";
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {PopupAddOrUpdateAdComponent} from "../popup-add-or-update-ad/popup-add-or-update-ad.component";
|
||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||
import {PopupDeleteAdAdvertiserComponent} from "../popup-delete-ad-advertiser/popup-delete-ad-advertiser.component";
|
||||
import {MatPaginator} from "@angular/material/paginator";
|
||||
import {PopupVisualizeImagesAdvertiserComponent} from "../popup-visualize-images-advertiser/popup-visualize-images-advertiser.component";
|
||||
import {FormControl} from "@angular/forms";
|
||||
import {HttpParams} from "@angular/common/http";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-ad-list-advertiser',
|
||||
templateUrl: './page-ad-list-advertiser.component.html',
|
||||
styleUrls: ['./page-ad-list-advertiser.component.scss']
|
||||
})
|
||||
export class PageAdListAdvertiserComponent implements AfterViewInit
|
||||
{
|
||||
displayedColumns: string[] = [ 'isVisible', 'title', 'interests', 'createdAt', 'updatedAt', 'countViews', 'actions' ];
|
||||
tabAdvertWithCountViews: any[] = [];
|
||||
dataSource;
|
||||
@ViewChild(MatSort) sort: MatSort;
|
||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||
|
||||
visible: boolean = true;
|
||||
noVisible: boolean = true;
|
||||
startDate: Date = null;
|
||||
endDate: Date = null;
|
||||
formControlInterests = new FormControl();
|
||||
|
||||
allVideoCategorie = [];
|
||||
allInterests: string[] = [];
|
||||
|
||||
|
||||
constructor( public themeService: ThemeService,
|
||||
public dialog: MatDialog,
|
||||
private snackBar: MatSnackBar,
|
||||
private messageService: MessageService ) { }
|
||||
|
||||
|
||||
ngAfterViewInit(): void
|
||||
{
|
||||
// Ask interests
|
||||
this.messageService
|
||||
.get("misc/getInterests")
|
||||
.subscribe(ret => this.afterReceivingInterests(ret), err => this.afterReceivingInterests(err) );
|
||||
|
||||
// Ask ads
|
||||
let params = new HttpParams();
|
||||
params = params.append("isActive", true);
|
||||
this.messageService
|
||||
.get("ad/findAll", params)
|
||||
.subscribe(ret => this.afterReceivingAds(ret), err => this.afterReceivingAds(err));
|
||||
}
|
||||
|
||||
|
||||
afterReceivingInterests(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log("afterReceivingInterests");
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.allVideoCategorie = retour.data;
|
||||
this.allInterests = retour.data.map(x => x.interest);
|
||||
this.allInterests.sort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
afterReceivingAds(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
if(retour.data.length !== 0)
|
||||
{
|
||||
for(let advert of retour.data) this.tabAdvertWithCountViews.push(this.advertToAdvertWithCountViews(advert));
|
||||
this.dataSource = new MatTableDataSource<any>();
|
||||
this.onFilter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
applyFilter(event: Event): void
|
||||
{
|
||||
const filterValue = (event.target as HTMLInputElement).value;
|
||||
this.dataSource.filter = filterValue.trim().toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
onVisualizeImages(advert: any)
|
||||
{
|
||||
if(advert.images.length !== 0)
|
||||
{
|
||||
const config = {
|
||||
width: '30%',
|
||||
height: '90%',
|
||||
data: { images: advert.images }
|
||||
};
|
||||
this.dialog
|
||||
.open(PopupVisualizeImagesAdvertiserComponent, config)
|
||||
.afterClosed()
|
||||
.subscribe(retour => {});
|
||||
}
|
||||
else {
|
||||
const config = { duration: 2000, panelClass: "custom-class" };
|
||||
const message = "Cette annonce ne contient aucune image" ;
|
||||
this.snackBar.open( message, "", config);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onAdd(): void
|
||||
{
|
||||
const config = {
|
||||
width: '75%',
|
||||
//height: '80%',
|
||||
panelClass: 'custom-dialog-container',
|
||||
data: {
|
||||
action: "add",
|
||||
advert: null,
|
||||
allVideoCategorie: this.allVideoCategorie,
|
||||
allTitle: this.tabAdvertWithCountViews.map(x => x.title)
|
||||
}
|
||||
};
|
||||
this.dialog
|
||||
.open(PopupAddOrUpdateAdComponent, config)
|
||||
.afterClosed()
|
||||
.subscribe( advertAdded => {
|
||||
|
||||
const config = { duration: 1000, panelClass: "custom-class" };
|
||||
let message = "" ;
|
||||
if((advertAdded === undefined) || (advertAdded === null)) {
|
||||
message = "Opération annulée" ;
|
||||
}
|
||||
else {
|
||||
this.tabAdvertWithCountViews.push(this.advertToAdvertWithCountViews(advertAdded));
|
||||
this.onFilter();
|
||||
message = "L'annonce a bien été ajoutée ✔" ;
|
||||
}
|
||||
this.snackBar.open( message, "", config);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
onUpdate(advertToUpdate: any): void
|
||||
{
|
||||
const config = {
|
||||
width: '75%',
|
||||
//height: '80%',
|
||||
panelClass: 'custom-dialog-container',
|
||||
data: {
|
||||
action: "update",
|
||||
advert: advertToUpdate,
|
||||
allVideoCategorie: this.allVideoCategorie,
|
||||
allTitle: this.tabAdvertWithCountViews.map(x => x.title)
|
||||
}
|
||||
};
|
||||
this.dialog
|
||||
.open(PopupAddOrUpdateAdComponent, config)
|
||||
.afterClosed()
|
||||
.subscribe( advertUpdated => {
|
||||
|
||||
const config = { duration: 1000, panelClass: "custom-class" };
|
||||
let message = "" ;
|
||||
if((advertUpdated === undefined) || (advertUpdated === null)) {
|
||||
message = "Opération annulée" ;
|
||||
}
|
||||
else {
|
||||
const index = this.tabAdvertWithCountViews.findIndex(elt => (elt.id === advertToUpdate.id));
|
||||
this.tabAdvertWithCountViews.splice(index, 1, this.advertToAdvertWithCountViews(advertUpdated));
|
||||
this.onFilter();
|
||||
message = "L'annonce a bien été modifiée ✔" ;
|
||||
}
|
||||
this.snackBar.open( message, "", config);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
onDelete(advert: any): void
|
||||
{
|
||||
const config = {
|
||||
data: { advert: advert }
|
||||
};
|
||||
this.dialog
|
||||
.open(PopupDeleteAdAdvertiserComponent, config)
|
||||
.afterClosed()
|
||||
.subscribe( retour => {
|
||||
|
||||
const config = { duration: 1000, panelClass: "custom-class" };
|
||||
let message = "" ;
|
||||
if((retour === undefined) || (retour === null)) {
|
||||
message = "Opération annulée" ;
|
||||
}
|
||||
else {
|
||||
const index = this.dataSource.data.findIndex( elt => (elt.id === advert.id));
|
||||
this.dataSource.data.splice(index, 1);
|
||||
this.dataSource.data = this.dataSource.data;
|
||||
this.dataSource = this.dataSource;
|
||||
message = advert.title + " a bien été supprimée ✔" ;
|
||||
}
|
||||
this.snackBar.open( message, "", config);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
onFilter(): void
|
||||
{
|
||||
if(this.dataSource === null || this.dataSource === undefined) this.dataSource = new MatTableDataSource();
|
||||
this.dataSource.data = [];
|
||||
for(let advert of this.tabAdvertWithCountViews)
|
||||
{
|
||||
let valide: boolean = true;
|
||||
|
||||
if(advert.isVisible && this.visible) valide = true;
|
||||
else if((!advert.isVisible) && this.noVisible) valide = true;
|
||||
else valide = false;
|
||||
|
||||
if(valide)
|
||||
{
|
||||
if ((advert.createdAt === null) && (this.startDate !== null)) valide = false;
|
||||
else if ((advert.createdAt === null) && (this.endDate !== null)) valide = false;
|
||||
else if (this.startDate !== null)
|
||||
{
|
||||
if(this.startDate.getTime() > advert.createdAt.getTime()) valide = false;
|
||||
else if (this.endDate !== null)
|
||||
{
|
||||
if(this.endDate.getTime() < advert.createdAt.getTime()) valide = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(valide) {
|
||||
if(this.formControlInterests.value !== null) {
|
||||
for (let interest of this.formControlInterests.value) {
|
||||
if (advert.interests.indexOf(interest) === -1) {
|
||||
valide = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(valide) this.dataSource.data.push(advert);
|
||||
}
|
||||
|
||||
this.dataSource = new MatTableDataSource(this.dataSource.data);
|
||||
this.dataSource.sort = this.sort;
|
||||
this.dataSource.paginator = this.paginator;
|
||||
}
|
||||
|
||||
|
||||
onNewStartDate(event): void {
|
||||
this.startDate = new Date(event);
|
||||
}
|
||||
|
||||
onNewEndDate(event): void {
|
||||
this.endDate = new Date(event);
|
||||
}
|
||||
|
||||
|
||||
onSliderIsVisible(advert: any): void
|
||||
{
|
||||
// il faut envoyer la négation de user.isActive
|
||||
this.messageService
|
||||
.put("ad/update/"+advert.id, { isVisible: !advert.isVisible })
|
||||
.subscribe(
|
||||
ret => {},
|
||||
err => {
|
||||
console.log("onSliderIsVisible");
|
||||
console.log(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
advertToAdvertWithCountViews(advert)
|
||||
{
|
||||
return {
|
||||
id: advert.id,
|
||||
userId: advert.userId,
|
||||
title: advert.title,
|
||||
url: advert.url,
|
||||
images: advert.images,
|
||||
interests: advert.interests.map(x => x.interest),
|
||||
comment: advert.comment,
|
||||
views: advert.views,
|
||||
countViews: advert.views.length,
|
||||
isVisible: advert.isVisible,
|
||||
isActive: advert.isActive,
|
||||
createdAt: advert.createdAt,
|
||||
updatedAt: advert.updatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<div class="myContainer1">
|
||||
<div class="myContainer2">
|
||||
|
||||
<!-- titre popup -->
|
||||
<h1 mat-dialog-title>{{title}}</h1>
|
||||
|
||||
|
||||
<mat-divider></mat-divider><br>
|
||||
|
||||
|
||||
<!-- tous les champs -->
|
||||
<div class="row">
|
||||
|
||||
<!-- title + interests + comments + isVisible -->
|
||||
<div class="col-6">
|
||||
|
||||
<!-- Title -->
|
||||
<mat-form-field class="titleContainer" appearance="fill">
|
||||
<mat-label> Titre annonce </mat-label>
|
||||
<input matInput type="text" [(ngModel)]="advert.title" required>
|
||||
</mat-form-field>
|
||||
|
||||
<!-- Interests -->
|
||||
<app-input-interests-ad [myInterests]="advert.interests" (eventEmitter)="onEventInputTags($event)"></app-input-interests-ad>
|
||||
|
||||
<!-- Comments -->
|
||||
<mat-form-field class="commentContainer" appearance="fill">
|
||||
<mat-label> Commentaire </mat-label>
|
||||
<textarea matInput [(ngModel)]="advert.comment" rows="5" style="resize: none;"></textarea>
|
||||
</mat-form-field><br>
|
||||
|
||||
<!-- url -->
|
||||
<mat-form-field class="commentContainer" appearance="fill">
|
||||
<mat-label> URL </mat-label>
|
||||
<input matInput [(ngModel)]="advert.url">
|
||||
</mat-form-field><br>
|
||||
|
||||
<!-- IsVisible -->
|
||||
<mat-checkbox [(ngModel)]="advert.isVisible"> Visible </mat-checkbox><br><br>
|
||||
|
||||
<!-- Images déjà présentes -->
|
||||
<div *ngIf="advert.images.length !== 0">
|
||||
<div style="font-weight: bold; margin-bottom: 5px;">
|
||||
Images déjà associées:
|
||||
</div>
|
||||
<div style="margin-left: 20px; padding-left: 2px; border-left: solid 1px #a4a4a4">
|
||||
<div *ngFor="let image of advert.images" style="padding: 2px 0px 2px 0px;">
|
||||
<mat-chip [selectable]="true" [removable]="true" style="font-size: small;">
|
||||
{{image.description}}
|
||||
<button matChipRemove (click)="onRemoveImgAlreadyPresent(image)">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
</mat-chip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- nouvelles images -->
|
||||
<div class="col-6" style="overflow-x: hidden; overflow-y: scroll; max-height: 70vh;">
|
||||
<app-drag-and-drop (eventEmitter)="onReceiveNewImages($event)"></app-drag-and-drop>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<br><mat-divider></mat-divider>
|
||||
|
||||
<!-- message d'erreur -->
|
||||
<div *ngIf="hasError" style="text-align: center; margin-bottom: 20px;">
|
||||
<span class="mat-error">{{errorMessage}}</span>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="dialogRef.close()">Annuler</button>
|
||||
<button mat-button (click)="onValidate()">Valider</button>
|
||||
</mat-dialog-actions>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
.myContainer1 {
|
||||
padding: 10px 10px 0px 25px;
|
||||
margin: 0px 0px 0px 0px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
|
||||
.myContainer2 {
|
||||
padding: 0px 0px 0px 0px;
|
||||
margin: 0px 0px 0px 0px;
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.myContainer2::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.col-6, .col-8 {
|
||||
border-left: solid 1px #a4a4a4;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
.titleContainer {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.commentContainer {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.imageContainer {
|
||||
border: solid 1px grey;
|
||||
}
|
||||
|
||||
mat-dialog-actions {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// --- LightTheme ---
|
||||
|
||||
// aura
|
||||
.lightTheme ::ng-deep .mat-checkbox-ripple .mat-ripple-element {
|
||||
background-color: grey !important;
|
||||
}
|
||||
|
||||
// contenu coche
|
||||
.lightTheme ::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||
background-color: black !important;
|
||||
}
|
||||
|
||||
// indeterminate
|
||||
.lightTheme ::ng-deep .mat-checkbox .mat-checkbox-frame {
|
||||
border-color: black !important;
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
// --- DarkTheme ---
|
||||
|
||||
// aura
|
||||
.darTheme ::ng-deep .mat-checkbox-ripple .mat-ripple-element {
|
||||
background-color: grey !important;
|
||||
}
|
||||
|
||||
// contenu coche
|
||||
.darkTheme ::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||
background-color: black !important;
|
||||
}
|
||||
|
||||
// indeterminate
|
||||
.darkTheme ::ng-deep .mat-checkbox .mat-checkbox-frame {
|
||||
border-color: white !important;
|
||||
//background-color: white !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupAddOrUpdateAdComponent } from './popup-add-or-update-ad.component';
|
||||
|
||||
describe('PopupAddOrUpdateAdComponent', () => {
|
||||
let component: PopupAddOrUpdateAdComponent;
|
||||
let fixture: ComponentFixture<PopupAddOrUpdateAdComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupAddOrUpdateAdComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupAddOrUpdateAdComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
|
||||
|
||||
|
||||
|
||||
const ADVERT_VIDE = {
|
||||
_id: "",
|
||||
userId: "",
|
||||
title: "",
|
||||
url: "",
|
||||
images: [],
|
||||
interests: [],
|
||||
comment: "",
|
||||
views: [],
|
||||
isVisible: true,
|
||||
isActive: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-add-or-update-ad',
|
||||
templateUrl: './popup-add-or-update-ad.component.html',
|
||||
styleUrls: ['./popup-add-or-update-ad.component.scss']
|
||||
})
|
||||
export class PopupAddOrUpdateAdComponent implements OnInit
|
||||
{
|
||||
advert: any;
|
||||
title: string = "" ;
|
||||
allVideoCategorie = [];
|
||||
allTitle = [];
|
||||
|
||||
tabOfNewImagesBase64 = [];
|
||||
tabOfNewImagesName = [];
|
||||
|
||||
hasError: boolean = false;
|
||||
errorMessage: string = "" ;
|
||||
|
||||
|
||||
|
||||
constructor( public dialogRef: MatDialogRef<PopupAddOrUpdateAdComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data,
|
||||
private messageService: MessageService,
|
||||
public themeService: ThemeService ) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.allVideoCategorie = this.data.allVideoCategorie;
|
||||
this.allTitle = this.data.allTitle.slice();
|
||||
if(this.data.action === "add")
|
||||
{
|
||||
this.advert = Object.assign({}, ADVERT_VIDE);
|
||||
this.advert.images = [];
|
||||
this.advert.interests = [];
|
||||
this.title = "Ajouter annonce" ;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.advert = Object.assign({}, this.data.advert);
|
||||
this.advert.interests = this.data.advert.interests.slice();
|
||||
this.title = "Modifier annonce" ;
|
||||
const indexOldTitle = this.allTitle.findIndex(title => title == this.advert.title);
|
||||
this.allTitle.splice(indexOldTitle, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onValidate(): void
|
||||
{
|
||||
this.checkField();
|
||||
if(!this.hasError)
|
||||
{
|
||||
// preparation des donnees
|
||||
this.prepareAdvertInterests();
|
||||
this.prepareAdvertImages();
|
||||
|
||||
// si creation
|
||||
if (this.data.action === "add")
|
||||
{
|
||||
this.messageService
|
||||
.post("ad/create", this.advert)
|
||||
.subscribe(ret => this.onCreateCallback(ret), err => this.onCreateCallback(err));
|
||||
}
|
||||
// si update
|
||||
else
|
||||
{
|
||||
const id = this.advert.id;
|
||||
Reflect.deleteProperty(this.advert, "id");
|
||||
Reflect.deleteProperty(this.advert, "_id");
|
||||
this.messageService
|
||||
.put("ad/update/" + id, this.advert)
|
||||
.subscribe(ret => this.onUpdateCallback(ret, id), err => this.onUpdateCallback(err, id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
checkField()
|
||||
{
|
||||
if(this.advert.title.length === 0) {
|
||||
this.errorMessage = "Veuillez remplir le champ 'titre'." ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(this.allTitle.includes(this.advert.title)) {
|
||||
this.errorMessage = "Ce titre est déjà pris." ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if((this.advert.images.length === 0) && (this.tabOfNewImagesName.length === 0)) {
|
||||
this.errorMessage = "Veuillez uploader au moins une image." ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else {
|
||||
this.errorMessage = "";
|
||||
this.hasError = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
onCreateCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.dialogRef.close();
|
||||
}
|
||||
else {
|
||||
this.dialogRef.close(retour.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onUpdateCallback(retour: any, id: string): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.dialogRef.close();
|
||||
}
|
||||
else {
|
||||
this.advert.id = id;
|
||||
this.dialogRef.close(this.advert);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onEventInputTags(myTags: string[]): void
|
||||
{
|
||||
this.advert.interests = myTags;
|
||||
}
|
||||
|
||||
|
||||
onRemoveImgAlreadyPresent(image)
|
||||
{
|
||||
const index = this.advert.images.indexOf(image);
|
||||
this.advert.images.splice(index, 1);
|
||||
}
|
||||
|
||||
|
||||
onReceiveNewImages(files: any): void
|
||||
{
|
||||
this.tabOfNewImagesBase64 = [];
|
||||
this.tabOfNewImagesName = [];
|
||||
if(files)
|
||||
{
|
||||
for(let file of files)
|
||||
{
|
||||
if(file)
|
||||
{
|
||||
const reader = new FileReader();
|
||||
reader.onload = this.handleReaderLoaded.bind(this);
|
||||
this.tabOfNewImagesName.push(file.name)
|
||||
reader.readAsBinaryString(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handleReaderLoaded(e)
|
||||
{
|
||||
this.tabOfNewImagesBase64.push('data:image/png;base64,' + btoa(e.target.result))
|
||||
}
|
||||
|
||||
|
||||
// Met bien en forme les "images" avant d'être envoyer
|
||||
prepareAdvertImages(): void
|
||||
{
|
||||
for(let i=0; i<this.tabOfNewImagesName.length ; i++)
|
||||
{
|
||||
let newImagePrepared = {
|
||||
base64: this.tabOfNewImagesBase64[i],
|
||||
url: "",
|
||||
description: this.tabOfNewImagesName[i],
|
||||
type: 0,
|
||||
};
|
||||
this.advert.images.push(newImagePrepared);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Met bien en forme les "interests" avant d'être envoyer
|
||||
prepareAdvertInterests(): void
|
||||
{
|
||||
let interests = [];
|
||||
|
||||
for (let interest of this.advert.interests) {
|
||||
for (let videoCategorie of this.allVideoCategorie) {
|
||||
if (videoCategorie.interest === interest) {
|
||||
interests.push(videoCategorie);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.advert.interests = interests;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<mat-dialog-content class="mat-typography">
|
||||
Êtes-vous sûr de vouloir supprimer l'annonce <i>{{advert.title}}</i> ?
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="dialogRef.close();">Annuler</button>
|
||||
<button mat-button (click)="onValidate()" cdkFocusInitial>Valider</button>
|
||||
</mat-dialog-actions>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupDeleteAdAdvertiserComponent } from './popup-delete-ad-advertiser.component';
|
||||
|
||||
describe('PopupDeleteAdComponent', () => {
|
||||
let component: PopupDeleteAdAdvertiserComponent;
|
||||
let fixture: ComponentFixture<PopupDeleteAdAdvertiserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupDeleteAdAdvertiserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupDeleteAdAdvertiserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-delete-ad-advertiser',
|
||||
templateUrl: './popup-delete-ad-advertiser.component.html',
|
||||
styleUrls: ['./popup-delete-ad-advertiser.component.scss']
|
||||
})
|
||||
export class PopupDeleteAdAdvertiserComponent implements OnInit
|
||||
{
|
||||
advert: any;
|
||||
|
||||
|
||||
constructor( public dialogRef: MatDialogRef<PopupDeleteAdAdvertiserComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data,
|
||||
private messageService: MessageService) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.advert = this.data.advert;
|
||||
}
|
||||
|
||||
|
||||
onValidate(): void
|
||||
{
|
||||
this.messageService
|
||||
.delete("ad/delete/"+this.advert.id)
|
||||
.subscribe(ret => this.onValidateCallback(ret), err => this.onValidateCallback(err));
|
||||
}
|
||||
|
||||
|
||||
onValidateCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.dialogRef.close();
|
||||
}
|
||||
else {
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
|
||||
<h1 mat-dialog-title>{{advert.title}}</h1>
|
||||
|
||||
<!-- ----------------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
<mat-dialog-content>
|
||||
|
||||
<!-- Images -->
|
||||
<div class="row myRow">
|
||||
<div class="col-6 myLabel"> Images: </div>
|
||||
<div class="col-6 myValue" style="border-left: solid 1px #e6e6e6">
|
||||
<div *ngFor="let image of advert.images"> {{image.url}} </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tags -->
|
||||
<div class="row myRow">
|
||||
<div class="col-6 myLabel"> Centre d'intérêt :</div>
|
||||
<div class="col-6 myValue" style="border-left: solid 1px #e6e6e6">
|
||||
<div *ngFor="let tag of advert.interests"> • {{tag}} </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Comment -->
|
||||
<div class="row myRow">
|
||||
<div class="col-6 myLabel"> Commentaire: </div>
|
||||
<div class="col-6 myValue"> {{advert.comment}} </div>
|
||||
</div>
|
||||
|
||||
<!-- Views -->
|
||||
<div class="row myRow">
|
||||
<label class="col-6 myLabel"> Vues: </label>
|
||||
<div class="col-6 myValue"> {{advert.views}} </div>
|
||||
</div>
|
||||
|
||||
<!-- Created at -->
|
||||
<div class="row myRow">
|
||||
<label class="col-6 myLabel"> Date de création: </label>
|
||||
<div class="col-6 myValue">
|
||||
{{ advert.createdAt | date:'dd/LL/YYYY à HH:mm:ss' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Last updtade -->
|
||||
<div class="row myRow">
|
||||
<label class="col-6 myLabel"> Date de dernière modification: </label>
|
||||
<div class="col-6 myValue">
|
||||
{{ advert.updatedAt | date:'dd/LL/YYYY à HH:mm:ss' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- IsVisible -->
|
||||
<div class="row myRow">
|
||||
<label class="col-6 myLabel"> Visibilité: </label>
|
||||
<div class="col-6 myValue">
|
||||
<mat-icon *ngIf="advert.isVisible">checked</mat-icon>
|
||||
<mat-icon *ngIf="!advert.isVisible">close</mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</mat-dialog-content>
|
||||
|
||||
<!-- ----------------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="dialogRef.close()">Fermer</button>
|
||||
</mat-dialog-actions>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
.lightTheme, .darkTheme {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: xx-large;
|
||||
}
|
||||
|
||||
|
||||
.myRow {
|
||||
margin: 15px 0px 15px 0px;
|
||||
}
|
||||
|
||||
|
||||
.myLabel {
|
||||
text-align: right;
|
||||
padding: 0px 5px 0px 0px;
|
||||
margin: 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.myValue {
|
||||
text-align: left;
|
||||
padding: 0px 0px 0px 5px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupVisualizeAdAdvertiserComponent } from './popup-visualize-ad-advertiser.component';
|
||||
|
||||
describe('PopupVisualizeAdComponent', () => {
|
||||
let component: PopupVisualizeAdAdvertiserComponent;
|
||||
let fixture: ComponentFixture<PopupVisualizeAdAdvertiserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupVisualizeAdAdvertiserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupVisualizeAdAdvertiserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from "@angular/material/dialog";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-visualize-ad-advertiser',
|
||||
templateUrl: './popup-visualize-ad-advertiser.component.html',
|
||||
styleUrls: ['./popup-visualize-ad-advertiser.component.scss']
|
||||
})
|
||||
export class PopupVisualizeAdAdvertiserComponent implements OnInit
|
||||
{
|
||||
advert: any;
|
||||
|
||||
constructor( public dialogRef: MatDialogRef<PopupVisualizeAdAdvertiserComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data,
|
||||
public themeService: ThemeService,
|
||||
public dialog: MatDialog ) { }
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.advert = this.data.advert;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<div mat-dialog-title class="dialog-title">
|
||||
<h2></h2>
|
||||
<button mat-icon-button aria-label="close dialog" mat-dialog-close>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<mat-grid-list cols="12" rowHeight="500">
|
||||
<mat-grid-tile colspan="1" rowspan="1" (click)="onPrecedent()">
|
||||
<button> < </button>
|
||||
</mat-grid-tile>
|
||||
<mat-grid-tile colspan="10" rowspan="1">
|
||||
<img [src]="tabImages[index].base64" [alt]="tabImages[index].description">
|
||||
</mat-grid-tile>
|
||||
<mat-grid-tile colspan="1" rowspan="1" (click)="onSuivant()">
|
||||
<button> > </button>
|
||||
</mat-grid-tile>
|
||||
</mat-grid-list>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
carousel {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
justify-content: center
|
||||
}
|
||||
|
||||
|
||||
|
||||
.dialog-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupVisualizeImagesAdvertiserComponent } from './popup-visualize-images-advertiser.component';
|
||||
|
||||
describe('PopupVisualizeImagesComponent', () => {
|
||||
let component: PopupVisualizeImagesAdvertiserComponent;
|
||||
let fixture: ComponentFixture<PopupVisualizeImagesAdvertiserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupVisualizeImagesAdvertiserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupVisualizeImagesAdvertiserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-visualize-images-advertiser',
|
||||
templateUrl: './popup-visualize-images-advertiser.component.html',
|
||||
styleUrls: ['./popup-visualize-images-advertiser.component.scss']
|
||||
})
|
||||
export class PopupVisualizeImagesAdvertiserComponent implements OnInit
|
||||
{
|
||||
tabImages = [];
|
||||
index: number = 0;
|
||||
nbImage: number = 0;
|
||||
|
||||
|
||||
constructor( public dialogRef: MatDialogRef<PopupVisualizeImagesAdvertiserComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data ) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.tabImages = this.data.images;
|
||||
this.nbImage = this.tabImages.length;
|
||||
}
|
||||
|
||||
onPrecedent(): void
|
||||
{
|
||||
if(this.index !== 0) this.index -= 1;
|
||||
}
|
||||
|
||||
onSuivant(): void
|
||||
{
|
||||
if(this.index !== (this.nbImage-1)) this.index += 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
<!-- NavBar -->
|
||||
<app-navbar-advertiser></app-navbar-advertiser>
|
||||
|
||||
<!-- Boite -->
|
||||
<div class="boite">
|
||||
|
||||
<!-- Photo de profil -->
|
||||
<div style="text-align: center">
|
||||
<img [src]="advertiser.profileImageUrl"
|
||||
onerror="this.onerror=null; this.src='assets/profil.png'">
|
||||
</div>
|
||||
|
||||
<!-- entreprise -->
|
||||
<div class="row myRow">
|
||||
<div class="col-6 myLabel">Entreprise:</div>
|
||||
<div class="col-6 myValue"> {{advertiser.company}} </div>
|
||||
</div>
|
||||
|
||||
<!-- login -->
|
||||
<div class="row myRow">
|
||||
<div class="col-6 myLabel">Pseudo:</div>
|
||||
<div class="col-6 myValue"> {{advertiser.login}} </div>
|
||||
</div>
|
||||
|
||||
<!-- email -->
|
||||
<div class="row myRow">
|
||||
<div class="col-6 myLabel">Mail:</div>
|
||||
<div class="col-6 myValue"> {{advertiser.email}} </div>
|
||||
</div>
|
||||
|
||||
<!-- createdAt -->
|
||||
<div class="row myRow">
|
||||
<div class="col-6 myLabel">Date de création:</div>
|
||||
<div class="col-6 myValue">{{advertiser.createdAt | date:'dd/LL/YYYY'}}</div>
|
||||
</div>
|
||||
|
||||
<!-- Modifier profil -->
|
||||
<div class="btnContainer">
|
||||
<button mat-button class="myBtn" (click)="onModifier()">Modifier profil</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
.myContainer {
|
||||
max-width: 100vw;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
|
||||
.boite {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 25%;
|
||||
margin-top: 10vh;
|
||||
border: solid 3px;
|
||||
border-radius: 10px;
|
||||
padding: 20px 40px 20px 40px;
|
||||
background-color: #ffffff;
|
||||
text-align: center;
|
||||
box-shadow: 10px 5px 5px black;
|
||||
}
|
||||
.lightTheme .boite {
|
||||
border-color: black;
|
||||
}
|
||||
.darkTheme .boite {
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
margin: 0px 0px 10px 0px;
|
||||
width: 5vw;
|
||||
height: 5vw;
|
||||
border: solid 2px black;
|
||||
border-radius: 50%;
|
||||
font-size: xxx-large;
|
||||
}
|
||||
|
||||
|
||||
.myRow {
|
||||
margin: 15px 0px 15px 0px;
|
||||
}
|
||||
.myLabel {
|
||||
text-align: right;
|
||||
padding: 0px 5px 0px 0px;
|
||||
margin: 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.myValue {
|
||||
text-align: left;
|
||||
padding: 0px 0px 0px 5px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
.btnContainer {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
.myBtn {
|
||||
border: solid 1px black;
|
||||
background-color: white;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PageProfilAdvertiserComponent } from './page-profil-advertiser.component';
|
||||
|
||||
describe('PageProfilAdvertiserComponent', () => {
|
||||
let component: PageProfilAdvertiserComponent;
|
||||
let fixture: ComponentFixture<PageProfilAdvertiserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PageProfilAdvertiserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PageProfilAdvertiserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||
import {PopupUpdateAdvertiserComponent} from "../popup-update-advertiser/popup-update-advertiser.component";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {ProfilService} from "../../../utils/profil/profil.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-profil-advertiser',
|
||||
templateUrl: './page-profil-advertiser.component.html',
|
||||
styleUrls: ['./page-profil-advertiser.component.scss']
|
||||
})
|
||||
export class PageProfilAdvertiserComponent implements OnInit
|
||||
{
|
||||
advertiser = {
|
||||
_id: "",
|
||||
login: "",
|
||||
hashPass: "",
|
||||
email: "",
|
||||
role: {
|
||||
name: "advertiser",
|
||||
permission: 5,
|
||||
isAccepted: true,
|
||||
},
|
||||
profileImageUrl: "",
|
||||
dateOfBirth: null,
|
||||
gender: "man",
|
||||
interests: [],
|
||||
company: "",
|
||||
isActive: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
lastConnexion: null
|
||||
};
|
||||
|
||||
|
||||
constructor( public themeService: ThemeService,
|
||||
public dialog: MatDialog,
|
||||
private snackBar: MatSnackBar,
|
||||
private messageService: MessageService,
|
||||
private profilService: ProfilService ) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.messageService
|
||||
.get( "user/findOne/"+this.profilService.getId())
|
||||
.subscribe( retour => this.ngOnInitCallback(retour), err => this.ngOnInitCallback(err) )
|
||||
}
|
||||
|
||||
|
||||
ngOnInitCallback(retour: any)
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.advertiser = retour.data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onModifier()
|
||||
{
|
||||
const config = {
|
||||
width: '25%',
|
||||
data: { advertiser: this.advertiser }
|
||||
};
|
||||
this.dialog
|
||||
.open(PopupUpdateAdvertiserComponent, config)
|
||||
.afterClosed()
|
||||
.subscribe(retour => {
|
||||
|
||||
if((retour === null) || (retour === undefined))
|
||||
{
|
||||
const config = { duration: 1000, panelClass: "custom-class" };
|
||||
this.snackBar.open( "Opération annulé", "", config);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.advertiser = retour;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<div class="myContainer">
|
||||
<div class="boite">
|
||||
|
||||
<!-- photo de profil -->
|
||||
<div style="text-align: center">
|
||||
<img [src]="advertiserCopy.profileImageUrl" onerror="this.onerror=null; this.src='assets/profil.png'"><br>
|
||||
<input title="lien vers image" type="text" [(ngModel)]="advertiserCopy.profileImageUrl" style="width: 90%">
|
||||
</div>
|
||||
|
||||
<!-- divider -->
|
||||
<br><mat-divider></mat-divider><br>
|
||||
|
||||
<!-- entreprise -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Entreprise</mat-label>
|
||||
<input matInput type="text" [(ngModel)]="advertiserCopy.company">
|
||||
</mat-form-field><br>
|
||||
|
||||
<!-- login -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Pseudo</mat-label>
|
||||
<input matInput type="text" [(ngModel)]="advertiserCopy.login">
|
||||
</mat-form-field><br>
|
||||
|
||||
<!-- divider -->
|
||||
<mat-divider></mat-divider><br>
|
||||
|
||||
<!-- Modifier mot de passe -->
|
||||
<div>
|
||||
Modifier mot de passe:
|
||||
<mat-checkbox [(ngModel)]="changePassword"></mat-checkbox>
|
||||
</div>
|
||||
|
||||
<!-- nouveau mot de passe -->
|
||||
<div *ngIf="changePassword" style="margin-top: 10px">
|
||||
<!-- Nouveau mot de passe -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Nouveau mot de passe</mat-label>
|
||||
<input matInput type="password" [(ngModel)]="newPassword">
|
||||
</mat-form-field>
|
||||
<br>
|
||||
<!-- Confirmation nouveau mot de passe -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Confirmation nouveau mot de passe</mat-label>
|
||||
<input matInput type="password" [(ngModel)]="confirmNewPassword">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div *ngIf="!changePassword"><br></div>
|
||||
|
||||
<!-- divider -->
|
||||
<mat-divider></mat-divider><br>
|
||||
|
||||
<!-- message d'erreur -->
|
||||
<div *ngIf="hasError" style="text-align: center; margin-bottom: 20px;">
|
||||
<span class="mat-error">{{errorMessage}}</span>
|
||||
</div>
|
||||
|
||||
<!-- boutons -->
|
||||
<div style="width: 100%; text-align: right">
|
||||
<button mat-button (click)="this.dialogRef.close(null)"> Annuler </button>
|
||||
<button mat-button (click)="onValider()"> Enregistrer </button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
.boite {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
img {
|
||||
margin: 0px 0px 10px 0px;
|
||||
width: 5vw;
|
||||
height: 5vw;
|
||||
border: solid 2px black;
|
||||
border-radius: 50%;
|
||||
font-size: xxx-large;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// aura
|
||||
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
|
||||
background-color: grey !important;
|
||||
}
|
||||
|
||||
// contenu coche
|
||||
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||
background-color: black !important;
|
||||
}
|
||||
|
||||
// indeterminate
|
||||
::ng-deep .mat-checkbox .mat-checkbox-frame {
|
||||
background-color: white !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupUpdateAdvertiserComponent } from './popup-update-advertiser.component';
|
||||
|
||||
describe('PopupUpdateAdvertiserComponent', () => {
|
||||
let component: PopupUpdateAdvertiserComponent;
|
||||
let fixture: ComponentFixture<PopupUpdateAdvertiserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupUpdateAdvertiserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupUpdateAdvertiserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {ProfilService} from "../../../utils/profil/profil.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-update-advertiser',
|
||||
templateUrl: './popup-update-advertiser.component.html',
|
||||
styleUrls: ['./popup-update-advertiser.component.scss']
|
||||
})
|
||||
export class PopupUpdateAdvertiserComponent implements OnInit
|
||||
{
|
||||
advertiserCopy;
|
||||
newPassword: string = "";
|
||||
confirmNewPassword: string = "" ;
|
||||
changePassword: boolean = false ;
|
||||
hasError: boolean = false;
|
||||
errorMessage: string = "" ;
|
||||
|
||||
|
||||
constructor( public dialogRef: MatDialogRef<PopupUpdateAdvertiserComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data,
|
||||
private messageService: MessageService,
|
||||
private profilService: ProfilService ) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
const advertiser0 = this.data.advertiser;
|
||||
this.advertiserCopy = {
|
||||
_id: advertiser0._id,
|
||||
login: advertiser0.login,
|
||||
hashPass: advertiser0.hashPass,
|
||||
email: advertiser0.email,
|
||||
role: {
|
||||
name: advertiser0.role.name,
|
||||
permission: advertiser0.role.permission,
|
||||
isAccepted: advertiser0.role.isAccepted,
|
||||
},
|
||||
profileImageUrl: advertiser0.profileImageUrl,
|
||||
dateOfBirth: advertiser0.dateOfBirth,
|
||||
gender: advertiser0.gender,
|
||||
interests: [],
|
||||
company: advertiser0.company,
|
||||
isActive: advertiser0.isActive,
|
||||
createdAt: advertiser0.createdAt,
|
||||
updatedAt: advertiser0.updatedAt,
|
||||
lastConnexion: new Date()
|
||||
};
|
||||
for(let interest of advertiser0.interests) this.advertiserCopy.interests.push(interest);
|
||||
}
|
||||
|
||||
|
||||
onValider()
|
||||
{
|
||||
this.checkField();
|
||||
if(!this.hasError)
|
||||
{
|
||||
if(this.changePassword) this.advertiserCopy.hashPass = this.newPassword;
|
||||
const data = {
|
||||
login: this.advertiserCopy.login,
|
||||
hashPass: this.advertiserCopy.hashPass,
|
||||
email: this.advertiserCopy.email,
|
||||
profileImageUrl: this.advertiserCopy.profileImageUrl,
|
||||
company: this.advertiserCopy.company
|
||||
};
|
||||
this.messageService
|
||||
.put("user/update/"+this.profilService.getId(), data)
|
||||
.subscribe( ret => this.onValiderCallback(ret), err => this.onValiderCallback(err) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onValiderCallback(retour: any)
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.dialogRef.close(null);
|
||||
}
|
||||
else {
|
||||
this.profilService.setProfileImageUrl(this.advertiserCopy.profileImageUrl);
|
||||
this.dialogRef.close(this.advertiserCopy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
checkField()
|
||||
{
|
||||
if(this.advertiserCopy.login.length === 0) {
|
||||
this.errorMessage = "Veuillez remplir le champ 'pseudo'" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(this.advertiserCopy.email.length === 0) {
|
||||
this.errorMessage = "Veuillez remplir le champ 'email'" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(!this.isValidEmail(this.advertiserCopy.email)) {
|
||||
this.errorMessage = "Email invalide" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if((this.changePassword) && (this.newPassword.length === 0)) {
|
||||
this.errorMessage = "Veuillez remplir le champ 'mot de passe'" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if((this.changePassword) && (this.newPassword !== this.confirmNewPassword)) {
|
||||
this.errorMessage = "Le mot de passe est différent de sa confirmation" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else {
|
||||
this.errorMessage = "" ;
|
||||
this.hasError = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
isValidEmail(email)
|
||||
{
|
||||
let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
return re.test(email);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
|
||||
<!-- Navbar -->
|
||||
<app-navbar-advertiser></app-navbar-advertiser>
|
||||
|
||||
|
||||
|
||||
<!-- Filter -->
|
||||
<div class="filtersContainer mat-elevation-z8">
|
||||
|
||||
<div style="font-weight: bold;">Filtre</div>
|
||||
|
||||
<mat-divider></mat-divider><br>
|
||||
|
||||
<div *ngIf="true; then colPeriode"></div>
|
||||
|
||||
<br><mat-divider style="width: 60%"></mat-divider><br>
|
||||
|
||||
<div style="text-align: right; font-size: small;">
|
||||
<button mat-button (click)="onApplyFilter()" style="font-size: small">Appliquer</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- chart -->
|
||||
<div *ngIf="isDisplayable" class="chartContainer">
|
||||
<canvas baseChart
|
||||
[datasets]="lineChartData"
|
||||
[labels]="lineChartLabels"
|
||||
[options]="chartOptions"
|
||||
[colors]="[]"
|
||||
[legend]="true"
|
||||
[chartType]="'line'">
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<ng-template #colPeriode>
|
||||
|
||||
<!-- startDate -->
|
||||
<mat-form-field appearance="fill" style="width: 140px;">
|
||||
<mat-label>début</mat-label>
|
||||
<input matInput type="date" [ngModel] ="startDate | date:'yyyy-MM-dd'" (ngModelChange)="onNewStartDate($event);">
|
||||
</mat-form-field>
|
||||
-
|
||||
|
||||
<!-- endDate -->
|
||||
<mat-form-field appearance="fill" style="width: 140px;">
|
||||
<mat-label>fin</mat-label>
|
||||
<input matInput type="date" [ngModel] ="endDate | date:'yyyy-MM-dd'" (ngModelChange)="onNewEndDate($event);">
|
||||
</mat-form-field>
|
||||
-
|
||||
|
||||
<!-- step -->
|
||||
<mat-form-field appearance="fill" style="width: 140px;">
|
||||
<mat-label>pas d'affichage</mat-label>
|
||||
<input matInput type="number" [(ngModel)] =step>
|
||||
</mat-form-field>
|
||||
-
|
||||
|
||||
<!-- step unity -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>unité du pas d'affichage</mat-label>
|
||||
<mat-select [(ngModel)]="stepUnity">
|
||||
<mat-option value="jour">jour</mat-option>
|
||||
<mat-option value="semaine">semaine</mat-option>
|
||||
<mat-option value="mois">mois</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field><br>
|
||||
|
||||
|
||||
<!-- ads -->
|
||||
<mat-select [formControl]="formControl" multiple style="padding-top: 10px; width: 60%;">
|
||||
<mat-select-trigger>
|
||||
<span *ngIf="formControl.value?.length > 0">
|
||||
<span *ngFor="let coupleNameViews of formControl.value"> {{coupleNameViews.name}}, </span>
|
||||
</span>
|
||||
</mat-select-trigger>
|
||||
<mat-option *ngFor="let coupleNameViews of allCoupleNameViews" [value]="coupleNameViews">{{coupleNameViews.name}}</mat-option>
|
||||
</mat-select>
|
||||
<button mat-button class="btnToutSelectionner" (click)="onSelectAll()">Tout sélectionner</button>
|
||||
<button mat-button class="btnToutDeselectionner" (click)="onDeSelectAll()">Tout désélectionner</button>
|
||||
|
||||
</ng-template>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
.myContainer {
|
||||
font-size: small;
|
||||
max-width: 100vw;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: small;
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.filtersContainer {
|
||||
background-color: white;
|
||||
width: 60%;
|
||||
margin: 50px 50px 50px 50px;
|
||||
padding: 20px 20px 20px 20px;
|
||||
}
|
||||
|
||||
.chartContainer {
|
||||
background-color: white;
|
||||
border: solid 1px black;
|
||||
padding: 10px 10px 10px 10px;
|
||||
margin: 50px 50px 50px 50px;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------
|
||||
// periode
|
||||
|
||||
.periode {
|
||||
padding: 10px 10px 0px 10px;
|
||||
}
|
||||
|
||||
.periode .titleContainer {
|
||||
text-align: right;
|
||||
border-right: solid 1px #dcdcdc;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btnToutSelectionner {
|
||||
font-size: small;
|
||||
}
|
||||
.btnToutDeselectionner {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
::ng-deep .mat-pseudo-checkbox-checked {
|
||||
background-color: black !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PagesPopularityComponent } from './pages-popularity.component';
|
||||
|
||||
describe('SubjectsPopularityComponent', () => {
|
||||
let component: PagesPopularityComponent;
|
||||
let fixture: ComponentFixture<PagesPopularityComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PagesPopularityComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PagesPopularityComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {FormControl} from "@angular/forms";
|
||||
import {ChartDataSets} from "chart.js";
|
||||
import {Label} from "ng2-charts";
|
||||
import { Router} from "@angular/router";
|
||||
import {HttpParams} from "@angular/common/http";
|
||||
import {ThemeService} from "../../utils/theme/theme.service";
|
||||
import {MessageService} from "../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
interface CoupleNameViews {
|
||||
name: string,
|
||||
views: Date[],
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-subjects-popularity',
|
||||
templateUrl: './pages-popularity.component.html',
|
||||
styleUrls: ['./pages-popularity.component.scss']
|
||||
})
|
||||
export class PagesPopularityComponent implements OnInit
|
||||
{
|
||||
formControl: FormControl = new FormControl();
|
||||
allCoupleNameViews: CoupleNameViews[] = [];
|
||||
|
||||
allInterests: string[] = [];
|
||||
|
||||
startDate: Date = null;
|
||||
endDate: Date = null;
|
||||
step: number = 1;
|
||||
stepUnity: string = "jour" ;
|
||||
|
||||
oneDay: number = 24*60*60*1000;
|
||||
oneWeek: number = 7*24*60*60*1000;
|
||||
|
||||
lineChartData: ChartDataSets[] = [];
|
||||
lineChartLabels: Label[] = [];
|
||||
chartOptions: any = {
|
||||
responsive: true,
|
||||
scales: {
|
||||
yAxes: [{ display: true, scaleLabel: { display: true, labelString: "vues" } }],
|
||||
xAxes: [{ scaleLabel: { display: true, labelString: "temps" } }],
|
||||
}
|
||||
};
|
||||
|
||||
isDisplayable: boolean = false;
|
||||
|
||||
|
||||
constructor( private router: Router,
|
||||
public themeService: ThemeService,
|
||||
private messageService: MessageService ) {}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
// Sera excuté si on est sur la page 'adsPopularity'
|
||||
// Remplie l'attribut 'allCoupleNameViews'
|
||||
if(this.router.url.includes("ads"))
|
||||
{
|
||||
let params = new HttpParams();
|
||||
params = params.append("isActive", true);
|
||||
this.messageService
|
||||
.get("ad/findAll", params )
|
||||
.subscribe(ret => this.afterReceivingAds(ret), err => this.afterReceivingAds(err));
|
||||
}
|
||||
|
||||
// Sera excuté si on est sur la page 'subjectsPopularity'
|
||||
// Remplie l'attribut 'allCoupleNameViews'
|
||||
else if(this.router.url.includes("subjects"))
|
||||
{
|
||||
this.messageService
|
||||
.get("misc/getInterests")
|
||||
.subscribe( retour => {
|
||||
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.allInterests = retour.data.map(x => x.interest);
|
||||
this.allInterests.sort();
|
||||
this.messageService
|
||||
.get("video/findAll")
|
||||
.subscribe(ret => this.afterReceivingVideos(ret), err => this.afterReceivingVideos(err));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Callback: Sera excuté si on est sur la page 'adsPopularity'
|
||||
afterReceivingAds(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
const allAdverts = retour.data;
|
||||
for(let advert of allAdverts)
|
||||
{
|
||||
let couple = {name: advert.title, views: advert.views }
|
||||
this.allCoupleNameViews.push(couple);
|
||||
}
|
||||
|
||||
this.formControl = new FormControl(this.allCoupleNameViews);
|
||||
this.onApplyFilter();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Callback: Sera excuté si on est sur la page 'subjectsPopularity'
|
||||
afterReceivingVideos(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
const allVideos = retour.data;
|
||||
let myMap: Map<string,Date[]> = new Map();
|
||||
|
||||
// parcours des interest de chaque video
|
||||
for(let video of allVideos)
|
||||
{
|
||||
const key = video.interest;
|
||||
if(!myMap.has(key)) myMap.set(key, video.watchedDates);
|
||||
else {
|
||||
let tabDate = myMap.get(key);
|
||||
for(let date0 of video.watchedDates) tabDate = this.insertInOrder(tabDate, date0);
|
||||
myMap.set(key, tabDate);
|
||||
}
|
||||
}
|
||||
|
||||
// parcours les interest qui n'ont pas p été vu dans les videos
|
||||
for(let interest of this.allInterests)
|
||||
{
|
||||
if(!myMap.has(interest)) myMap.set(interest, []);
|
||||
}
|
||||
|
||||
// parcours de la map pour remplir 'allCoupleNameViews'
|
||||
for(const [key, value] of myMap.entries())
|
||||
{
|
||||
let couple = {name: key, views: value }
|
||||
this.allCoupleNameViews.push(couple);
|
||||
}
|
||||
|
||||
this.formControl = new FormControl(this.allCoupleNameViews);
|
||||
this.onApplyFilter();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Applique le filtre
|
||||
onApplyFilter(): void
|
||||
{
|
||||
// --- initialisation ---
|
||||
this.lineChartData = [];
|
||||
this.lineChartLabels = [];
|
||||
|
||||
if(this.step <= 0) this.step = 0;
|
||||
if((this.endDate === null) || (this.endDate === undefined)) this.endDate = new Date();
|
||||
if((this.startDate === null) || (this.startDate === undefined)) this.startDate = new Date(this.endDate.getTime() - this.oneWeek); // date d'il y a une semaine
|
||||
|
||||
const startTime = this.startDate.getTime();
|
||||
const endTime = this.endDate.getTime();
|
||||
|
||||
|
||||
// --- remplissage de 'lineChartLabels' ---
|
||||
let dataWithZeros = [];
|
||||
let time = startTime;
|
||||
const intervals = [];
|
||||
while(time <= endTime)
|
||||
{
|
||||
dataWithZeros.push(0);
|
||||
this.lineChartLabels.push(this.getLabel(new Date(time)));
|
||||
intervals.push(time);
|
||||
time = this.addStep(time);
|
||||
}
|
||||
intervals.push(time);
|
||||
|
||||
|
||||
// --- remplissage de 'lineChartLabels' ---
|
||||
for(let coupleNameViews of this.formControl.value)
|
||||
{
|
||||
let data = dataWithZeros.slice();
|
||||
let label = coupleNameViews.name;
|
||||
let index = 0;
|
||||
|
||||
for(let date0 of coupleNameViews.views)
|
||||
{
|
||||
const time0 = (new Date(date0)).getTime();
|
||||
|
||||
if(time0 > endTime) break;
|
||||
|
||||
if((startTime <= time0) && (time0 <= endTime))
|
||||
{
|
||||
while((index < intervals.length) && (time0 >= intervals[index])) index += 1;
|
||||
index = index - 1;
|
||||
data[index] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.lineChartData.push({"data": data.slice(), "label": label});
|
||||
}
|
||||
this.isDisplayable = true;
|
||||
}
|
||||
|
||||
|
||||
onNewStartDate(event): void {
|
||||
this.startDate = new Date(event);
|
||||
}
|
||||
|
||||
|
||||
onNewEndDate(event): void {
|
||||
this.endDate = new Date(event);
|
||||
}
|
||||
|
||||
|
||||
// Renvoie le bon label pour le graph
|
||||
getLabel(date0: Date): string
|
||||
{
|
||||
if((this.stepUnity === 'jour') && (this.step === 1))
|
||||
{
|
||||
return date0.toLocaleDateString();
|
||||
}
|
||||
else {
|
||||
const time2 = this.addStep((new Date(date0)).getTime()) - this.oneDay;
|
||||
let date2 = new Date(time2);
|
||||
return date0.toLocaleDateString() + " à " + date2.toLocaleDateString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Ajoute le bon pas à la date 'new Date(time)'
|
||||
addStep(time: number): number
|
||||
{
|
||||
let newDate;
|
||||
|
||||
if(this.stepUnity === 'jour') {
|
||||
newDate = new Date(time + this.step*this.oneDay);
|
||||
}
|
||||
else if(this.stepUnity === 'semaine') {
|
||||
newDate = new Date(time + this.step*this.oneWeek);
|
||||
}
|
||||
else
|
||||
{
|
||||
const oldDate = new Date(time);
|
||||
|
||||
let newMonth = oldDate.getMonth() + this.step;
|
||||
const newYear = oldDate.getFullYear() + (newMonth / 12);
|
||||
newMonth = newMonth % 12;
|
||||
const day = this.startDate.getDate();
|
||||
|
||||
if((newMonth === 1) && ([29, 30, 31].includes(day))) { // si fevrier et si jour n'existe pas
|
||||
newDate = new Date(newYear, newMonth, 28);
|
||||
}
|
||||
else if((day === 31) && ([3, 5, 9, 10].includes(newMonth))) { // si 31 et mois à 30 jours
|
||||
newDate = new Date(newYear, newMonth, 30);
|
||||
}
|
||||
else {
|
||||
newDate = new Date(newYear, newMonth, day);
|
||||
}
|
||||
}
|
||||
|
||||
const _1h = 60*60*1000;
|
||||
if(newDate.getHours() === 23) return newDate.getTime() + _1h;
|
||||
else if(newDate.getHours() === 1) return newDate.getTime() - _1h;
|
||||
else return newDate.getTime();
|
||||
}
|
||||
|
||||
|
||||
// Insere la date0 dans le tableau tabDate par ordre croissant
|
||||
insertInOrder(tabDate: Date[], date0: Date): Date[]
|
||||
{
|
||||
let i = 0;
|
||||
let n = tabDate.length;
|
||||
let time0 = (new Date(date0)).getTime();
|
||||
|
||||
while((i <n) && (time0 > (new Date(tabDate[i])).getTime())) i++;
|
||||
if(i === n) tabDate.push(date0);
|
||||
else tabDate.splice(i, 0, date0);
|
||||
|
||||
return tabDate;
|
||||
}
|
||||
|
||||
|
||||
onSelectAll(): void
|
||||
{
|
||||
this.formControl = new FormControl(this.allCoupleNameViews);
|
||||
}
|
||||
|
||||
onDeSelectAll(): void
|
||||
{
|
||||
this.formControl = new FormControl([]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { DragAndDropDirective } from './drag-and-drop.directive';
|
||||
|
||||
describe('DragAndDropDirective', () => {
|
||||
it('should create an instance', () => {
|
||||
const directive = new DragAndDropDirective();
|
||||
expect(directive).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import {Directive, EventEmitter, HostBinding, HostListener, Output} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appDragAndDrop]'
|
||||
})
|
||||
export class DragAndDropDirective
|
||||
{
|
||||
@HostBinding('class.fileover') fileOver: boolean;
|
||||
@Output() fileDropped = new EventEmitter<any>();
|
||||
|
||||
// Dragover listener
|
||||
@HostListener('dragover', ['$event']) onDragOver(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
this.fileOver = true;
|
||||
}
|
||||
|
||||
// Dragleave listener
|
||||
@HostListener('dragleave', ['$event']) public onDragLeave(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
this.fileOver = false;
|
||||
}
|
||||
|
||||
// Drop listener
|
||||
@HostListener('drop', ['$event']) public ondrop(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
this.fileOver = false;
|
||||
let files = evt.dataTransfer.files;
|
||||
if (files.length > 0) {
|
||||
this.fileDropped.emit(files);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<nav class="navbar navbar-expand-lg">
|
||||
|
||||
<!-- PolyNotFound -->
|
||||
<a class="navbar-brand" routerLink="/url/adList"> StreamNotFound </a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
|
||||
<!-- [adList] [adsPopularity] [subjectsPopularity] -->
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item active monLi">
|
||||
<a *ngIf="(url !== routes[0]) && (url !== routes[1])" [routerLink]="routes[1]" class="nav-link">Gestion des annonces</a>
|
||||
<a *ngIf="(url === routes[0]) || (url === routes[1])" [routerLink]="routes[1]" class="nav-link myActiveLink">Gestion des annonces</a>
|
||||
</li>
|
||||
<li class="nav-item active monLi">
|
||||
<a *ngIf="url !== routes[2]" [routerLink]="routes[2]" class="nav-link">Popularité des annonces</a>
|
||||
<a *ngIf="url === routes[2]" [routerLink]="routes[2]" class="nav-link myActiveLink">Popularité des annonces</a>
|
||||
</li>
|
||||
<li class="nav-item active monLi">
|
||||
<a *ngIf="url !== routes[3]" [routerLink]="routes[3]" class="nav-link">Popularité des domaines</a>
|
||||
<a *ngIf="url === routes[3]" [routerLink]="routes[3]" class="nav-link myActiveLink">Popularité des domaines</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Mon profil -->
|
||||
<img [src]=profilService.getProfileImageUrl()
|
||||
onerror="this.onerror=null; this.src='assets/profil.png'"
|
||||
[routerLink]="routes[4]"
|
||||
alt="">
|
||||
|
||||
|
||||
<!-- Deconnexion -->
|
||||
<button mat-button class="btnDeconnexion" (click)="onDeconnexion()" routerLink="/">
|
||||
Déconnexion
|
||||
</button>
|
||||
|
||||
</nav>
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
.navbar {
|
||||
background-color: black;
|
||||
height: 60px;
|
||||
font-size: medium;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.navbar-expand-lg {
|
||||
border-bottom: solid;
|
||||
border-color: white;
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
|
||||
|
||||
// PolyNotFound
|
||||
.navbar-brand {
|
||||
font-family: cursive;
|
||||
font-weight: bold;
|
||||
font-size: x-large;
|
||||
margin-left: 15px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.monLi {
|
||||
margin: 0px 10px 0px 10px;
|
||||
}
|
||||
|
||||
|
||||
.nav-link {
|
||||
color: white;
|
||||
}
|
||||
.nav-link:hover {
|
||||
color: grey;
|
||||
}
|
||||
.myActiveLink {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
.btnDeconnexion {
|
||||
font-size: medium;
|
||||
margin: 0px 10px 0px 10px
|
||||
}
|
||||
.btnDeconnexion:hover {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
border: solid 2px white;
|
||||
border-radius: 50px;
|
||||
margin: 0px 10px 0px 15px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
img:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
|
||||
::ng-deep .mat-slide-toggle-thumb {
|
||||
background-color: #c8c8c8;
|
||||
}
|
||||
|
||||
::ng-deep .mat-slide-toggle-bar {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-thumb {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-bar {
|
||||
background-color: #646464;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NavbarAdvertiserComponent } from './navbar-advertiser.component';
|
||||
|
||||
describe('NavbarAdvertiserComponent', () => {
|
||||
let component: NavbarAdvertiserComponent;
|
||||
let fixture: ComponentFixture<NavbarAdvertiserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ NavbarAdvertiserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NavbarAdvertiserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import { Component } from '@angular/core';
|
||||
import {Router} from "@angular/router";
|
||||
import {ProfilService} from "../../../utils/profil/profil.service";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-navbar-advertiser',
|
||||
templateUrl: './navbar-advertiser.component.html',
|
||||
styleUrls: ['./navbar-advertiser.component.scss']
|
||||
})
|
||||
export class NavbarAdvertiserComponent
|
||||
{
|
||||
routes: string[] = [
|
||||
"/advertiser", // 0
|
||||
"/advertiser/adList", // 1
|
||||
"/advertiser/adsPopularity", // 2
|
||||
"/advertiser/subjectsPopularity", // 3
|
||||
"/advertiser/myProfil" // 4
|
||||
];
|
||||
|
||||
url = this.router.url;
|
||||
|
||||
constructor( private router: Router,
|
||||
public profilService: ProfilService,
|
||||
private messageService: MessageService ) { }
|
||||
|
||||
onDeconnexion(): void
|
||||
{
|
||||
this.messageService
|
||||
.delete('user/logout')
|
||||
.subscribe(retour => this.onDeconnexionCallback(retour), err => this.onDeconnexionCallback(err));
|
||||
}
|
||||
|
||||
onDeconnexionCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") console.log(retour);
|
||||
}
|
||||
|
||||
}
|
||||
41
src/app/app-routing.module.ts
Normal file
41
src/app/app-routing.module.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import {PageLoginComponent} from "./beforeConnexion/login/page-login/page-login.component";
|
||||
import {PageRegisterComponent} from "./beforeConnexion/register/page-register/page-register.component";
|
||||
import {PageSearchComponent} from "./user/search/page-search/page-search.component";
|
||||
import {PageMyPlaylistsComponent} from "./user/myPlaylists/page-my-playlists/page-my-playlists.component";
|
||||
import {PageProfilUserComponent} from "./user/myProfil/page-profil-user/page-profil-user.component";
|
||||
import {PageWatchingVideoComponent} from "./user/watching/page-watching-video/page-watching-video.component";
|
||||
import {PageHistoryUserComponent} from "./user/history/page-history-user/page-history-user.component";
|
||||
import {PageAdListAdvertiserComponent} from "./advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component";
|
||||
import {PagesPopularityComponent} from "./advertiser/pages-popularity/pages-popularity.component";
|
||||
import {PageProfilAdvertiserComponent} from "./advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component";
|
||||
|
||||
|
||||
const routes: Routes = [
|
||||
// Before connexion
|
||||
{ path: '', component: PageLoginComponent },
|
||||
{ path: 'login', component: PageLoginComponent },
|
||||
{ path: 'register', component: PageRegisterComponent },
|
||||
|
||||
// User
|
||||
{ path: 'user', component: PageSearchComponent },
|
||||
{ path: 'user/search', component: PageSearchComponent },
|
||||
{ path: 'user/myPlaylists', component: PageMyPlaylistsComponent },
|
||||
{ path: 'user/history', component: PageHistoryUserComponent },
|
||||
{ path: 'user/myProfil', component: PageProfilUserComponent },
|
||||
{ path: 'user/watching', component: PageWatchingVideoComponent },
|
||||
|
||||
// Advertiser
|
||||
{ path: 'advertiser', component: PageAdListAdvertiserComponent },
|
||||
{ path: 'advertiser/adList', component: PageAdListAdvertiserComponent },
|
||||
{ path: 'advertiser/myProfil', component: PageProfilAdvertiserComponent },
|
||||
{ path: 'advertiser/adsPopularity', component: PagesPopularityComponent },
|
||||
{ path: 'advertiser/subjectsPopularity', component: PagesPopularityComponent },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
2
src/app/app.component.html
Normal file
2
src/app/app.component.html
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<router-outlet></router-outlet>
|
||||
24
src/app/app.component.scss
Normal file
24
src/app/app.component.scss
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
::ng-deep snack-bar-container.custom-class {
|
||||
//background: yellow;
|
||||
}
|
||||
::ng-deep .custom-class .mat-simple-snackbar {
|
||||
//color: green;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
// aura
|
||||
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
|
||||
background-color: grey !important;
|
||||
}
|
||||
|
||||
// contenu coche
|
||||
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||
background-color: black !important;
|
||||
}
|
||||
|
||||
// indeterminate
|
||||
::ng-deep .mat-checkbox .mat-checkbox-frame {
|
||||
border: solid 1px black !important;
|
||||
background-color: white !important;
|
||||
}
|
||||
35
src/app/app.component.spec.ts
Normal file
35
src/app/app.component.spec.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'userAndAdvertiser'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('userAndAdvertiser');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('.content span')?.textContent).toContain('userAndAdvertiser app is running!');
|
||||
});
|
||||
});
|
||||
10
src/app/app.component.ts
Normal file
10
src/app/app.component.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'userAndAdvertiser';
|
||||
}
|
||||
131
src/app/app.module.ts
Normal file
131
src/app/app.module.ts
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
|
||||
import {MatSlideToggleModule} from "@angular/material/slide-toggle";
|
||||
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||
import {HttpClientModule} from "@angular/common/http";
|
||||
import {MatIconModule} from "@angular/material/icon";
|
||||
import {MatDialogModule} from "@angular/material/dialog";
|
||||
import {MatButtonModule} from "@angular/material/button";
|
||||
import {MatInputModule} from "@angular/material/input";
|
||||
import {MatCheckboxModule} from "@angular/material/checkbox";
|
||||
import {MatFormFieldModule} from "@angular/material/form-field";
|
||||
import {MatDividerModule} from "@angular/material/divider";
|
||||
import {MatSnackBarModule} from "@angular/material/snack-bar";
|
||||
import {MatGridListModule} from "@angular/material/grid-list";
|
||||
import {MatTableModule} from "@angular/material/table";
|
||||
import {MatSortModule} from "@angular/material/sort";
|
||||
import {MatChipsModule} from "@angular/material/chips";
|
||||
import {MatSelectModule} from "@angular/material/select";
|
||||
import {MatAutocompleteModule} from "@angular/material/autocomplete";
|
||||
import {MatRadioModule} from "@angular/material/radio";
|
||||
import {MatPaginatorModule} from "@angular/material/paginator";
|
||||
import {MatDatepickerModule} from "@angular/material/datepicker";
|
||||
import { ChartsModule } from 'ng2-charts';
|
||||
import {DragAndDropComponent} from "./advertiser/adList/drag-and-drop/drag-and-drop.component";
|
||||
import {InputInterestsAdComponent} from "./advertiser/adList/input-interests-ad/input-interests-ad.component";
|
||||
import {PageAdListAdvertiserComponent} from "./advertiser/adList/page-ad-list-advertiser/page-ad-list-advertiser.component";
|
||||
import {PopupAddOrUpdateAdComponent} from "./advertiser/adList/popup-add-or-update-ad/popup-add-or-update-ad.component";
|
||||
import {PopupDeleteAdAdvertiserComponent} from "./advertiser/adList/popup-delete-ad-advertiser/popup-delete-ad-advertiser.component";
|
||||
import {PopupVisualizeAdAdvertiserComponent} from "./advertiser/adList/popup-visualize-ad-advertiser/popup-visualize-ad-advertiser.component";
|
||||
import {PopupVisualizeImagesAdvertiserComponent} from "./advertiser/adList/popup-visualize-images-advertiser/popup-visualize-images-advertiser.component";
|
||||
import {NavbarAdvertiserComponent} from "./advertiser/utils/navbar-advertiser/navbar-advertiser.component";
|
||||
import {DragAndDropDirective} from "./advertiser/utils/dragAndDrop/drag-and-drop.directive";
|
||||
import {PageProfilAdvertiserComponent} from "./advertiser/myProfil/page-profil-advertiser/page-profil-advertiser.component";
|
||||
import {PopupUpdateAdvertiserComponent} from "./advertiser/myProfil/popup-update-advertiser/popup-update-advertiser.component";
|
||||
import {PagesPopularityComponent} from "./advertiser/pages-popularity/pages-popularity.component";
|
||||
import {NavbarUserComponent} from "./user/utils/components/navbar-user/navbar-user.component";
|
||||
import {PageHistoryUserComponent} from "./user/history/page-history-user/page-history-user.component";
|
||||
import {PageMyPlaylistsComponent} from "./user/myPlaylists/page-my-playlists/page-my-playlists.component";
|
||||
import {PlaylistListComponent} from "./user/myPlaylists/playlist-list/playlist-list.component";
|
||||
import {PopupCreateOrUpdatePlaylistComponent} from "./user/myPlaylists/popup-create-or-update-playlist/popup-create-or-update-playlist.component";
|
||||
import {PopupDeletePlaylistComponent} from "./user/myPlaylists/popup-delete-playlist/popup-delete-playlist.component";
|
||||
import {VideoListComponent} from "./user/myPlaylists/video-list/video-list.component";
|
||||
import {InputInterestsProfilComponent} from "./user/myProfil/input-interests-profil/input-interests-profil.component";
|
||||
import {PageProfilUserComponent} from "./user/myProfil/page-profil-user/page-profil-user.component";
|
||||
import {PopupUpdateUserComponent} from "./user/myProfil/popup-update-user/popup-update-user.component";
|
||||
import {PageSearchComponent} from "./user/search/page-search/page-search.component";
|
||||
import {VideoGridComponent} from "./user/search/video-grid/video-grid.component";
|
||||
import {PageWatchingVideoComponent} from "./user/watching/page-watching-video/page-watching-video.component";
|
||||
import {AdvertComponent} from "./user/utils/components/advert/advert.component";
|
||||
import {PopupAddVideoToPlaylistsComponent} from "./user/utils/components/popup-add-video-to-playlists/popup-add-video-to-playlists.component";
|
||||
import {PageLoginComponent} from "./beforeConnexion/login/page-login/page-login.component";
|
||||
import {PopupForgottenPasswordComponent} from "./beforeConnexion/login/popup-forgotten-password/popup-forgotten-password.component";
|
||||
import {InputInterestsRegisterComponent} from "./beforeConnexion/register/input-interests-register/input-interests-register.component";
|
||||
import {PageRegisterComponent} from "./beforeConnexion/register/page-register/page-register.component";
|
||||
import {PopupConfirmationComponent} from "./beforeConnexion/register/popup-confirmation/popup-confirmation.component";
|
||||
import {NavbarBeforeConnexionComponent} from "./beforeConnexion/utils/navbar-before-connexion/navbar-before-connexion.component";
|
||||
import {MatStepperModule} from "@angular/material/stepper";
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
DragAndDropDirective,
|
||||
NavbarBeforeConnexionComponent,
|
||||
PageLoginComponent,
|
||||
PopupForgottenPasswordComponent,
|
||||
InputInterestsRegisterComponent,
|
||||
PageRegisterComponent,
|
||||
PopupConfirmationComponent,
|
||||
NavbarAdvertiserComponent,
|
||||
DragAndDropComponent,
|
||||
InputInterestsAdComponent,
|
||||
PageAdListAdvertiserComponent,
|
||||
PopupAddOrUpdateAdComponent,
|
||||
PopupDeleteAdAdvertiserComponent,
|
||||
PopupVisualizeAdAdvertiserComponent,
|
||||
PopupVisualizeImagesAdvertiserComponent,
|
||||
PageProfilAdvertiserComponent,
|
||||
PopupUpdateAdvertiserComponent,
|
||||
PagesPopularityComponent,
|
||||
NavbarUserComponent,
|
||||
PageHistoryUserComponent,
|
||||
PageMyPlaylistsComponent,
|
||||
PlaylistListComponent,
|
||||
PopupCreateOrUpdatePlaylistComponent,
|
||||
PopupDeletePlaylistComponent,
|
||||
VideoListComponent,
|
||||
InputInterestsProfilComponent,
|
||||
PageProfilUserComponent,
|
||||
PopupUpdateUserComponent,
|
||||
PageSearchComponent,
|
||||
VideoGridComponent,
|
||||
PageWatchingVideoComponent,
|
||||
AdvertComponent,
|
||||
PopupAddVideoToPlaylistsComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
MatSlideToggleModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
MatDialogModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatDividerModule,
|
||||
MatCheckboxModule,
|
||||
MatFormFieldModule,
|
||||
MatSnackBarModule,
|
||||
MatGridListModule,
|
||||
MatTableModule,
|
||||
MatSortModule,
|
||||
MatChipsModule,
|
||||
ReactiveFormsModule,
|
||||
MatAutocompleteModule,
|
||||
MatSelectModule,
|
||||
MatRadioModule,
|
||||
MatPaginatorModule,
|
||||
MatDatepickerModule,
|
||||
ChartsModule,
|
||||
MatStepperModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="bg">
|
||||
|
||||
<app-navbar-before-connexion pour="login"></app-navbar-before-connexion>
|
||||
|
||||
<div class="wrapper fadeInDown">
|
||||
<div id="formContent">
|
||||
|
||||
<!-- Icon -->
|
||||
<div class="fadeIn first">
|
||||
<h1 style="font-family: cursive; font-size: 45px; margin-top: 20px; margin-bottom: 10px">StreamNotFound</h1>
|
||||
<img src="../../../../assets/logo.png" id="icon" alt="User Icon" style="margin-top: 10px"/>
|
||||
</div>
|
||||
|
||||
<!-- Login Form -->
|
||||
<form>
|
||||
<input [(ngModel)]="email" type="text" id="email" class="fadeIn second" name="email" placeholder="Email">
|
||||
<input [(ngModel)]="password" type="password" id="password" class="fadeIn third" name="password" placeholder="Mot de passe">
|
||||
<!-- Message d'erreur -->
|
||||
<div *ngIf="hasError" style="text-align: center; margin-bottom: 20px;">
|
||||
<span class="mat-error"> {{errorMessage}} </span>
|
||||
</div>
|
||||
<input type="submit" class="fadeIn fourth" value="Se connecter" (click)="onSeConnecter()">
|
||||
</form>
|
||||
|
||||
<!-- Oubli mot de passe -->
|
||||
<div id="formFooter">
|
||||
<a class="underlineHover" (click)="onForgottenPassword()">J'ai oublié mon mot de passe</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
html {
|
||||
background-color: #56baed;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Poppins", sans-serif;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #5E89FF;
|
||||
display:inline-block;
|
||||
text-decoration: none;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
display:inline-block;
|
||||
margin: 40px 8px 10px 8px;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* STRUCTURE */
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-height: 80%;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#formContent {
|
||||
-webkit-border-radius: 10px 10px 10px 10px;
|
||||
border-radius: 10px 10px 10px 10px;
|
||||
background: #fff;
|
||||
padding: 30px;
|
||||
width: 90%;
|
||||
max-width: 450px;
|
||||
position: relative;
|
||||
padding: 0px;
|
||||
-webkit-box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3);
|
||||
box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#formFooter {
|
||||
background-color: #f6f6f6;
|
||||
border-top: 1px solid #dce8f1;
|
||||
padding: 25px;
|
||||
text-align: center;
|
||||
-webkit-border-radius: 0 0 10px 10px;
|
||||
border-radius: 0 0 10px 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* TABS */
|
||||
|
||||
h2.inactive {
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
h2.active {
|
||||
color: #0d0d0d;
|
||||
border-bottom: 2px solid #5fbae9;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* FORM TYPOGRAPHY*/
|
||||
|
||||
input[type=button], input[type=submit], input[type=reset] {
|
||||
background-color: #5E89FF;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 15px 80px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
font-size: 13px;
|
||||
-webkit-box-shadow: 0 10px 30px 0 rgba(95,186,233,0.4);
|
||||
box-shadow: 0 10px 30px 0 rgba(95,186,233,0.4);
|
||||
-webkit-border-radius: 5px 5px 5px 5px;
|
||||
border-radius: 5px 5px 5px 5px;
|
||||
margin: 5px 20px 40px 20px;
|
||||
-webkit-transition: all 0.3s ease-in-out;
|
||||
-moz-transition: all 0.3s ease-in-out;
|
||||
-ms-transition: all 0.3s ease-in-out;
|
||||
-o-transition: all 0.3s ease-in-out;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
input[type=button]:hover, input[type=submit]:hover, input[type=reset]:hover {
|
||||
background-color: #39ace7;
|
||||
}
|
||||
|
||||
input[type=button]:active, input[type=submit]:active, input[type=reset]:active {
|
||||
-moz-transform: scale(0.95);
|
||||
-webkit-transform: scale(0.95);
|
||||
-o-transform: scale(0.95);
|
||||
-ms-transform: scale(0.95);
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
input[type=text], input[type=password] {
|
||||
background-color: #f6f6f6;
|
||||
border: none;
|
||||
color: #0d0d0d;
|
||||
padding: 15px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 5px;
|
||||
width: 85%;
|
||||
border: 2px solid #f6f6f6;
|
||||
-webkit-transition: all 0.5s ease-in-out;
|
||||
-moz-transition: all 0.5s ease-in-out;
|
||||
-ms-transition: all 0.5s ease-in-out;
|
||||
-o-transition: all 0.5s ease-in-out;
|
||||
transition: all 0.5s ease-in-out;
|
||||
-webkit-border-radius: 5px 5px 5px 5px;
|
||||
border-radius: 5px 5px 5px 5px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
input[type=text]:focus, input[type=password]:focus {
|
||||
background-color: #fff;
|
||||
border-bottom: 2px solid #5fbae9;
|
||||
}
|
||||
|
||||
input[type=text]::placeholder, input[type=password]::placeholder {
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.bg{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* ANIMATIONS */
|
||||
|
||||
/* Simple CSS3 Fade-in-down Animation */
|
||||
.fadeInDown {
|
||||
-webkit-animation-name: fadeInDown;
|
||||
animation-name: fadeInDown;
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
@-webkit-keyframes fadeInDown {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInDown {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Simple CSS3 Fade-in Animation */
|
||||
@-webkit-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
||||
@-moz-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
||||
@keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
|
||||
|
||||
.fadeIn {
|
||||
opacity:0;
|
||||
-webkit-animation:fadeIn ease-in 1;
|
||||
-moz-animation:fadeIn ease-in 1;
|
||||
animation:fadeIn ease-in 1;
|
||||
|
||||
-webkit-animation-fill-mode:forwards;
|
||||
-moz-animation-fill-mode:forwards;
|
||||
animation-fill-mode:forwards;
|
||||
|
||||
-webkit-animation-duration:1s;
|
||||
-moz-animation-duration:1s;
|
||||
animation-duration:1s;
|
||||
}
|
||||
|
||||
.fadeIn.first {
|
||||
-webkit-animation-delay: 0.4s;
|
||||
-moz-animation-delay: 0.4s;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.fadeIn.second {
|
||||
-webkit-animation-delay: 0.6s;
|
||||
-moz-animation-delay: 0.6s;
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.fadeIn.third {
|
||||
-webkit-animation-delay: 0.8s;
|
||||
-moz-animation-delay: 0.8s;
|
||||
animation-delay: 0.8s;
|
||||
}
|
||||
|
||||
.fadeIn.fourth {
|
||||
-webkit-animation-delay: 1s;
|
||||
-moz-animation-delay: 1s;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
/* Simple CSS3 Fade-in Animation */
|
||||
.underlineHover:after {
|
||||
display: block;
|
||||
left: 0;
|
||||
bottom: -10px;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
//background-color: #5E89FF;
|
||||
background-color: #5E89FF;
|
||||
content: "";
|
||||
transition: width 0.2s;
|
||||
}
|
||||
|
||||
.underlineHover:hover {
|
||||
color: #0d0d0d;
|
||||
}
|
||||
|
||||
.underlineHover:hover:after{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h1{
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* OTHERS */
|
||||
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#icon {
|
||||
width:30%;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PageLoginComponent } from './page-login.component';
|
||||
|
||||
describe('PageLoginComponent', () => {
|
||||
let component: PageLoginComponent;
|
||||
let fixture: ComponentFixture<PageLoginComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PageLoginComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PageLoginComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
101
src/app/beforeConnexion/login/page-login/page-login.component.ts
Normal file
101
src/app/beforeConnexion/login/page-login/page-login.component.ts
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {Router} from "@angular/router";
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {PopupForgottenPasswordComponent} from "../popup-forgotten-password/popup-forgotten-password.component";
|
||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||
import {ProfilService} from "../../../utils/profil/profil.service";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-login',
|
||||
templateUrl: './page-login.component.html',
|
||||
styleUrls: ['./page-login.component.scss']
|
||||
})
|
||||
export class PageLoginComponent implements OnInit
|
||||
{
|
||||
email: string = "" ;
|
||||
password: string = "" ;
|
||||
hasError: boolean = false;
|
||||
errorMessage: string = "";
|
||||
|
||||
|
||||
constructor( private messageService: MessageService,
|
||||
private router: Router,
|
||||
public themeService: ThemeService,
|
||||
public dialog: MatDialog,
|
||||
private snackBar: MatSnackBar,
|
||||
private profilService: ProfilService) { }
|
||||
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
|
||||
onSeConnecter(): void
|
||||
{
|
||||
this.checkError();
|
||||
|
||||
if(!this.hasError)
|
||||
{
|
||||
let data = {
|
||||
email: this.email,
|
||||
hashPass: this.password
|
||||
};
|
||||
this.messageService
|
||||
.post('user/auth', data)
|
||||
.subscribe( retour => this.onSeConnecterCallback(retour), err => this.onSeConnecterCallback(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onSeConnecterCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.errorMessage = retour.error.reason;
|
||||
this.hasError = true;
|
||||
}
|
||||
else {
|
||||
this.profilService.setId(retour.data.id);
|
||||
this.profilService.setProfileImageUrl(retour.data.profileImageUrl);
|
||||
if(retour.data.role.name === "user") this.router.navigateByUrl( '/user/search');
|
||||
else if(retour.data.role.name === "advertiser") this.router.navigateByUrl( '/advertiser/adList');
|
||||
else if(retour.data.role.name === "admin" || retour.data.role.name === "superAdmin") this.router.navigateByUrl( '/admin/userList');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onForgottenPassword(): void
|
||||
{
|
||||
this.dialog
|
||||
.open(PopupForgottenPasswordComponent, {width: '30%'})
|
||||
.afterClosed()
|
||||
.subscribe(result => {
|
||||
if((result !== null) && (result !== undefined))
|
||||
{
|
||||
const config = { duration: 5000, panelClass: "custom-class" };
|
||||
this.snackBar.open( "Un mail de réinitialisation de mot de passe vous a été envoyé.", "", config);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
checkError(): void
|
||||
{
|
||||
if(this.email === "") {
|
||||
this.errorMessage = "Veuillez remplir le champ email" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(this.password === "") {
|
||||
this.errorMessage = "Veuillez remplir le champ mot de passe" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else {
|
||||
this.errorMessage = "" ;
|
||||
this.hasError = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<h4>Récupération du mot de passe</h4>
|
||||
|
||||
<mat-divider></mat-divider><br>
|
||||
|
||||
<!-- email -->
|
||||
<div class="myDiv">
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput type="email" [(ngModel)]="email" required>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- message d'erreur -->
|
||||
<div class="myError" *ngIf="hasError">
|
||||
<span class="mat-error">{{errorMessage}}</span>
|
||||
</div>
|
||||
|
||||
<mat-divider></mat-divider>
|
||||
|
||||
<!-- boutons -->
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="dialogRef.close()">Annuler</button>
|
||||
<button mat-button (click)="onValidate()" cdkFocusInitial>Valider</button>
|
||||
</mat-dialog-actions>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
h4 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.myDiv {
|
||||
text-align: center;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.myError {
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupForgottenPasswordComponent } from './popup-forgotten-password.component';
|
||||
|
||||
describe('PopupForgottenPasswordComponent', () => {
|
||||
let component: PopupForgottenPasswordComponent;
|
||||
let fixture: ComponentFixture<PopupForgottenPasswordComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupForgottenPasswordComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupForgottenPasswordComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import { Component } from '@angular/core';
|
||||
import {MatDialogRef} from "@angular/material/dialog";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-forgotten-password',
|
||||
templateUrl: './popup-forgotten-password.component.html',
|
||||
styleUrls: ['./popup-forgotten-password.component.scss']
|
||||
})
|
||||
export class PopupForgottenPasswordComponent
|
||||
{
|
||||
email: string;
|
||||
hasError: boolean = false;
|
||||
errorMessage: string = "";
|
||||
|
||||
|
||||
constructor(public dialogRef: MatDialogRef<PopupForgottenPasswordComponent>) {}
|
||||
|
||||
|
||||
// Click sur valider
|
||||
onValidate()
|
||||
{
|
||||
if(this.email.length === 0) {
|
||||
this.errorMessage = "Veuillez remplir le champ 'email'." ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(!this.isValidEmail(this.email)) {
|
||||
this.errorMessage = "Email invalide." ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else {
|
||||
this.errorMessage = "" ;
|
||||
this.hasError = false;
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Indique si email a bien le format d'un email
|
||||
isValidEmail(email): boolean
|
||||
{
|
||||
let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
return re.test(email);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<mat-form-field class="example-chip-list" appearance="fill">
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------ -->
|
||||
|
||||
<mat-label>Centres d'intérêt</mat-label>
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------ -->
|
||||
|
||||
<mat-chip-list #chipList aria-label="Fruit selection">
|
||||
|
||||
<mat-chip
|
||||
*ngFor="let interest of myInterests"
|
||||
[selectable]="selectable"
|
||||
[removable]="removable"
|
||||
(removed)="remove(interest)">
|
||||
{{interest}}
|
||||
<button matChipRemove *ngIf="removable">
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
</mat-chip>
|
||||
|
||||
<input
|
||||
placeholder="Tapez un centre d'intérêt et pressez 'Entrer' pour l'inserer"
|
||||
#tagInput
|
||||
[formControl]="formControl"
|
||||
[matAutocomplete]="auto"
|
||||
[matChipInputFor]="chipList"
|
||||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||
(matChipInputTokenEnd)="add($event)">
|
||||
|
||||
</mat-chip-list>
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------ -->
|
||||
|
||||
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
|
||||
<mat-option *ngFor="let interest of filteredInterests | async" [value]="interest">
|
||||
{{interest}}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
|
||||
<!-- ------------------------------------------------------------------------------------ -->
|
||||
|
||||
</mat-form-field>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { InputInterestsRegisterComponent } from './input-interests-register.component';
|
||||
|
||||
describe('InputInterestsRegisterComponent', () => {
|
||||
let component: InputInterestsRegisterComponent;
|
||||
let fixture: ComponentFixture<InputInterestsRegisterComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ InputInterestsRegisterComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(InputInterestsRegisterComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
|
||||
import {COMMA, ENTER} from "@angular/cdk/keycodes";
|
||||
import {FormControl} from "@angular/forms";
|
||||
import {Observable} from "rxjs";
|
||||
import {map, startWith} from "rxjs/operators";
|
||||
import {MatChipInputEvent} from "@angular/material/chips";
|
||||
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-input-interests-register',
|
||||
templateUrl: './input-interests-register.component.html',
|
||||
styleUrls: ['./input-interests-register.component.scss']
|
||||
})
|
||||
export class InputInterestsRegisterComponent implements OnInit
|
||||
{
|
||||
selectable = true;
|
||||
removable = true;
|
||||
separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||
formControl = new FormControl();
|
||||
filteredInterests: Observable<string[]>;
|
||||
@Input() myInterests: string[] = [];
|
||||
allInterests: string[] = [];
|
||||
@Output() eventEmitter = new EventEmitter<string[]>();
|
||||
@ViewChild('tagInput') tagInput: ElementRef<HTMLInputElement>;
|
||||
interestsNotSelected: string[] = [];
|
||||
|
||||
|
||||
constructor( private messageService: MessageService ) {}
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.filteredInterests = this.formControl.valueChanges.pipe(
|
||||
startWith(null),
|
||||
map((fruit: string | null) => fruit ? this._filter(fruit) : this.interestsNotSelected.slice()));
|
||||
|
||||
this.messageService
|
||||
.get("misc/getInterests")
|
||||
.subscribe( retour => {
|
||||
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.allInterests = [];
|
||||
for(let elt of retour.data)
|
||||
{
|
||||
this.allInterests.push(elt.interest);
|
||||
this.interestsNotSelected.push(elt.interest);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
add(event: MatChipInputEvent): void
|
||||
{
|
||||
const value = (event.value || '').trim();
|
||||
const index = this.interestsNotSelected.indexOf(value);
|
||||
if (value && (index !== -1) && (!this.myInterests.includes(value)))
|
||||
{
|
||||
this.myInterests.push(value);
|
||||
event.chipInput!.clear();
|
||||
this.formControl.setValue(null);
|
||||
this.eventEmitter.emit(this.myInterests);
|
||||
this.interestsNotSelected.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
remove(interest: string): void
|
||||
{
|
||||
// supprimer 'interest' de 'myInterest'
|
||||
const index = this.myInterests.indexOf(interest);
|
||||
if (index >= 0) this.myInterests.splice(index, 1);
|
||||
this.eventEmitter.emit(this.myInterests);
|
||||
|
||||
// remmettre 'interest' dans 'interestsNotSelected'
|
||||
if(!this.interestsNotSelected.includes(interest))
|
||||
{
|
||||
const indexOfAutres = this.interestsNotSelected.indexOf("Autres");
|
||||
if(indexOfAutres !== -1)
|
||||
{
|
||||
this.interestsNotSelected.splice(indexOfAutres, 1);
|
||||
if(interest !== "Autres") this.interestsNotSelected.push(interest);
|
||||
this.interestsNotSelected.sort();
|
||||
this.interestsNotSelected.push("Autres");
|
||||
}
|
||||
else {
|
||||
this.interestsNotSelected.push(interest);
|
||||
if(interest !== "Autres") this.interestsNotSelected.sort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
selected(event: MatAutocompleteSelectedEvent): void
|
||||
{
|
||||
const value = event.option.viewValue;
|
||||
if(!this.myInterests.includes(value))
|
||||
{
|
||||
this.myInterests.push(value);
|
||||
const index = this.interestsNotSelected.indexOf(value);
|
||||
this.interestsNotSelected.splice(index, 1);
|
||||
}
|
||||
this.tagInput.nativeElement.value = '';
|
||||
this.formControl.setValue(null);
|
||||
this.eventEmitter.emit(this.myInterests);
|
||||
}
|
||||
|
||||
|
||||
private _filter(value: string): string[]
|
||||
{
|
||||
const filterValue = value.toLowerCase();
|
||||
return this.interestsNotSelected.filter(fruit => fruit.toLowerCase().includes(filterValue));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
<!-- navbar -->
|
||||
<app-navbar-before-connexion pour="register"></app-navbar-before-connexion>
|
||||
|
||||
<!-- stepper -->
|
||||
<mat-stepper [linear]=true>
|
||||
|
||||
<!-- Choix du role -->
|
||||
<mat-step label="Type de compte">
|
||||
<form style="margin-top: 10px">
|
||||
<!-- Choix du rôle -->
|
||||
<mat-radio-group [(ngModel)]="user.role.name" [ngModelOptions]="{standalone: true}" (click)="hasError = false; errorMessage = '';">
|
||||
<mat-radio-button value="user">Utilisateur standard</mat-radio-button>
|
||||
<mat-radio-button value="advertiser">Annonceur</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
|
||||
<!-- Bouton suivant -->
|
||||
<div style="text-align: right;">
|
||||
<button mat-button matStepperNext>Suivant</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
|
||||
|
||||
<!-- Infos personelles -->
|
||||
<mat-step label="Compte & Informations personelles">
|
||||
<form style="margin-top: 10px">
|
||||
<!-- Redirection vers le bon bloc -->
|
||||
<ng-template *ngIf="user.role.name==='user'; then userBlock else advertiserBlock"></ng-template>
|
||||
|
||||
<!-- Message d'erreur -->
|
||||
<div *ngIf="hasError">
|
||||
<mat-error>{{errorMessage}}</mat-error>
|
||||
</div>
|
||||
|
||||
<!-- Boutons-->
|
||||
<div style="text-align: right;">
|
||||
<button mat-button matStepperPrevious>Précédent</button>
|
||||
<button mat-button (click)="onEnregistrer()">Enregistrer</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-step>
|
||||
|
||||
</mat-stepper>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- -------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<!-- Info personnelles user -->
|
||||
<ng-template #userBlock>
|
||||
<br>
|
||||
<div class="row">
|
||||
|
||||
<!-- Colonne gauche -->
|
||||
<div class="col-5 leftCol">
|
||||
<h4>Compte</h4>
|
||||
|
||||
<!-- Login -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Pseudo</mat-label>
|
||||
<input matInput type="text" [(ngModel)]="user.login" [ngModelOptions]="{standalone: true}" required>
|
||||
</mat-form-field>
|
||||
<br>
|
||||
|
||||
<!-- Mot de passe -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Mot de passe</mat-label>
|
||||
<input matInput type="password" [(ngModel)]="password" [ngModelOptions]="{standalone: true}" required>
|
||||
</mat-form-field>
|
||||
<br>
|
||||
|
||||
<!-- Confirmation mot de passe -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Confirmation mot de passe</mat-label>
|
||||
<input matInput type="password" [(ngModel)]="confirmPassword" [ngModelOptions]="{standalone: true}" required>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- Colonne droite -->
|
||||
<div class="col-7">
|
||||
<h4>Informations personelles</h4>
|
||||
|
||||
<!-- Email -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput type="email" [(ngModel)]="user.email" [ngModelOptions]="{standalone: true}" required>
|
||||
</mat-form-field>
|
||||
<br>
|
||||
|
||||
<!-- gender -->
|
||||
<mat-radio-group [(ngModel)]="user.gender" [ngModelOptions]="{standalone: true}">
|
||||
<mat-radio-button value="man"> Homme </mat-radio-button>
|
||||
<mat-radio-button value="woman"> Femme </mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<br><br>
|
||||
|
||||
<!-- dateOfBirth -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Date de naissance</mat-label>
|
||||
<input matInput type="date"
|
||||
[ngModel] ="user.dateOfBirth | date:'yyyy-MM-dd'"
|
||||
(ngModelChange)="user.dateOfBirth = $event"
|
||||
[ngModelOptions]="{standalone: true}">
|
||||
</mat-form-field>
|
||||
|
||||
<!-- interests -->
|
||||
<app-input-interests-register
|
||||
[myInterests]="user.interests"
|
||||
(eventEmitter)="onEventInputInterests($event)"></app-input-interests-register>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
|
||||
<!-- -------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<!-- Info personnelles advertiser -->
|
||||
<ng-template #advertiserBlock>
|
||||
|
||||
<!-- Entreprise -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Entreprise</mat-label>
|
||||
<input matInput type="text" [(ngModel)]="user.company" [ngModelOptions]="{standalone: true}" required>
|
||||
</mat-form-field>
|
||||
<br>
|
||||
|
||||
<!-- Login -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Pseudo</mat-label>
|
||||
<input matInput type="text" [(ngModel)]="user.login" [ngModelOptions]="{standalone: true}" required>
|
||||
</mat-form-field>
|
||||
<br>
|
||||
|
||||
<!-- Email -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Email</mat-label>
|
||||
<input matInput type="email" [(ngModel)]="user.email" [ngModelOptions]="{standalone: true}" required>
|
||||
</mat-form-field>
|
||||
<br>
|
||||
|
||||
<!-- Mot de passe -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Mot de passe</mat-label>
|
||||
<input matInput type="password" [(ngModel)]="password" [ngModelOptions]="{standalone: true}" required>
|
||||
</mat-form-field>
|
||||
<br>
|
||||
|
||||
<!-- Confirmation mot de passe -->
|
||||
<mat-form-field appearance="fill">
|
||||
<mat-label>Confirmation mot de passe</mat-label>
|
||||
<input matInput type="password" [(ngModel)]="confirmPassword" [ngModelOptions]="{standalone: true}" required>
|
||||
</mat-form-field>
|
||||
|
||||
</ng-template>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
.myContainer {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
|
||||
mat-stepper {
|
||||
width: 60%;
|
||||
margin: 10vh auto;
|
||||
border: solid 1px black;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
|
||||
.leftCol {
|
||||
border-right: solid 1px #dcdcdc;
|
||||
}
|
||||
|
||||
|
||||
.myRow {
|
||||
margin: 15px 0px 15px 0px;
|
||||
}
|
||||
.myLabel {
|
||||
text-align: right;
|
||||
padding: 0px 5px 0px 0px;
|
||||
margin: 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.myValue {
|
||||
text-align: left;
|
||||
padding: 0px 0px 0px 5px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
||||
::ng-deep .mat-radio-inner-circle {
|
||||
color: black !important;
|
||||
background-color: black !important;
|
||||
}
|
||||
|
||||
::ng-deep .mat-radio-outer-circle{
|
||||
color: black !important;
|
||||
border: solid 1px gray !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PageRegisterComponent } from './page-register.component';
|
||||
|
||||
describe('PageRegisterComponent', () => {
|
||||
let component: PageRegisterComponent;
|
||||
let fixture: ComponentFixture<PageRegisterComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PageRegisterComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PageRegisterComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
import { Component } from '@angular/core';
|
||||
import {PopupConfirmationComponent} from "../popup-confirmation/popup-confirmation.component";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {Router} from "@angular/router";
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-register',
|
||||
templateUrl: './page-register.component.html',
|
||||
styleUrls: ['./page-register.component.scss']
|
||||
})
|
||||
export class PageRegisterComponent
|
||||
{
|
||||
password: string = "";
|
||||
confirmPassword: string = "";
|
||||
hasError: boolean = false;
|
||||
errorMessage: string = "";
|
||||
user = {
|
||||
_id: "",
|
||||
login: "",
|
||||
hashPass: "",
|
||||
email: "",
|
||||
role: {
|
||||
name: "user",
|
||||
permission: 0,
|
||||
isAccepted: false,
|
||||
},
|
||||
profileImageUrl: "",
|
||||
dateOfBirth: null,
|
||||
gender: "man",
|
||||
interests: [],
|
||||
company: "",
|
||||
isActive: true,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
lastConnexion: null
|
||||
};
|
||||
|
||||
|
||||
constructor( private messageService: MessageService,
|
||||
private router: Router,
|
||||
public dialog: MatDialog,
|
||||
public themeService: ThemeService ) { }
|
||||
|
||||
|
||||
// Envoie de l'utilisateur au backend
|
||||
onEnregistrer(): void
|
||||
{
|
||||
this.checkField();
|
||||
if(!this.hasError)
|
||||
{
|
||||
let data: any = Object.assign({}, this.user);
|
||||
if(this.user.role.name === "user") data.role = "user" ;
|
||||
else data.role = "advertiser";
|
||||
data.hashPass = this.password;
|
||||
this.messageService
|
||||
.post('user/create', data)
|
||||
.subscribe(retour => this.onEnregistrerCallback(retour), err => this.onEnregistrerCallback(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Gestion de la réponse du backend
|
||||
onEnregistrerCallback(retour): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else
|
||||
{
|
||||
const config = {
|
||||
width: '25%',
|
||||
data: {roleName: this.user.role.name}
|
||||
};
|
||||
this.dialog
|
||||
.open(PopupConfirmationComponent, config)
|
||||
.afterClosed()
|
||||
.subscribe(result => this.router.navigateByUrl( '/login' ));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check les champs saisies par l'utilisateur
|
||||
checkField(): void
|
||||
{
|
||||
if((this.user.role.name === 'advertiser') && (this.user.company.length === 0)) {
|
||||
this.errorMessage = "Veuillez remplir le champ 'entreprise'.";
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(this.user.login.length === 0) {
|
||||
this.errorMessage = "Veuillez remplir le champ 'pseudo'.";
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(this.user.email.length === 0) {
|
||||
this.errorMessage = "Veuillez remplir le champ 'email'.";
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(!this.isValidEmail(this.user.email)) {
|
||||
this.errorMessage = "Email invalide.";
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(this.password.length === 0) {
|
||||
this.errorMessage = "Veuillez remplir le champ 'mot de passe'.";
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(this.password !== this.confirmPassword) {
|
||||
this.errorMessage = "Le mot de passe est différent de sa confirmation.";
|
||||
this.hasError = true;
|
||||
}
|
||||
else {
|
||||
this.errorMessage = "" ;
|
||||
this.hasError = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Indique si email a bien le format d'un email
|
||||
isValidEmail(email): boolean
|
||||
{
|
||||
let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
return re.test(email);
|
||||
}
|
||||
|
||||
|
||||
// Récupère la liste des centres d'intérets (car celle-ci est remplie à l'aide d'un component intermédiaire)
|
||||
onEventInputInterests(myInterets: string[]): void
|
||||
{
|
||||
this.user.interests = myInterets;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<p *ngIf="data.roleName === 'user'">
|
||||
Votre inscription a bien été effectuée.
|
||||
</p>
|
||||
|
||||
<p *ngIf="data.roleName === 'advertiser'">
|
||||
Votre inscription est en cours de validation.
|
||||
</p>
|
||||
|
||||
<div style="text-align: right">
|
||||
<button mat-button mat-dialog-close> Continuer </button>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
p {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
div {
|
||||
font-size: small;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupConfirmationComponent } from './popup-confirmation.component';
|
||||
|
||||
describe('PopupConfirmationComponent', () => {
|
||||
let component: PopupConfirmationComponent;
|
||||
let fixture: ComponentFixture<PopupConfirmationComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupConfirmationComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupConfirmationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import {Component, Inject} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-confirmation',
|
||||
templateUrl: './popup-confirmation.component.html',
|
||||
styleUrls: ['./popup-confirmation.component.scss']
|
||||
})
|
||||
export class PopupConfirmationComponent
|
||||
{
|
||||
constructor( public dialogRef: MatDialogRef<PopupConfirmationComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<!-- Login -->
|
||||
<div *ngIf="pour === 'login'">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
|
||||
<!-- PolyNotFound -->
|
||||
<a class="navbar-brand" routerLink=""> StreamNotFound </a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<!-- Rien -->
|
||||
<div class="collapse navbar-collapse"></div>
|
||||
|
||||
<!-- S'inscrire -->
|
||||
<button mat-button class="btnDeconnexion" routerLink="/register">
|
||||
S'inscrire
|
||||
</button>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- --------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<!-- Register -->
|
||||
<div *ngIf="pour === 'register'">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
|
||||
<!-- PolyNotFound -->
|
||||
<a class="navbar-brand" routerLink=""> StreamNotFound </a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<!-- Rien -->
|
||||
<div class="collapse navbar-collapse"></div>
|
||||
|
||||
</nav>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
.navbar {
|
||||
background-color: black;
|
||||
height: 60px;
|
||||
font-size: medium;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
.navbar-expand-lg {
|
||||
border-bottom: solid;
|
||||
border-color: white;
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
|
||||
|
||||
// PolyNotFound
|
||||
.navbar-brand {
|
||||
font-family: cursive;
|
||||
font-weight: bold;
|
||||
font-size: x-large;
|
||||
margin-left: 15px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
// Recherche, Mes Playlists, Historique
|
||||
.nav-link {
|
||||
color: white;
|
||||
}
|
||||
.nav-link:hover {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
|
||||
// Bonton deconnexion
|
||||
.btnDeconnexion {
|
||||
font-size: medium;
|
||||
margin: 0px 10px 0px 10px
|
||||
}
|
||||
.btnDeconnexion:hover {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
|
||||
.monLi {
|
||||
margin: 0px 10px 0px 10px;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
border: solid 2px white;
|
||||
border-radius: 50px;
|
||||
margin: 0px 10px 0px 15px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
img:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
|
||||
::ng-deep .mat-slide-toggle-thumb {
|
||||
background-color: #c8c8c8;
|
||||
}
|
||||
|
||||
::ng-deep .mat-slide-toggle-bar {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-thumb {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
::ng-deep .mat-slide-toggle.mat-checked:not(.mat-disabled) .mat-slide-toggle-bar {
|
||||
background-color: #646464;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { NavbarBeforeConnexionComponent } from './navbar-before-connexion.component';
|
||||
|
||||
describe('NavbarBeforeConnexionComponent', () => {
|
||||
let component: NavbarBeforeConnexionComponent;
|
||||
let fixture: ComponentFixture<NavbarBeforeConnexionComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ NavbarBeforeConnexionComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NavbarBeforeConnexionComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import {Component, Input} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-navbar-before-connexion',
|
||||
templateUrl: './navbar-before-connexion.component.html',
|
||||
styleUrls: ['./navbar-before-connexion.component.scss']
|
||||
})
|
||||
export class NavbarBeforeConnexionComponent
|
||||
{
|
||||
@Input() pour = "login";
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
|
||||
<app-navbar-user></app-navbar-user><br><br>
|
||||
|
||||
<!-- ---------------------------------------------------------------------------------- -->
|
||||
|
||||
<div style="text-align: center">
|
||||
<input (keyup)="applyFilter($event)" placeholder="Rechercher par mots clés...">
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<!-- ---------------------------------------------------------------------------------- -->
|
||||
|
||||
<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">
|
||||
<!-- Aperçu Column -->
|
||||
<ng-container matColumnDef="aperçu">
|
||||
<th mat-header-cell *matHeaderCellDef> Aperçu </th>
|
||||
<td mat-cell *matCellDef="let video">
|
||||
<div class="imgsContainer" (click)="onVideo(video)">
|
||||
<img class="imgPlay" src="/assets/play.png">
|
||||
<img class="imgVideo" [src]="video.imageUrl">
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Titre Column -->
|
||||
<ng-container matColumnDef="title">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Titre </th>
|
||||
<td mat-cell *matCellDef="let video"> {{video.title}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Date Column -->
|
||||
<ng-container matColumnDef="date">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Date </th>
|
||||
<td mat-cell *matCellDef="let video">
|
||||
{{video.date | date:'dd/LL/YYYY à HH:mm:ss'}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Source Column -->
|
||||
<ng-container matColumnDef="source">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Source </th>
|
||||
<td mat-cell *matCellDef="let video"> {{video.source}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Action Column -->
|
||||
<ng-container matColumnDef="action">
|
||||
<th mat-header-cell *matHeaderCellDef> Action </th>
|
||||
<td mat-cell *matCellDef="let video">
|
||||
<button mat-icon-button (click)="onDelete(video)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
<tr class="mat-row" *matNoDataRow>
|
||||
<td class="mat-cell" colspan="4"> Aucune vidéo ne correspond au filtre: "{{input.value}}" </td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="width: 80%; margin: auto auto">
|
||||
<mat-paginator [pageSizeOptions]="[5, 10, 20]" showFirstLastButtons aria-label="Select page of periodic elements"></mat-paginator>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
.myContainer {
|
||||
max-width: 100vw;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
|
||||
th.mat-sort-header-sorted {
|
||||
color: black;
|
||||
}
|
||||
|
||||
|
||||
input {
|
||||
width: 35%;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------
|
||||
|
||||
.imgsContainer {
|
||||
position: relative;
|
||||
width: 20vw;
|
||||
height: 15vh;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.imgPlay {
|
||||
position: absolute;
|
||||
margin-left: 9vw;
|
||||
width: 3vw;
|
||||
margin-top: 5vh;
|
||||
height: 6vh;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
.imgVideo {
|
||||
border: solid 1px black;
|
||||
width: 20vw;
|
||||
height: 15vh;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PageHistoryUserComponent } from './page-history-user.component';
|
||||
|
||||
describe('PageHistoriqueComponent', () => {
|
||||
let component: PageHistoryUserComponent;
|
||||
let fixture: ComponentFixture<PageHistoryUserComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PageHistoryUserComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PageHistoryUserComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
import {AfterViewInit, Component, ViewChild} from '@angular/core';
|
||||
import {MatTableDataSource} from "@angular/material/table";
|
||||
import {MatSort} from "@angular/material/sort";
|
||||
import {MatPaginator} from "@angular/material/paginator";
|
||||
import {Router} from "@angular/router";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-history-user',
|
||||
templateUrl: './page-history-user.component.html',
|
||||
styleUrls: ['./page-history-user.component.scss']
|
||||
})
|
||||
export class PageHistoryUserComponent implements AfterViewInit
|
||||
{
|
||||
displayedColumns: string[] = [ 'aperçu', 'title', 'date', 'source', 'action' ];
|
||||
dataSource ;
|
||||
@ViewChild(MatSort) sort: MatSort;
|
||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||
|
||||
|
||||
constructor( public themeService: ThemeService,
|
||||
private messageService: MessageService,
|
||||
private router: Router ) { }
|
||||
|
||||
|
||||
// charge la page
|
||||
ngAfterViewInit(): void
|
||||
{
|
||||
this.messageService
|
||||
.get("user/history")
|
||||
.subscribe(ret => this.ngAfterViewInitCallback(ret), err => this.ngAfterViewInitCallback(err));
|
||||
}
|
||||
|
||||
|
||||
ngAfterViewInitCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
const tabVideoHistory = retour.data.map( video => {
|
||||
return {
|
||||
_id: video._id,
|
||||
videoId: video.videoId,
|
||||
imageUrl: video.imageUrl,
|
||||
title: video.title,
|
||||
date: video.watchedDate,
|
||||
source: video.source,
|
||||
}
|
||||
});
|
||||
this.dataSource = new MatTableDataSource(tabVideoHistory);
|
||||
this.dataSource.sort = this.sort;
|
||||
this.dataSource.paginator = this.paginator;
|
||||
this.dataSource = this.dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Applique le filtre
|
||||
applyFilter(event: Event): void
|
||||
{
|
||||
const filterValue = (event.target as HTMLInputElement).value;
|
||||
this.dataSource.filter = filterValue.trim().toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
// Supprime la video
|
||||
onDelete(video: any): void
|
||||
{
|
||||
this.messageService
|
||||
.put("video/update/"+video._id, { watchedDates: []})
|
||||
.subscribe(ret => this.onDeleteCallback(ret, video), err => this.onDeleteCallback(err, video))
|
||||
}
|
||||
|
||||
|
||||
onDeleteCallback(retour: any, video: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
const index = this.dataSource.data.indexOf(video);
|
||||
this.dataSource.data.splice(index, 1);
|
||||
this.dataSource.data = this.dataSource.data;
|
||||
this.dataSource = this.dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onVideo(video): void
|
||||
{
|
||||
this.messageService
|
||||
.put("video/update/"+video._id, {watchedDate: true})
|
||||
.subscribe(ret => this.onVideoCallback(ret), err => this.onVideoCallback(err));
|
||||
|
||||
const params = {
|
||||
videoId: video.videoId,
|
||||
source: video.source,
|
||||
from: "history",
|
||||
};
|
||||
this.router.navigate(['/user/watching'], { queryParams: params });
|
||||
}
|
||||
|
||||
|
||||
onVideoCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") console.log(retour);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
<!-- Navbar -->
|
||||
<div>
|
||||
<app-navbar-user></app-navbar-user>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<!-- --------------------------------------------------------------------- -->
|
||||
|
||||
<!-- [liste des videos] + [liste des playlist] + [pub] -->
|
||||
<mat-grid-list cols="10" rowHeight="85vh">
|
||||
|
||||
<!-- liste des videos -->
|
||||
<mat-grid-tile colspan="4" rowspan="1" class="celluleListeVideo">
|
||||
<app-video-list [playlist]="playlist"></app-video-list>
|
||||
</mat-grid-tile>
|
||||
|
||||
<!-- liste des playlist -->
|
||||
<mat-grid-tile colspan="4" rowspan="1" class="celluleListePlaylist">
|
||||
<app-playlist-list (eventEmitter)="transmitPlaylistToVideoList($event)"></app-playlist-list>
|
||||
</mat-grid-tile>
|
||||
|
||||
<!-- pub -->
|
||||
<mat-grid-tile colspan="2" rowspan="1" class="cellulePub">
|
||||
<div class="conteneurPub">
|
||||
<app-advert [ad]="ad" from="myPlaylists"></app-advert>
|
||||
</div>
|
||||
</mat-grid-tile>
|
||||
|
||||
</mat-grid-list>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
.lightTheme {
|
||||
border-color: black;
|
||||
}
|
||||
.darkTheme {
|
||||
border-color: white;
|
||||
}
|
||||
.myContainer {
|
||||
text-align: center;
|
||||
max-width: 100vw;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
// Liste des vidéos -------------------------------------------------
|
||||
|
||||
.celluleListeVideo {
|
||||
margin: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
// Liste des playlists ---------------------------------------------
|
||||
|
||||
.celluleListePlaylist {
|
||||
margin: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
// Pub -------------------------------------------------------------
|
||||
|
||||
.cellulePub {
|
||||
padding: 0px 10px 0px 10px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.conteneurPub {
|
||||
//height: 85vh;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
vertical-align: middle;
|
||||
display: block;
|
||||
width: 75%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PageMyPlaylistsComponent } from './page-my-playlists.component';
|
||||
|
||||
describe('PageMesPlaylistsComponent', () => {
|
||||
let component: PageMyPlaylistsComponent;
|
||||
let fixture: ComponentFixture<PageMyPlaylistsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PageMyPlaylistsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PageMyPlaylistsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {HttpParams} from "@angular/common/http";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-page-my-playlists',
|
||||
templateUrl: './page-my-playlists.component.html',
|
||||
styleUrls: ['./page-my-playlists.component.scss']
|
||||
})
|
||||
export class PageMyPlaylistsComponent implements OnInit
|
||||
{
|
||||
ad; // pub
|
||||
playlist: any; // la playlist sélectionnée
|
||||
|
||||
|
||||
constructor( public themeService: ThemeService,
|
||||
private messageService: MessageService ) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
let params = new HttpParams();
|
||||
params = params.append("quantity", 1);
|
||||
this.messageService
|
||||
.get("user/ad", params)
|
||||
.subscribe(ret => this.adCallback(ret), err => this.adCallback(err));
|
||||
}
|
||||
|
||||
|
||||
adCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.ad = retour.data[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
transmitPlaylistToVideoList(playlist): void
|
||||
{
|
||||
if ((playlist === null) || (playlist === undefined)) {
|
||||
this.playlist = playlist;
|
||||
}
|
||||
else {
|
||||
this.messageService
|
||||
.get("playlist/findOne/" + playlist.id)
|
||||
.subscribe(ret => this.afterReceivingPlaylistWithVideo(ret, playlist), err => this.afterReceivingPlaylistWithVideo(err, playlist));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
afterReceivingPlaylistWithVideo(retour: any, playlist): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.playlist = playlist;
|
||||
}
|
||||
else {
|
||||
this.playlist = retour.data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
<!-- Search bar -->
|
||||
<div class="row searchBarContainer">
|
||||
<div style="text-align: center">
|
||||
<input type="search" placeholder="Rechercher..." class="inputSearchBar" [(ngModel)]="search" (ngModelChange)="whileSearch()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Liste des playlist -->
|
||||
<div class="row" style="margin: 0px">
|
||||
<div class="playlistListContainer">
|
||||
<div *ngFor="let playlist of tabPlaylist" class="playlistContainer">
|
||||
|
||||
<div [class]="getClassOfPlaylistContainer(playlist)" (click)="eventEmitter.emit(playlist); playlistFocusedOn = playlist">
|
||||
<div class="col-2" style="text-align: left">
|
||||
<button mat-icon-button (click)="onUpdatePlaylist(playlist)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-8" style="text-align: center">
|
||||
<span class="playlistName"> {{playlist.name}} </span><br>
|
||||
<span class="playListCount" *ngIf="playlist.videoIds.length <= 1"> {{playlist.videoIds.length}} vidéo </span>
|
||||
<span class="playListCount" *ngIf="playlist.videoIds.length > 1"> {{playlist.videoIds.length}} vidéos </span>
|
||||
</div>
|
||||
<div class="col-2" style="text-align: right">
|
||||
<button mat-icon-button (click)="onDeletePlaylist(playlist)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Bouton creer playlist-->
|
||||
<div class="row btnCreerPlaylistContainer" >
|
||||
<button mat-button class="btnCreerPlaylist" (click)="onCreatePlaylist()"> Créer une playlist </button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
.myContainer {
|
||||
background-color: white ;
|
||||
text-align: center;
|
||||
width: 35vw;
|
||||
margin: 1vh 0vh 3vh 0vh;
|
||||
padding: 0px;
|
||||
border: solid 2px black;
|
||||
border-radius: 10px;
|
||||
box-shadow: 10px 5px 5px black;
|
||||
}
|
||||
|
||||
// SearchBar -----------------------------------------------------------
|
||||
|
||||
.searchBarContainer {
|
||||
text-align: center;
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding: 10px 0px 10px 0px;
|
||||
//background-color: #dcdcdc;
|
||||
background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
|
||||
background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
|
||||
font-size: large;
|
||||
border-bottom: solid 1px black;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
|
||||
.inputSearchBar {
|
||||
width: 70%;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
|
||||
// Liste des playlists -------------------------------------------------
|
||||
|
||||
.playlistListContainer {
|
||||
max-width: 100%;
|
||||
height: 60vh;
|
||||
overflow-y: scroll;
|
||||
padding: 0px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.playlistContainer {
|
||||
max-width: 100%;
|
||||
padding: 0px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
|
||||
.btnPlaylist {
|
||||
background-color: white;
|
||||
padding: 20px;
|
||||
border-bottom: solid 1px black;
|
||||
//width: 100%;
|
||||
width: 35vw;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.btnPlaylist:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.btnPlaylistFocus {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
.btnPlaylistFocus:hover {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
|
||||
.playListCount {
|
||||
color: gray;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
// Bouton creer playlist -------------------------------------------------
|
||||
|
||||
.btnCreerPlaylistContainer {
|
||||
margin: 0px 0px 0px 0px;
|
||||
background-color: #dcdcdc;
|
||||
font-size: large;
|
||||
border-top: solid 1px black;
|
||||
border-bottom-left-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
|
||||
.btnCreerPlaylist {
|
||||
margin: 0px 0px 0px 0px;
|
||||
border-bottom-left-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
|
||||
background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
|
||||
//background: linear-gradient(180deg, #e6e6e6 0%, rgba(0,0,0,0.25) 49%, rgba(38,38,38,0.6) 51%, rgba(0,0,0,0.25) 100%);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PlaylistListComponent } from './playlist-list.component';
|
||||
|
||||
describe('PlaylistListComponent', () => {
|
||||
let component: PlaylistListComponent;
|
||||
let fixture: ComponentFixture<PlaylistListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PlaylistListComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PlaylistListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {MatDialog} from "@angular/material/dialog";
|
||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||
import {PopupCreateOrUpdatePlaylistComponent} from "../popup-create-or-update-playlist/popup-create-or-update-playlist.component";
|
||||
import {PopupDeletePlaylistComponent} from "../popup-delete-playlist/popup-delete-playlist.component";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-playlist-list',
|
||||
templateUrl: './playlist-list.component.html',
|
||||
styleUrls: ['./playlist-list.component.scss']
|
||||
})
|
||||
export class PlaylistListComponent implements OnInit
|
||||
{
|
||||
allPlaylists: any[] = []; // toutes les playlists
|
||||
@Output() eventEmitter = new EventEmitter<any>(); // pour envoyer au parent la playlist selectionner
|
||||
search: string = "" ; // contenu de la barre de recherche
|
||||
tabPlaylist: any[] = []; // playlist affichées
|
||||
playlistFocusedOn: any;
|
||||
|
||||
|
||||
constructor( public themeService: ThemeService,
|
||||
public dialog: MatDialog,
|
||||
public snackBar: MatSnackBar,
|
||||
private messageService: MessageService ) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.messageService
|
||||
.get("playlist/findAll")
|
||||
.subscribe( retour => this.ngOnInitCallback(retour), err => this.ngOnInitCallback(err) );
|
||||
}
|
||||
|
||||
|
||||
ngOnInitCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
} else {
|
||||
const aux = retour.data.filter( x => x.isActive === true);
|
||||
this.allPlaylists = aux.map(x => {
|
||||
x["_id"] = x.id ;
|
||||
return x;
|
||||
});
|
||||
this.tabPlaylist = [].concat(this.allPlaylists);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// s'execute lorsqu'on écrit sur la barre de recherche
|
||||
whileSearch()
|
||||
{
|
||||
this.tabPlaylist = [];
|
||||
for(let playlist of this.allPlaylists)
|
||||
{
|
||||
if(playlist.name.includes(this.search)) this.tabPlaylist.push(playlist);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// click sur créer playlist
|
||||
onCreatePlaylist(): void
|
||||
{
|
||||
const config = {
|
||||
data: {
|
||||
action: "create",
|
||||
tabPlaylist: this.tabPlaylist,
|
||||
}
|
||||
};
|
||||
this.dialog
|
||||
.open(PopupCreateOrUpdatePlaylistComponent, config )
|
||||
.afterClosed()
|
||||
.subscribe(playlist => {
|
||||
|
||||
const config = { duration: 1500, panelClass: "custom-class" };
|
||||
if((playlist === null) || (playlist === undefined)) {
|
||||
this.snackBar.open("Opération annulée", "", config);
|
||||
}
|
||||
else {
|
||||
playlist["_id"] = playlist.id;
|
||||
this.allPlaylists.push(playlist);
|
||||
this.tabPlaylist.push(playlist);
|
||||
this.snackBar.open(`La playlist '${playlist.name}' a bien été créée ✔`, "", config);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// click sur update playlist
|
||||
onUpdatePlaylist(playlistToUpdate): void
|
||||
{
|
||||
const config = {
|
||||
data: {
|
||||
action: "update",
|
||||
tabPlaylist: this.tabPlaylist,
|
||||
playlistName: playlistToUpdate.name,
|
||||
playlistId: playlistToUpdate._id
|
||||
}
|
||||
};
|
||||
this.dialog
|
||||
.open(PopupCreateOrUpdatePlaylistComponent, config)
|
||||
.afterClosed()
|
||||
.subscribe(newName => {
|
||||
|
||||
const config = { duration: 1500, panelClass: "custom-class" };
|
||||
if((newName === null) || (newName === undefined)) {
|
||||
this.snackBar.open("Opération annulée", "", config);
|
||||
}
|
||||
else {
|
||||
let index = this.allPlaylists.findIndex( elt => (elt._id === playlistToUpdate._id));
|
||||
this.allPlaylists[index].name = newName;
|
||||
index = this.tabPlaylist.findIndex( elt => (elt._id === playlistToUpdate._id));
|
||||
this.tabPlaylist[index].name = newName;
|
||||
this.snackBar.open(`La playlist '${playlistToUpdate.name}' a bien été mise à jour ✔`, "", config);
|
||||
this.eventEmitter.emit(this.tabPlaylist[index]);
|
||||
this.playlistFocusedOn = this.tabPlaylist[index]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// click sur supprimer playlist
|
||||
onDeletePlaylist(playlist): void
|
||||
{
|
||||
const config = {data: playlist};
|
||||
this.dialog
|
||||
.open(PopupDeletePlaylistComponent, config)
|
||||
.afterClosed()
|
||||
.subscribe(retour => {
|
||||
|
||||
const config = { duration: 1500, panelClass: "custom-class" };
|
||||
if((retour === null) || (retour === undefined)) {
|
||||
this.snackBar.open("Opération annulée", "", config);
|
||||
}
|
||||
else {
|
||||
let index = this.allPlaylists.indexOf(playlist);
|
||||
if(index >= 0) this.allPlaylists.splice(index, 1);
|
||||
|
||||
index = this.tabPlaylist.indexOf(playlist);
|
||||
if(index >= 0) this.tabPlaylist.splice(index, 1);
|
||||
|
||||
this.eventEmitter.emit(null);
|
||||
this.playlistFocusedOn = null;
|
||||
this.snackBar.open(`La playlist '${playlist.name}' a bien été suprimée ✔`, "", config);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// retourne la class CSS de conteneur de playlist
|
||||
getClassOfPlaylistContainer(playlist): string
|
||||
{
|
||||
if(playlist === this.playlistFocusedOn) return "row btnPlaylist btnPlaylistFocus" ;
|
||||
else return "row btnPlaylist" ;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<div style="text-align: center; margin: 0px; padding: 0px">
|
||||
|
||||
<div style="margin: 0px; padding: 0px">
|
||||
<mat-form-field appearance="fill" style="margin: 0px; padding: 0px">
|
||||
<mat-label> Nom de la playlist </mat-label>
|
||||
<input matInput [(ngModel)]="name" style="width: 100%">
|
||||
</mat-form-field>
|
||||
<span *ngIf="hasError" class="mat-error"> {{errorMessage}} </span>
|
||||
</div>
|
||||
|
||||
<div style="text-align: right ; margin: 0px; padding: 0px">
|
||||
<button mat-button (click)="onAnnuler()">Annuler</button>
|
||||
<button mat-button (click)="onValider()">
|
||||
<span *ngIf="action === 'create'"> Créer </span>
|
||||
<span *ngIf="action === 'update'"> Modifier </span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupCreateOrUpdatePlaylistComponent } from './popup-create-or-update-playlist.component';
|
||||
|
||||
describe('PopupCreatePlaylistComponent', () => {
|
||||
let component: PopupCreateOrUpdatePlaylistComponent;
|
||||
let fixture: ComponentFixture<PopupCreateOrUpdatePlaylistComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupCreateOrUpdatePlaylistComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupCreateOrUpdatePlaylistComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-create-or-update-playlist',
|
||||
templateUrl: './popup-create-or-update-playlist.component.html',
|
||||
styleUrls: ['./popup-create-or-update-playlist.component.scss']
|
||||
})
|
||||
export class PopupCreateOrUpdatePlaylistComponent implements OnInit
|
||||
{
|
||||
name: string = "" ;
|
||||
hasError: boolean = false;
|
||||
tabNomPlaylist: string[] = [];
|
||||
errorMessage: string = "" ;
|
||||
action: string = "";
|
||||
|
||||
|
||||
constructor( public dialogRef: MatDialogRef<PopupCreateOrUpdatePlaylistComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data,
|
||||
private messageService: MessageService) { }
|
||||
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.action = this.data.action;
|
||||
this.tabNomPlaylist = this.data.tabPlaylist.map( playlist0 => playlist0.name );
|
||||
if(this.action === "update") this.name = this.data.playlistName;
|
||||
}
|
||||
|
||||
|
||||
onValider(): void
|
||||
{
|
||||
this.checkError();
|
||||
if(!this.hasError)
|
||||
{
|
||||
if(this.action === "create")
|
||||
{
|
||||
this.messageService
|
||||
.post("playlist/create", {name: this.name})
|
||||
.subscribe(retour => this.onValiderCallback(retour), err => this.onValiderCallback(err));
|
||||
}
|
||||
else if(this.action === "update")
|
||||
{
|
||||
this.messageService
|
||||
.put("playlist/update/"+this.data.playlistId, {name: this.name})
|
||||
.subscribe(retour => this.onValiderCallback(retour), err => this.onValiderCallback(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onValiderCallback(retour): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.dialogRef.close(null);
|
||||
}
|
||||
else {
|
||||
if(this.action === "create") this.dialogRef.close(retour.data);
|
||||
else if(this.action === "update") this.dialogRef.close(this.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
checkError(): void
|
||||
{
|
||||
if(this.name === "") {
|
||||
this.errorMessage = "Le nom ne peut pas être vide" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else if(this.tabNomPlaylist.includes(this.name)){
|
||||
this.errorMessage = "Ce nom est déjà utilisé" ;
|
||||
this.hasError = true;
|
||||
}
|
||||
else {
|
||||
this.hasError = false;
|
||||
this.errorMessage = "" ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onAnnuler(): void
|
||||
{
|
||||
this.dialogRef.close(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<mat-dialog-content class="mat-typography">
|
||||
Êtes-vous sûr de vouloir supprimer <i>{{playlist.name}}</i> ?
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="dialogRef.close()">Annuler</button>
|
||||
<button mat-button (click)="onValidate()" cdkFocusInitial>Valider</button>
|
||||
</mat-dialog-actions>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PopupDeletePlaylistComponent } from './popup-delete-playlist.component';
|
||||
|
||||
describe('PopupDeletePlaylistComponent', () => {
|
||||
let component: PopupDeletePlaylistComponent;
|
||||
let fixture: ComponentFixture<PopupDeletePlaylistComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PopupDeletePlaylistComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PopupDeletePlaylistComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-popup-delete-playlist',
|
||||
templateUrl: './popup-delete-playlist.component.html',
|
||||
styleUrls: ['./popup-delete-playlist.component.scss']
|
||||
})
|
||||
export class PopupDeletePlaylistComponent implements OnInit
|
||||
{
|
||||
playlist;
|
||||
|
||||
constructor( public dialogRef: MatDialogRef<PopupDeletePlaylistComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data,
|
||||
private messageService: MessageService ) { }
|
||||
|
||||
ngOnInit(): void
|
||||
{
|
||||
this.playlist = this.data;
|
||||
}
|
||||
|
||||
onValidate(): void
|
||||
{
|
||||
this.messageService
|
||||
.delete("playlist/delete/"+this.playlist._id)
|
||||
.subscribe( retour => this.onValidateCallback(retour), err => this.onValidateCallback(err));
|
||||
}
|
||||
|
||||
onValidateCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
this.dialogRef.close(null);
|
||||
}
|
||||
else {
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<div [class]="themeService.getClassTheme()">
|
||||
<div class="myContainer">
|
||||
|
||||
<!-- Bordure haute -->
|
||||
<div class="row topBorder">
|
||||
|
||||
<!-- PlaylistDB existe ? -->
|
||||
<div *ngIf="(playlist !== null) && (playlist !== undefined); then ok1 else nope1"></div>
|
||||
|
||||
<!-- oui -->
|
||||
<ng-template #ok1>
|
||||
<span class="spanPlayListTitle"> {{playlist.name}} </span>
|
||||
</ng-template>
|
||||
|
||||
<!-- non -->
|
||||
<ng-template #nope1>
|
||||
<span class="spanPlayListTitle"> Aucune playlist selectionnée </span>
|
||||
</ng-template>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- --------------------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<!-- Liste des videos -->
|
||||
<div class="row" style="margin: 0px; padding: 0px">
|
||||
<div class="listVideoContainer">
|
||||
|
||||
<!-- 'playlist' existe ? -->
|
||||
<div *ngIf="(playlist !== null) && (playlist !== undefined); then ok2 else nope2"></div>
|
||||
|
||||
<!-- oui, 'playlist' existe -->
|
||||
<ng-template #ok2>
|
||||
<div *ngFor="let video of videosInPlaylist; let i = index;" class="videoContainer">
|
||||
|
||||
<mat-grid-list cols="12" rowHeight="15vh">
|
||||
|
||||
<!-- btnAdd -->
|
||||
<mat-grid-tile [colspan]="2" [rowspan]="1">
|
||||
<button mat-icon-button (click)="onAddToPlaylist(video)">
|
||||
<mat-icon> add_circle </mat-icon>
|
||||
</button>
|
||||
</mat-grid-tile>
|
||||
|
||||
<!-- imgVideo -->
|
||||
<mat-grid-tile [colspan]="8" [rowspan]="1">
|
||||
<div style="margin: auto; width: 20vw; height: 15vh;">
|
||||
<div class="imgsContainer" (click)="onVideo(video)">
|
||||
<img class="imgPlay" src="/assets/play.png">
|
||||
<img class="imgVideo" [src]="video.imageUrl">
|
||||
</div>
|
||||
</div>
|
||||
</mat-grid-tile>
|
||||
|
||||
<!-- btnDelete -->
|
||||
<mat-grid-tile [colspan]="2" [rowspan]="1">
|
||||
<button mat-icon-button (click)="onDelete(video, i)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</mat-grid-tile>
|
||||
|
||||
</mat-grid-list>
|
||||
|
||||
<!-- titre video -->
|
||||
<span style="margin-top: 10px">
|
||||
{{video.title}}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<!-- non, 'playlist' n'existe pas -->
|
||||
<ng-template #nope2>
|
||||
<div></div>
|
||||
</ng-template>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- --------------------------------------------------------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
<!-- Bordure basse -->
|
||||
<div class="row bottomBorder">
|
||||
<div style="visibility: hidden">a</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
.myContainer {
|
||||
//background-color: white ;
|
||||
background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
|
||||
background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
|
||||
text-align: center;
|
||||
width: 35vw;
|
||||
margin: 1vh 0vh 3vh 0vh;
|
||||
padding: 0px;
|
||||
border: solid 2px black;
|
||||
border-radius: 10px;
|
||||
box-shadow: 10px 5px 5px black;
|
||||
}
|
||||
|
||||
// TopBorder --------------------------------------------------------
|
||||
|
||||
.topBorder {
|
||||
margin: 0px 0px 0px 0px;
|
||||
//background-color: #dcdcdc;
|
||||
background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
|
||||
background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
|
||||
text-align: left;
|
||||
padding: 5px 0px 5px 5px;
|
||||
border-bottom: solid 1px black;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
|
||||
.spanPlayListTitle {
|
||||
font-size: large;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
// Liste des videos ------------------------------------------------
|
||||
|
||||
.listVideoContainer {
|
||||
height: 65vh;
|
||||
background-color: white;
|
||||
padding: 0px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.videoContainer {
|
||||
border-bottom: solid 1px black;
|
||||
padding: 15px 0px 15px 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.imgsContainer {
|
||||
position: relative;
|
||||
width: 20vw;
|
||||
height: 15vh;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.imgPlay {
|
||||
position: absolute;
|
||||
margin-left: 9vw;
|
||||
width: 3vw;
|
||||
margin-top: 5vh;
|
||||
height: 6vh;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
.imgVideo {
|
||||
border: solid 1px black;
|
||||
width: 20vw;
|
||||
height: 15vh;
|
||||
padding: 0px 0px 0px 0px;
|
||||
}
|
||||
|
||||
// BottomBorder --------------------------------------------------------
|
||||
|
||||
.bottomBorder {
|
||||
margin: 0px 0px 0px 0px;
|
||||
//background-color: #dcdcdc;
|
||||
background: linear-gradient(top, rgba(38,38,38,0.8), #e6e6e6 25%, #fff 38%, #c5c5c5 87%, rgba(38,38,38,0.8));
|
||||
background: -webkit-linear-gradient(top, #c5c5c5, #e6e6e6 25%, #fff 38%, #c5c5c5 87%, #c5c5c5);
|
||||
border-top: solid 1px black;
|
||||
border-bottom: solid 1px black;
|
||||
font-size: large;
|
||||
border-bottom-left-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { VideoListComponent } from './video-list.component';
|
||||
|
||||
describe('VideoListComponent', () => {
|
||||
let component: VideoListComponent;
|
||||
let fixture: ComponentFixture<VideoListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ VideoListComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(VideoListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
92
src/app/user/myPlaylists/video-list/video-list.component.ts
Normal file
92
src/app/user/myPlaylists/video-list/video-list.component.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
|
||||
import {AddVideoToPlaylistsService} from "../../utils/services/addVideoToPlaylists/add-video-to-playlists.service";
|
||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||
import {Router} from "@angular/router";
|
||||
import {MessageService} from "../../../utils/message/message.service";
|
||||
import {ThemeService} from "../../../utils/theme/theme.service";
|
||||
import {ProfilService} from "../../../utils/profil/profil.service";
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-video-list',
|
||||
templateUrl: './video-list.component.html',
|
||||
styleUrls: ['./video-list.component.scss']
|
||||
})
|
||||
export class VideoListComponent implements OnChanges
|
||||
{
|
||||
@Input() playlist: any;
|
||||
videosInPlaylist: any[] = [];
|
||||
|
||||
|
||||
constructor( private messageService: MessageService,
|
||||
public themeService: ThemeService,
|
||||
private addVideoToPlaylistsService: AddVideoToPlaylistsService,
|
||||
private snackBar: MatSnackBar,
|
||||
private profilService: ProfilService,
|
||||
private router: Router ) { }
|
||||
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void
|
||||
{
|
||||
if((this.playlist !== null) && (this.playlist !== undefined)) this.videosInPlaylist = this.playlist.videos;
|
||||
}
|
||||
|
||||
|
||||
onAddToPlaylist(video: any): void
|
||||
{
|
||||
this.addVideoToPlaylistsService.run(video.videoId, video.source, video.interest);
|
||||
}
|
||||
|
||||
|
||||
onDelete(video0: any, indexVideo: number): void
|
||||
{
|
||||
const data = {
|
||||
videoId: {
|
||||
id: video0._id,
|
||||
action: "delete"
|
||||
}
|
||||
}
|
||||
this.messageService
|
||||
.put("playlist/update/"+this.playlist._id, data)
|
||||
.subscribe( ret => this.onDeleteCallback(ret, indexVideo), err => this.onDeleteCallback(err, indexVideo));
|
||||
}
|
||||
|
||||
|
||||
onDeleteCallback(retour: any, indexVideo: number): void
|
||||
{
|
||||
if(retour.status !== "success") {
|
||||
console.log(retour);
|
||||
}
|
||||
else {
|
||||
this.playlist.videos.splice(indexVideo, 1);
|
||||
this.videosInPlaylist.splice(indexVideo, 1);
|
||||
let message = "La video a bien été supprimé de la playlist";
|
||||
const config = { duration: 1000, panelClass: "custom-class" };
|
||||
this.snackBar.open( message, "", config);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onVideo(video: any): void
|
||||
{
|
||||
this.messageService
|
||||
.put("video/update/"+video._id, {watchedDate: true})
|
||||
.subscribe(ret => this.onVideoCallback(ret), err => this.onVideoCallback(err));
|
||||
|
||||
const params = {
|
||||
videoId: video.videoId,
|
||||
source: video.source,
|
||||
_idPlaylist: this.playlist._id,
|
||||
from: "myPlaylists",
|
||||
};
|
||||
this.router.navigate(['/user/watching'], { queryParams: params });
|
||||
}
|
||||
|
||||
|
||||
onVideoCallback(retour: any): void
|
||||
{
|
||||
if(retour.status !== "success") console.log(retour);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue