Update: Environment for Heroku Production

This commit is contained in:
Yûki VACHOT 2021-12-22 13:39:06 +01:00
parent 5fbdb7098e
commit 13daf10ca8
201 changed files with 34 additions and 2736 deletions

View file

@ -0,0 +1,39 @@
<mat-form-field class="example-chip-list" appearance="fill">
<!-- ------------------------------------------------------------------------------------ -->
<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"
#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>

View file

@ -0,0 +1,20 @@
mat-form-field {
width: 100%;
font-size: small;
}
mat-chip-list {
font-size: small;
}
mat-chip {
font-size: small;
}
input {
font-size: small;
}
mat-option {
font-size: small;
}

View file

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { InputInterestsProfilComponent } from './input-interests-profil.component';
describe('InputInterestsComponent', () => {
let component: InputInterestsProfilComponent;
let fixture: ComponentFixture<InputInterestsProfilComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ InputInterestsProfilComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(InputInterestsProfilComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,121 @@
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
import {COMMA, ENTER} from "@angular/cdk/keycodes";
import {FormControl} from "@angular/forms";
import {Observable} from "rxjs";
import {map, startWith} from "rxjs/operators";
import {MatChipInputEvent} from "@angular/material/chips";
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
import {MessageService} from "../../../utils/message/message.service";
@Component({
selector: 'app-input-interests-profil',
templateUrl: './input-interests-profil.component.html',
styleUrls: ['./input-interests-profil.component.scss']
})
export class InputInterestsProfilComponent implements OnInit
{
selectable = true;
removable = true;
separatorKeysCodes: number[] = [ENTER, COMMA];
formControl = new FormControl();
filteredInterests: Observable<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));
}
}

View file

@ -0,0 +1,92 @@
<div [class]="themeService.getClassTheme()">
<div class="myContainer">
<!-- NavBar -->
<app-navbar-user></app-navbar-user>
<!-- Boite -->
<div class="boite">
<!-- Photo de profil -->
<div style="text-align: center">
<img [src]="user.profileImageUrl" onerror="this.onerror=null; this.src='assets/profil.png'">
</div>
<!-- Col gauche & droite -->
<div class="row">
<ng-template *ngIf="true; then leftCol"></ng-template>
<ng-template *ngIf="true; then rightCol"></ng-template>
</div>
<!-- Modifier profil -->
<div class="btnContainer">
<button mat-button class="myBtn" (click)="onModifier()">Modifier profil</button>
</div>
</div>
</div>
</div>
<!-- --------------------------------------------------------------------------------------------------------------- -->
<ng-template #leftCol>
<div class="col-6">
<!-- login -->
<div class="row myRow">
<div class="col-8 myLabel">Pseudo:</div>
<div class="col-4 myValue"> {{user.login}} </div>
</div>
<!-- email -->
<div class="row myRow">
<div class="col-8 myLabel">Mail:</div>
<div class="col-4 myValue"> {{user.email}} </div>
</div>
<!-- gender -->
<div class="row myRow">
<div class="col-8 myLabel">Sexe:</div>
<div class="col-4 myValue">
<span *ngIf="user.gender==='man'"> Homme </span>
<span *ngIf="user.gender==='woman'"> Femme </span>
</div>
</div>
<!-- createdAt -->
<div class="row myRow">
<div class="col-8 myLabel">Date de création:</div>
<div class="col-4 myValue">{{ user.createdAt | date:'dd/LL/YYYY' }}</div>
</div>
</div>
</ng-template>
<!-- --------------------------------------------------------------------------------------------------------------- -->
<ng-template #rightCol>
<div class="col-6" style="border-left: solid 1px #e6e6e6">
<!-- dateOfBirth -->
<div class="row myRow">
<div class="col-4 myLabel">Date de naissance:</div>
<div class="col-8 myValue">{{ user.dateOfBirth | date:'dd/LL/YYYY' }}</div>
</div>
<!-- interets -->
<div class="row myRow">
<div class="col-4 myLabel">Centres d'intérêt:</div>
<div class="col-8 myValue">
<div class="interestsContainer">
<div *ngFor="let interest of user.interests" class="interest"> {{interest}} </div>
</div>
</div>
</div>
</div>
</ng-template>

View file

@ -0,0 +1,80 @@
.myContainer {
max-width: 100vw;
height: 100vh;
overflow-x: hidden;
}
.boite {
margin-left: auto;
margin-right: auto;
width: 70%;
margin-top: 10vh;
border: solid 3px;
border-radius: 10px;
padding: 20px 40px 20px 40px;
background-color: #ffffff;
text-align: center;
box-shadow: 10px 5px 5px black;
}
.lightTheme .boite {
border-color: black;
}
.darkTheme .boite {
border-color: white;
}
// --------------------------------------------------------------------------------------------
img {
margin: 0px 0px 10px 0px;
width: 5vw;
height: 5vw;
border: solid 2px black;
border-radius: 50%;
font-size: xxx-large;
}
// --------------------------------------------------------------------------------------------
.myRow {
margin: 15px 0px 15px 0px;
}
.myLabel {
text-align: right;
padding: 0px 5px 0px 0px;
margin: 0px;
font-weight: bold;
}
.myValue {
text-align: left;
padding: 0px 0px 0px 5px;
margin: 0px;
}
// --------------------------------------------------------------------------------------------
.interestsContainer {
width: 70%;
height: 15vh;
overflow-y: scroll;
border: 1px solid black;
}
.interest {
border-bottom: 1px solid #dcdcdc;
padding: 5px 5px 5px 5px;
}
// --------------------------------------------------------------------------------------------
.btnContainer {
text-align: center;
margin-top: 40px;
}
.myBtn {
border: solid 1px black;
background-color: white;
}

View file

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PageProfilUserComponent } from './page-profil-user.component';
describe('PageProfilUserComponent', () => {
let component: PageProfilUserComponent;
let fixture: ComponentFixture<PageProfilUserComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PageProfilUserComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PageProfilUserComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,89 @@
import { Component, OnInit } from '@angular/core';
import {MatDialog} from "@angular/material/dialog";
import {MatSnackBar} from "@angular/material/snack-bar";
import {PopupUpdateUserComponent} from "../popup-update-user/popup-update-user.component";
import {ThemeService} from "../../../utils/theme/theme.service";
import {MessageService} from "../../../utils/message/message.service";
import {ProfilService} from "../../../utils/profil/profil.service";
@Component({
selector: 'app-page-profil-user',
templateUrl: './page-profil-user.component.html',
styleUrls: ['./page-profil-user.component.scss']
})
export class PageProfilUserComponent implements OnInit
{
user = {
_id: "",
login: "",
hashPass: "",
email: "",
role: {
name: "user",
permission: 0,
isAccepted: false,
},
profileImageUrl: "",
dateOfBirth: null,
gender: "man",
interests: [],
company: "",
isActive: true,
createdAt: new Date(),
updatedAt: new Date(),
lastConnexion: null
};
constructor( public themeService: ThemeService,
public dialog: MatDialog,
private snackBar: MatSnackBar,
private messageService: MessageService,
private profilService: ProfilService ) { }
ngOnInit(): void
{
this.messageService
.get( "user/findOne/"+this.profilService.getId())
.subscribe( retour => this.ngOnInitCallback(retour), err => this.ngOnInitCallback(err) )
}
ngOnInitCallback(retour: any)
{
if(retour.status !== "success") {
console.log(retour);
}
else {
this.user = retour.data;
}
}
onModifier()
{
const config = {
width: '70%',
data: { user: this.user }
};
this.dialog
.open(PopupUpdateUserComponent, config)
.afterClosed()
.subscribe(retour => {
if((retour === null) || (retour === undefined))
{
const config = { duration: 1000, panelClass: "custom-class" };
this.snackBar.open( "Opération annulé", "", config);
}
else
{
this.user = retour;
}
});
}
}

View file

@ -0,0 +1,125 @@
<div class="myContainer">
<!-- Photo de profil -->
<div style="text-align: center; font-size: small">
<img [src]="userCopy.profileImageUrl" onerror="this.onerror=null; this.src='assets/profil.png'"><br>
<input title="lien vers image"
type="text"
class="form-control inputUrlImage"
style="font-size: small"
[(ngModel)]="userCopy.profileImageUrl">
</div><br>
<!-- Col gauche & droite -->
<div class="row">
<ng-template *ngIf="true; then leftCol"></ng-template>
<ng-template *ngIf="true; then rightCol"></ng-template>
</div>
<!-- message d'erreur -->
<div *ngIf="hasError" style="text-align: center; margin-bottom: 20px;">
<span class="mat-error"><br>{{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>
<!-- ----------------------------------------------------------------------------------------------------------------------------- -->
<!-- Colonne gauche -->
<ng-template #leftCol>
<div class="col-6">
<!-- login -->
<div class="form-group row myRow">
<label for="login" class="col-sm-6 col-form-label myLeftLabel">Pseudo:</label>
<div class="col-sm-6">
<input type="text" class="form-control myValue" id="login" [(ngModel)]="userCopy.login">
</div>
</div>
<!-- dateOfBirth -->
<div class="form-group row myRow">
<label for="dateOfBirth" class="col-sm-6 col-form-label myLeftLabel">Date de naissance:</label>
<div class="col-sm-6">
<input type="date"
class="form-control myValue"
id="dateOfBirth"
[ngModel] ="userCopy.dateOfBirth | date:'yyyy-MM-dd'"
(ngModelChange)="userCopy.dateOfBirth = $event">
</div>
</div>
<!-- sexe -->
<div class="myRow" style="text-align: right; margin-right: 15px">
<mat-radio-group [(ngModel)]="userCopy.gender">
<mat-radio-button value="man"> Homme </mat-radio-button>&nbsp; &nbsp;
<mat-radio-button value="woman"> Femme </mat-radio-button>
</mat-radio-group>
</div>
<!-- Modifier mot de passe -->
<div class="form-group row" style="margin: 10px 0px 10px 0px">
<label for="changePassword" class="col-sm-6 col-form-label myLeftLabel">Modifier mot de passe:</label>
<div class="col-sm-6">
<mat-checkbox id="changePassword" [(ngModel)]="changePassword"></mat-checkbox>
</div>
</div>
<!-- Nouveau mot de passe -->
<div class="form-group row" style="margin: 10px 0px 10px 0px">
<label for="newPassword" class="col-sm-6 col-form-label myLeftLabel"></label>
<div class="col-sm-6">
<input type="password"
class="form-control myValue"
id="newPassword"
placeholder=" Nouveau mot de passe"
[(ngModel)]="newPassword"
[disabled]="!changePassword">
</div>
</div>
<!-- Confirmation nouveau mot de passe -->
<div class="form-group row" style="margin: 10px 0px 10px 0px">
<label for="confimNewPassword" class="col-sm-6 col-form-label myLeftLabel"></label>
<div class="col-sm-6">
<input type="password"
class="form-control myValue"
id="confimNewPassword"
placeholder=" Confirmation nouveau mot de passe"
[(ngModel)]="confirmNewPassword"
[disabled]="!changePassword">
</div>
</div>
</div>
</ng-template>
<!-- ----------------------------------------------------------------------------------------------------------------------------- -->
<!-- Colonne droite -->
<ng-template #rightCol>
<div class="col-6" style="border-left: solid 1px #e6e6e6">
<!-- interests -->
<div class="form-group row myRow">
<label for="interests" class="col-sm-3 col-form-label myLeftLabel">Centres d'intérêt:</label>
<div class="col-sm-9">
<app-input-interests-profil
id="interests"
[myInterests]="userCopy.interests"
(eventEmitter)="onEventInputInterests($event)"></app-input-interests-profil>
</div>
</div>
</div>
</ng-template>

View file

@ -0,0 +1,81 @@
.myContainer {
font-size: small;
}
button {
font-size: small;
}
img {
margin: 0px 0px 10px 0px;
width: 5vw;
height: 5vw;
border: solid 2px black;
border-radius: 50%;
font-size: xxx-large;
}
.inputUrlImage {
width: 80%;
text-align: center;
margin-left: 10%;
margin-right: 10%;
font-size: small;
border-radius: 5px;
}
input {
font-size: small;
}
// -------------------------------------------------------------------------
.myRow {
margin: 15px 0px 15px 0px;
}
.myLeftLabel {
text-align: right;
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
font-weight: bold;
}
.myRightLabel {
text-align: left;
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
font-weight: bold;
}
.myValue {
text-align: left;
padding: 0px 0px 0px 0px;
margin: 0px 0px 0px 0px;
}
// -------------------------------------------------------------------------
// aura
::ng-deep .mat-checkbox-ripple .mat-ripple-element {
background-color: grey !important;
}
// contenu coche
::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background {
background-color: black !important;
}
// indeterminate
::ng-deep .mat-checkbox .mat-checkbox-frame {
background-color: white !important;
}
// -------------------------------------------------------------------------
::ng-deep .mat-radio-inner-circle {
color: black !important;
background-color: black !important;
}
::ng-deep .mat-radio-outer-circle{
color: black !important;
border: solid 1px gray !important;
}

View file

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PopupUpdateUserComponent } from './popup-update-user.component';
describe('PopupUpdateUserComponent', () => {
let component: PopupUpdateUserComponent;
let fixture: ComponentFixture<PopupUpdateUserComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PopupUpdateUserComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(PopupUpdateUserComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,132 @@
import {Component, Inject, OnInit} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {MessageService} from "../../../utils/message/message.service";
import {ProfilService} from "../../../utils/profil/profil.service";
@Component({
selector: 'app-popup-update-user',
templateUrl: './popup-update-user.component.html',
styleUrls: ['./popup-update-user.component.scss']
})
export class PopupUpdateUserComponent implements OnInit
{
userCopy;
newPassword: string = "";
confirmNewPassword: string = "" ;
changePassword: boolean = false ;
hasError: boolean = false;
errorMessage: string = "" ;
constructor( public dialogRef: MatDialogRef<PopupUpdateUserComponent>,
@Inject(MAT_DIALOG_DATA) public data,
private messageService: MessageService,
private profilService: ProfilService ) { }
ngOnInit(): void
{
const user0 = this.data.user;
this.userCopy = {
_id: user0._id,
login: user0.login,
hashPass: user0.hashPass,
email: user0.email,
role: {
name: user0.role.name,
permission: user0.role.permission,
isAccepted: user0.role.isAccepted,
},
profileImageUrl: user0.profileImageUrl,
dateOfBirth: user0.dateOfBirth,
gender: user0.gender,
interests: [],
company: "",
isActive: user0.isActive,
createdAt: user0.createdAt,
updatedAt: user0.updatedAt,
lastConnexion: new Date()
};
for(let interest of user0.interests) this.userCopy.interests.push(interest);
}
onValider()
{
this.checkField();
if(!this.hasError)
{
if(this.changePassword) this.userCopy.hashPass = this.newPassword;
const data = {
login: this.userCopy.login,
hashPass: this.userCopy.hashPass,
email: this.userCopy.email,
profileImageUrl: this.userCopy.profileImageUrl,
dateOfBirth: this.userCopy.dateOfBirth,
gender: this.userCopy.gender,
interests: this.userCopy.interests,
};
this.messageService
.put("user/update/"+this.profilService.getId(), data)
.subscribe( ret => this.onValiderCallback(ret), err => this.onValiderCallback(err) );
}
}
onValiderCallback(retour: any)
{
if(retour.status !== "success") {
console.log(retour);
this.dialogRef.close(null);
}
else {
this.profilService.setProfileImageUrl(this.userCopy.profileImageUrl);
this.dialogRef.close(this.userCopy);
}
}
checkField()
{
if(this.userCopy.login.length === 0) {
this.errorMessage = "Veuillez remplir le champ 'pseudo'." ;
this.hasError = true;
}
else if(this.userCopy.email.length === 0) {
this.errorMessage = "Veuillez remplir le champ 'email'." ;
this.hasError = true;
}
else if(!this.isValidEmail(this.userCopy.email)) {
this.errorMessage = "Email invalide." ;
this.hasError = true;
}
else if((this.changePassword) && (this.newPassword.length === 0)) {
this.errorMessage = "Veuillez remplir le champ 'mot de passe'" ;
this.hasError = true;
}
else if((this.changePassword) && (this.newPassword !== this.confirmNewPassword)) {
this.errorMessage = "Le mot de passe est différent de sa confirmation" ;
this.hasError = true;
}
else {
this.errorMessage = "" ;
this.hasError = false;
}
}
isValidEmail(email)
{
let re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
onEventInputInterests(myInterets: string[])
{
this.userCopy.interests = myInterets;
}
}