Merge branch 'features/replace_primeng_by_material' into 'develop'

replace primeng by material && improvments

See merge request framasoft/framadate/funky-framadate-front!41
This commit is contained in:
seraph_ino 2020-06-17 13:52:06 +02:00
commit d722103c05
78 changed files with 996 additions and 990 deletions

View File

@ -17,7 +17,7 @@
"isMaybeAnswerAvailable": true,
"areResultsPublic": true,
"dateCreated": "2020-05-17",
"expires": "2020-06-17"
"expires": "2020-12-31"
},
"ownerId": 1,
"question": "Quelle date pour le picnic ?",
@ -35,7 +35,7 @@
"isMaybeAnswerAvailable": true,
"areResultsPublic": true,
"dateCreated": "2020-05-17",
"expires": "2020-06-17"
"expires": "2020-11-30"
},
"ownerId": 2,
"question": "On fait quoi pendant les vacances ?",

View File

@ -31,12 +31,13 @@
"@angular/core": "^9.0.7",
"@angular/forms": "^9.0.7",
"@angular/localize": "^9.1.1",
"@angular/material": "^9.2.4",
"@angular/platform-browser": "^9.0.7",
"@angular/platform-browser-dynamic": "^9.0.7",
"@angular/router": "^9.0.7",
"@fullcalendar/core": "^4.4.0",
"@ngx-translate/core": "^12.1.2",
"@ngx-translate/http-loader": "^4.0.0",
"@ngx-translate/http-loader": "^5.0.0",
"angular-date-value-accessor": "^1.0.2",
"axios": "^0.19.2",
"bulma": "^0.9.0",
@ -51,8 +52,8 @@
"quill": "^1.3.7",
"rxjs": "^6.5.5",
"rxjs-compat": "^6.5.5",
"short-unique-id": "^3.0.3",
"tslib": "<2.0.0",
"uuid": "^8.0.0",
"zone.js": "^0.10.3"
},
"devDependencies": {
@ -67,7 +68,6 @@
"@compodoc/compodoc": "^1.1.11",
"@types/jest": "^26.0.0",
"@types/node": "^14.0.1",
"@types/uuid": "^8.0.0",
"@typescript-eslint/eslint-plugin": "^3.0.0",
"@typescript-eslint/parser": "^3.0.0",
"babel-jest": "^26.0.0",

View File

@ -1,6 +1,13 @@
<mat-sidenav-container (backdropClick)="sidenav.toggle()">
<mat-sidenav #sidenav mode="side">
<app-navigation></app-navigation>
</mat-sidenav>
<mat-sidenav-content>
<div id="big_container" [class]="themeClass">
<div class="container">
<app-header [isSidebarOpened]="isSidebarOpened" (toggleSidebarEE)="toggleSidebar($event)"></app-header>
<mat-slide-toggle (change)="sidenav.toggle()">Dev Menu</mat-slide-toggle>
<app-header></app-header>
<main>
<router-outlet></router-outlet>
</main>
@ -8,8 +15,5 @@
<app-feedback></app-feedback>
</div>
</div>
<p-toast position="bottom-center"></p-toast>
<p-sidebar [(visible)]="isSidebarOpened">
<app-navigation></app-navigation>
</p-sidebar>
</mat-sidenav-content>
</mat-sidenav-container>

View File

@ -0,0 +1,52 @@
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" role="button" (click)="toggleSidebarOpening()"> Dev menu </a>
<a
role="button"
class="navbar-burger burger"
aria-label="menu"
aria-expanded="false"
data-target="navbarBasicExample"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"> Modules </a>
<div class="navbar-dropdown">
<a class="navbar-item" routerLink="oldstuff" routerLinkActive="is-active">
Old stuff
</a>
<a class="navbar-item" routerLink="administration" routerLinkActive="is-active">
Administration
</a>
<a class="navbar-item" routerLink="consultation" routerLinkActive="is-active">
Consultation
</a>
<a class="navbar-item" routerLink="participation" routerLinkActive="is-active">
Participation
</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"> Tous les sondages </a>
<div class="navbar-dropdown">
<a
class="navbar-item"
*ngFor="let slug of slugsAvailables"
routerLink="{{ '/consultation/poll/' + slug }}"
routerLinkActive="is-active"
>
« {{ slug }} »
</a>
</div>
</div>
</div>
</div>
</nav>

View File

@ -1,19 +1,19 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PollEditComponent } from './poll-edit.component';
import { DevNavbarComponent } from './dev-navbar.component';
describe('PollEditComponent', () => {
let component: PollEditComponent;
let fixture: ComponentFixture<PollEditComponent>;
describe('DevNavbarComponent', () => {
let component: DevNavbarComponent;
let fixture: ComponentFixture<DevNavbarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [PollEditComponent],
declarations: [DevNavbarComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PollEditComponent);
fixture = TestBed.createComponent(DevNavbarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@ -0,0 +1,35 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Observable } from 'rxjs';
import { User } from '../../models/user.model';
import { ApiService } from '../../services/api.service';
import { UserService } from '../../services/user.service';
@Component({
selector: 'app-dev-navbar',
templateUrl: './dev-navbar.component.html',
styleUrls: ['./dev-navbar.component.scss'],
})
export class DevNavbarComponent implements OnInit {
@Input() isSidebarOpened: boolean;
@Output() toggleSidebarEE = new EventEmitter<boolean>();
public _user: Observable<User> = this.userService.user;
public slugsAvailables: string[] = [];
constructor(private apiService: ApiService, private userService: UserService) {}
public ngOnInit(): void {
this.getSlugs();
}
public async getSlugs(): Promise<void> {
this.slugsAvailables = await this.apiService.getAllPollsSlugs();
}
public toggleSidebarOpening(): void {
this.isSidebarOpened = !this.isSidebarOpened;
this.toggleSidebarEE.emit(this.isSidebarOpened);
}
}

View File

@ -1,9 +1,7 @@
<header class="big-header">
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item">
<a class="navbar-item" role="button" (click)="toggleSidebarOpening()"> Dev menu </a>
</a>
<a class="navbar-item" routerLink="/"> FramaSondage </a>
<a
role="button"
@ -20,51 +18,13 @@
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" routerLink="/" routerLinkActive="is-active">
Home
</a>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"> Menu </a>
<div class="navbar-dropdown">
<a class="navbar-item" routerLink="administration" routerLinkActive="is-active">
Créer un sondage
</a>
<a class="navbar-item" routerLink="administration/profile" routerLinkActive="is-active">
<a class="navbar-item" routerLink="administration/user-polls" routerLinkActive="is-active">
Mes sondages
</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"> Modules </a>
<div class="navbar-dropdown">
<a class="navbar-item" routerLink="oldstuff" routerLinkActive="is-active">
Old stuff
</a>
<a class="navbar-item" routerLink="administration" routerLinkActive="is-active">
Administration
</a>
<a class="navbar-item" routerLink="consultation" routerLinkActive="is-active">
Consultation
</a>
<a class="navbar-item" routerLink="participation" routerLinkActive="is-active">
Participation
</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"> Tous les sondages </a>
<div class="navbar-dropdown">
<a
class="navbar-item"
*ngFor="let slug of slugsAvailables"
routerLink="{{ '/consultation/poll/' + slug }}"
routerLinkActive="is-active"
>
« {{ slug }} »
</a>
</div>
</div>
</div>
<div class="navbar-end">
<div class="navbar-item" #container>

View File

@ -1,8 +1,8 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { SettingsComponent } from '../../../shared/components/settings/settings.component';
import { User } from '../../models/user.model';
import { ApiService } from '../../services/api.service';
import { ModalService } from '../../services/modal.service';
import { UserService } from '../../services/user.service';
@ -12,29 +12,13 @@ import { UserService } from '../../services/user.service';
styleUrls: ['./header.component.scss'],
})
export class HeaderComponent implements OnInit {
@Input() isSidebarOpened: boolean;
@Output() toggleSidebarEE = new EventEmitter<boolean>();
public _user: Observable<User> = this.userService.user;
public slugsAvailables: string[] = [];
constructor(private userService: UserService, private modalService: ModalService) {}
constructor(private userService: UserService, private modalService: ModalService, private apiService: ApiService) {}
public ngOnInit(): void {
this.getSlugs();
}
public async getSlugs(): Promise<void> {
this.slugsAvailables = await this.apiService.getAllPollsSlugs();
}
public ngOnInit(): void {}
public openDialog(): void {
this.modalService.openSettingsComponent();
}
public toggleSidebarOpening(): void {
this.isSidebarOpened = !this.isSidebarOpened;
this.toggleSidebarEE.emit(this.isSidebarOpened);
this.modalService.openModal(SettingsComponent);
}
}

View File

@ -17,6 +17,9 @@
<h2 class="subtitle">
Où sont mes sondages?
</h2>
<a role="button" class="button is-fullwidth is-primary" routerLink="administration/user-polls">
Mes sondages
</a>
</div>
</div>
</div>

View File

@ -1,19 +1,58 @@
<nav>
<a class="button" routerLink="oldstuff/home" routerLinkActive="active">
<i class="fa fa-home" aria-hidden="true"></i> Accueil
<nav class="has-background-light">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"> Tous les sondages </a>
<div class="navbar-dropdown">
<a
class="navbar-item"
*ngFor="let slug of slugsAvailables"
routerLink="{{ '/consultation/poll/' + slug }}"
routerLinkActive="is-active"
>
« {{ slug }} »
</a>
<a class="button" routerLink="oldstuff/step/creation" routerLinkActive="active"> Création </a>
<a class="button" routerLink="oldstuff/step/date" routerLinkActive="active"> Les Dates </a>
<a class="button" routerLink="oldstuff/step/answers" routerLinkActive="active"> Réponses </a>
<a class="button" routerLink="oldstuff/step/visibility" routerLinkActive="active"> Visibilité </a>
<a class="button" routerLink="oldstuff/step/resume" routerLinkActive="active"> Résumé </a>
<a class="button" routerLink="oldstuff/step/end" routerLinkActive="active"> Confirmation </a>
<a class="button" routerLink="oldstuff/step/admin"> Administration </a>
</div>
</div>
<hr />
<a class="button" routerLink="oldstuff/step/kind" routerLinkActive="active"> Page démo </a>
<a class="button" routerLink="oldstuff/vote/poll/id/1" routerLinkActive="active"> Sondage 1 </a>
<a class="button" routerLink="oldstuff/vote/poll/id/2" routerLinkActive="active"> Sondage 2 </a>
<a class="button" routerLink="oldstuff/vote/poll/id/3" routerLinkActive="active"> Sondage 3 (dessins animés) </a>
<a class="button" routerLink="oldstuff/graphic/toto" routerLinkActive="active"> Graphique </a>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"> Modules </a>
<div class="navbar-dropdown">
<a class="navbar-item" routerLink="oldstuff" routerLinkActive="is-active">
Old stuff
</a>
<a class="navbar-item" routerLink="administration" routerLinkActive="is-active">
Administration
</a>
<a class="navbar-item" routerLink="consultation" routerLinkActive="is-active">
Consultation
</a>
<a class="navbar-item" routerLink="participation" routerLinkActive="is-active">
Participation
</a>
</div>
</div>
<hr />
<a class="button is-block" routerLink="oldstuff/step/home" routerLinkActive="active">
<i class="fa fa-home" aria-hidden="true"></i> Accueil
</a>
<a class="button is-block" routerLink="oldstuff/step/creation" routerLinkActive="active"> Création </a>
<a class="button is-block" routerLink="oldstuff/step/date" routerLinkActive="active"> Les Dates </a>
<a class="button is-block" routerLink="oldstuff/step/answers" routerLinkActive="active"> Réponses </a>
<a class="button is-block" routerLink="oldstuff/step/visibility" routerLinkActive="active"> Visibilité </a>
<a class="button is-block" routerLink="oldstuff/step/resume" routerLinkActive="active"> Résumé </a>
<a class="button is-block" routerLink="oldstuff/step/end" routerLinkActive="active"> Confirmation </a>
<a class="button is-block" routerLink="oldstuff/step/admin"> Administration </a>
<hr />
<a class="button is-block" routerLink="oldstuff/step/kind" routerLinkActive="active"> Page démo </a>
<a class="button is-block" routerLink="oldstuff/vote/poll/id/1" routerLinkActive="active"> Sondage 1 </a>
<a class="button is-block" routerLink="oldstuff/vote/poll/id/2" routerLinkActive="active"> Sondage 2 </a>
<a class="button is-block" routerLink="oldstuff/vote/poll/id/3" routerLinkActive="active">
Sondage 3 (dessins animés)
</a>
<a class="button is-block" routerLink="oldstuff/graphic/toto" routerLinkActive="active"> Graphique </a>
</nav>

View File

@ -1,12 +1,23 @@
import { Component, OnInit } from '@angular/core';
import { ApiService } from '../../../services/api.service';
import { UserService } from '../../../services/user.service';
@Component({
selector: 'app-navigation',
templateUrl: './navigation.component.html',
styleUrls: ['./navigation.component.scss'],
})
export class NavigationComponent implements OnInit {
constructor() {}
public slugsAvailables: string[] = [];
ngOnInit(): void {}
constructor(private apiService: ApiService, private userService: UserService) {}
public ngOnInit(): void {
this.getSlugs();
}
public async getSlugs(): Promise<void> {
this.slugsAvailables = await this.apiService.getAllPollsSlugs();
}
}

View File

@ -10,9 +10,17 @@ import { HomeComponent } from './components/home/home.component';
import { LogoComponent } from './components/logo/logo.component';
import { NavigationComponent } from './components/sibebar/navigation/navigation.component';
import { throwIfAlreadyLoaded } from './guards/module-import.guard';
import { DevNavbarComponent } from './components/dev-navbar/dev-navbar.component';
@NgModule({
declarations: [FooterComponent, HeaderComponent, HomeComponent, LogoComponent, NavigationComponent],
declarations: [
FooterComponent,
HeaderComponent,
HomeComponent,
LogoComponent,
NavigationComponent,
DevNavbarComponent,
],
imports: [CommonModule, FormsModule, RouterModule, TranslateModule],
exports: [HeaderComponent, FooterComponent, NavigationComponent, LogoComponent],
})

View File

@ -1,24 +1,22 @@
import { environment } from '../../../environments/environment';
import { DateUtilsService } from '../utils/date-utils.service';
import { DateService } from '../services/date.service';
export class Configuration {
constructor(
public isAboutDate: boolean = false,
public isProtectedByPassword: boolean = false,
public isOwnerNotifiedByEmail: { onNewVote: boolean; onNewComment: boolean } = {
onNewVote: false,
onNewComment: false,
},
public isOwnerNotifiedByEmailOnNewVote: boolean = false,
public isOwnerNotifiedByEmailOnNewComment: boolean = false,
public isMaybeAnswerAvailable: boolean = false,
public areResultsPublic: boolean = false,
public areResultsPublic: boolean = true,
public dateCreated: Date = new Date(Date.now()),
public expires: Date = DateUtilsService.addDaysToDate(
public expires: Date = DateService.addDaysToDate(
environment.poll.defaultConfig.expiracyInDays,
new Date(Date.now())
)
) {}
public static isArchived(configuration: Configuration): boolean {
return DateUtilsService.isDateInPast(configuration.expires);
return DateService.isDateInPast(configuration.expires);
}
}

View File

@ -1,5 +1,4 @@
import { environment } from 'src/environments/environment';
import { v4 as uuidv4 } from 'uuid';
import { Choice } from './choice.model';
import { Comment } from './comment.model';
@ -9,9 +8,9 @@ import { User } from './user.model';
export class Poll {
constructor(
public owner: User,
public slug: string,
public question: string,
public description?: string,
public slug: string = uuidv4(),
public configuration: Configuration = new Configuration(),
public comments: Comment[] = [],
public choices: Choice[] = []
@ -30,9 +29,9 @@ export class Poll {
): Poll {
const poll = new Poll(
new User(item.owner.pseudo, item.owner.email, undefined),
item.slug,
item.question,
item.description,
item.slug,
item.configuration,
item.comments
.map(
@ -46,7 +45,6 @@ export class Poll {
choice.participants.set(key, new Set(value));
});
choice.updateCounts();
console.log({ choice });
return choice;
})
);

View File

@ -1,13 +1,13 @@
import { TestBed } from '@angular/core/testing';
import { DateUtilsService } from './date-utils.service';
import { DateService } from './date.service';
describe('DateUtilsService', () => {
let service: DateUtilsService;
let service: DateService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(DateUtilsService);
service = TestBed.inject(DateService);
});
it('should be created', () => {

View File

@ -4,7 +4,7 @@ import * as moment from 'moment';
@Injectable({
providedIn: 'root',
})
export class DateUtilsService {
export class DateService {
public static addDaysToDate(days: number, date: Date): Date {
return moment(date).add(days, 'days').toDate();
}

View File

@ -1,19 +0,0 @@
import { Injectable } from '@angular/core';
import { MessageService } from 'primeng/api';
import { MessageSeverity } from '../enums/message-severity.enum';
@Injectable({
providedIn: 'root',
})
export class MessageDisplayerService {
constructor(private messageService: MessageService) {}
public display(severity: MessageSeverity, summary?: string, detail?: string): void {
this.messageService.add({
severity,
summary,
detail,
});
}
}

View File

@ -5,6 +5,7 @@ import { Poll } from '../models/poll.model';
import { User } from '../models/user.model';
import { PollService } from './poll.service';
import { UserService } from './user.service';
import { UuidService } from './uuid.service';
@Injectable({
providedIn: 'root',
@ -13,10 +14,12 @@ export class MockingService {
private user: User;
public pollsDatabase: Poll[] = [];
private poll1: Poll = new Poll(this.user, 'Quand le picnic ?', 'Pour faire la teuf');
private poll2: Poll = new Poll(this.user, 'On fait quoi à la soirée ?', 'Balancez vos idées');
private poll1: Poll = new Poll(this.user, undefined, 'Quand le picnic ?', 'Pour faire la teuf');
private poll2: Poll = new Poll(this.user, undefined, 'On fait quoi à la soirée ?', 'Balancez vos idées');
constructor(private userService: UserService, private pollService: PollService) {
constructor(private userService: UserService, private pollService: PollService, private uuidService: UuidService) {
this.poll1.slug = this.uuidService.getUUID();
this.poll2.slug = this.uuidService.getUUID();
this.poll1.choices = [new Choice('mardi prochain'), new Choice('mercredi')];
this.poll2.choices = [new Choice('jeux'), new Choice('danser'), new Choice('discuter en picolant')];

View File

@ -1,26 +1,20 @@
import { Injectable } from '@angular/core';
import { DialogService } from 'primeng';
import { ChoiceDetailsComponent } from '../../shared/components/choice-details/choice-details.component';
import { SettingsComponent } from '../../shared/components/settings/settings.component';
import { Choice } from '../models/choice.model';
import { ComponentType } from '@angular/cdk/portal';
import { Injectable, TemplateRef } from '@angular/core';
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
@Injectable({
providedIn: 'root',
})
export class ModalService {
constructor(public dialogService: DialogService) {}
constructor(public dialog: MatDialog) {}
public openSettingsComponent(): void {
this.dialogService.open(SettingsComponent, { header: 'Paramètres', dismissableMask: true });
public openModal_OLD<T, K>(componentOrTemplateRef: ComponentType<T> | TemplateRef<T>, data?: K): void {
this.dialog.open(componentOrTemplateRef, { data: data });
}
public openChoiceDetailsComponent(choice: Choice): void {
this.dialogService.open(ChoiceDetailsComponent, {
header: 'Détails des votes',
dismissableMask: true,
data: choice,
width: '70%',
});
public openModal<T, D = any>(
componentOrTemplateRef: ComponentType<T> | TemplateRef<T>,
config?: MatDialogConfig<D>
): void {
this.dialog.open<T, D>(componentOrTemplateRef, config);
}
}

View File

@ -7,7 +7,7 @@ import { Choice } from '../models/choice.model';
import { Poll } from '../models/poll.model';
import { User } from '../models/user.model';
import { ApiService } from './api.service';
import { MessageDisplayerService } from './message-displayer.service';
import { ToastService } from './toast.service';
@Injectable({
providedIn: 'root',
@ -16,7 +16,7 @@ export class PollService {
private _poll: BehaviorSubject<Poll | undefined> = new BehaviorSubject<Poll | undefined>(undefined);
public readonly poll: Observable<Poll | undefined> = this._poll.asObservable();
constructor(private apiService: ApiService, private messageDisplayerService: MessageDisplayerService) {}
constructor(private apiService: ApiService, private toastService: ToastService) {}
public updateCurrentPoll(poll: Poll): void {
this._poll.next(poll);
@ -31,12 +31,9 @@ export class PollService {
const pollUrl: string = await this.apiService.createPoll(this._poll.getValue());
// TODO: Maybe handle the url to update currentPoll according to backend response
if (pollUrl) {
this.messageDisplayerService.display(MessageSeverity.SUCCESS, 'Le sondage a été enregistré.');
this.toastService.display('Le sondage a été enregistré.');
} else {
this.messageDisplayerService.display(
MessageSeverity.ERROR,
'Le sondage na été correctement enregistré, veuillez ré-essayer.'
);
this.toastService.display('Le sondage na été correctement enregistré, veuillez ré-essayer.');
}
}
@ -45,15 +42,12 @@ export class PollService {
currentPoll.choices.find((c) => c.label === choice.label)?.updateParticipation(user, response);
this.updateCurrentPoll(currentPoll);
this.apiService.createParticipation(currentPoll.slug, choice.label, user.pseudo, response);
this.messageDisplayerService.display(
MessageSeverity.SUCCESS,
'Votre participation au sondage a été enregistrée.'
);
this.toastService.display(MessageSeverity.SUCCESS, 'Votre participation au sondage a été enregistrée.');
}
public async deleteAllAnswers(): Promise<void> {
await this.apiService.deletePollAnswers(this._poll.getValue().slug);
this.messageDisplayerService.display(
this.toastService.display(
MessageSeverity.SUCCESS,
'Les participations des votants à ce sondage ont été supprimées.'
);
@ -61,15 +55,12 @@ export class PollService {
public async addComment(comment: string): Promise<void> {
await this.apiService.createComment(this._poll.getValue().slug, comment);
this.messageDisplayerService.display(MessageSeverity.SUCCESS, 'Votre commentaire a été enregistré.');
this.toastService.display('Votre commentaire a été enregistré.');
}
public async deleteComments(): Promise<void> {
await this.apiService.deletePollComments(this._poll.getValue().slug);
this.messageDisplayerService.display(
MessageSeverity.SUCCESS,
'Les commentaires de ce sondage ont été supprimés.'
);
this.toastService.display('Les commentaires de ce sondage ont été supprimés.');
}
public buildAnswersByChoiceLabelByPseudo(poll: Poll): Map<string, Map<string, Answer>> {

View File

@ -1,13 +1,13 @@
import { TestBed } from '@angular/core/testing';
import { MessageDisplayerService } from './message-displayer.service';
import { ToastService } from './toast.service';
describe('MessageService', () => {
let service: MessageDisplayerService;
let service: ToastService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(MessageDisplayerService);
service = TestBed.inject(ToastService);
});
it('should be created', () => {

View File

@ -0,0 +1,14 @@
import { Injectable } from '@angular/core';
import { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar';
@Injectable({
providedIn: 'root',
})
export class ToastService {
constructor(private _snackBar: MatSnackBar) {}
public display(message: string, action?: string): void {
const config: MatSnackBarConfig = { duration: 2000 };
this._snackBar.open(message, action, config);
}
}

View File

@ -1,5 +1,4 @@
import { Injectable } from '@angular/core';
import { DialogService } from 'primeng';
import { BehaviorSubject, Observable } from 'rxjs';
import { UserRole } from '../enums/user-role.enum';
@ -15,7 +14,7 @@ export class UserService {
private _user: BehaviorSubject<User> = new BehaviorSubject<User>(this.anonymous);
public readonly user: Observable<User> = this._user.asObservable();
constructor(private apiService: ApiService, public dialogService: DialogService) {}
constructor(private apiService: ApiService) {}
public updateUser(user: User): void {
this._user.next(user);

View File

@ -1,13 +1,13 @@
import { TestBed } from '@angular/core/testing';
import { PollUtilsService } from './poll-utils.service';
import { UuidService } from './uuid.service';
describe('PollUtilsService', () => {
let service: PollUtilsService;
describe('UuidService', () => {
let service: UuidService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(PollUtilsService);
service = TestBed.inject(UuidService);
});
it('should be created', () => {

View File

@ -0,0 +1,13 @@
import { Injectable } from '@angular/core';
import ShortUniqueId from 'short-unique-id';
@Injectable({
providedIn: 'root',
})
export class UuidService {
private uid = new ShortUniqueId();
public getUUID(): string {
return this.uid();
}
}

View File

@ -1,47 +0,0 @@
import { Injectable } from '@angular/core';
import { v4 as uuidv4 } from 'uuid';
@Injectable({
providedIn: 'root',
})
export class PollUtilsService {
public makeUuid(): string {
// TODO: how to be sure the uuid generated in front is available in backend ?
// It could be a better way to generate uuids in backend.
return uuidv4();
}
/**
* make a uniq slug for the current poll creation
* @param str
*/
public makeSlug(config: any): string {
let str = '';
str =
config.creationDate.getFullYear() +
'_' +
(config.creationDate.getMonth() + 1) +
'_' +
config.creationDate.getDate() +
'_' +
config.myName +
'_' +
config.title;
str = str.replace(/^\s+|\s+$/g, ''); // trim
str = str.toLowerCase();
// remove accents, swap ñ for n, etc
const from = 'àáäâèéëêìíïîòóöôùúüûñç·/_,:;';
const to = 'aaaaeeeeiiiioooouuuunc------';
for (let i = 0, l = from.length; i < l; i++) {
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
}
str = str
.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
.replace(/\s+/g, '-') // collapse whitespace and replace by -
.replace(/-+/g, '-'); // collapse dashes
return str;
}
}

View File

@ -2,30 +2,14 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdministrationComponent } from './administration.component';
import { EditConfigurationComponent } from './edit-configuration/edit-configuration.component';
import { EditDescriptionComponent } from './edit-description/edit-description.component';
import { EditOptionsComponent } from './edit-options/edit-options.component';
import { PollEditComponent } from './poll-edit/poll-edit.component';
import { ProfileComponent } from './profile/profile.component';
import { UserPollsComponent } from './user-polls/user-polls.component';
const routes: Routes = [
{ path: '', redirectTo: 'edit', pathMatch: 'full' },
{
path: 'edit',
component: AdministrationComponent,
children: [
{ path: '', redirectTo: 'description', pathMatch: 'full' },
{ path: 'description', component: EditDescriptionComponent },
{ path: 'description/:slug', component: EditDescriptionComponent },
{ path: 'options', component: EditOptionsComponent },
{ path: 'options/:slug', component: EditOptionsComponent },
{ path: 'configuration', component: EditConfigurationComponent },
{ path: 'configuration/:slug', component: EditConfigurationComponent },
{ path: 'preview', component: PollEditComponent },
{ path: 'preview/:slug', component: PollEditComponent },
],
},
{ path: 'profile', component: ProfileComponent },
{ path: '', redirectTo: 'poll', pathMatch: 'full' },
{ path: ':slug', redirectTo: 'poll/:slug', pathMatch: 'full' },
{ path: 'poll', component: AdministrationComponent },
{ path: 'poll/:slug', component: AdministrationComponent },
{ path: 'user-polls', component: UserPollsComponent },
];
@NgModule({

View File

@ -1,12 +1,21 @@
<div class="container has-text-centered">
<div class="columns">
<div class="column has-text-centered">
<h1>Administration</h1>
</div>
</div>
<app-spinner *ngIf="_isLoading | async"></app-spinner>
<ng-container *ngIf="!(_isLoading | async)">
<ng-container *ngIf="!(_poll | async)">
<app-page-not-found [message]="'PAGE_NOT_FOUND.POLL'"></app-page-not-found>
</ng-container>
<ng-container *ngIf="_poll | async as poll">
<div class="columns">
<div class="column">
<app-stepper></app-stepper>
</div>
</div>
<div class="columns">
<div class="column">
<router-outlet></router-outlet>
</div>
<app-stepper [poll]="poll"></app-stepper>
</div>
</div>
</ng-container>
</ng-container>

View File

@ -1,6 +1,13 @@
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { Poll } from '../../core/models/poll.model';
import { LoaderService } from '../../core/services/loader.service';
import { ModalService } from '../../core/services/modal.service';
import { PollService } from '../../core/services/poll.service';
import { UrlService } from '../../core/services/url.service';
import { UserService } from '../../core/services/user.service';
import { SettingsComponent } from '../../shared/components/settings/settings.component';
@Component({
selector: 'app-administration',
@ -8,9 +15,21 @@ import { Poll } from '../../core/models/poll.model';
styleUrls: ['./administration.component.scss'],
})
export class AdministrationComponent implements OnInit {
public poll: Poll;
public _isLoading: Observable<boolean> = this.loaderService.isLoading;
public _poll: Observable<Poll> = this.pollService.poll;
constructor() {}
constructor(
private urlService: UrlService,
private loaderService: LoaderService,
private pollService: PollService,
private userService: UserService,
private modalService: ModalService
) {}
ngOnInit(): void {}
ngOnInit(): void {
if (!this.userService.isCurrentUserIdentifiable()) {
this.modalService.openModal(SettingsComponent);
}
this.urlService.loadPollFromUrl();
}
}

View File

@ -6,23 +6,11 @@ import { TranslateModule } from '@ngx-translate/core';
import { SharedModule } from '../../shared/shared.module';
import { AdministrationRoutingModule } from './administration-routing.module';
import { AdministrationComponent } from './administration.component';
import { PollEditComponent } from './poll-edit/poll-edit.component';
import { ProfileComponent } from './profile/profile.component';
import { StepperComponent } from './stepper/stepper.component';
import { EditDescriptionComponent } from './edit-description/edit-description.component';
import { EditOptionsComponent } from './edit-options/edit-options.component';
import { EditConfigurationComponent } from './edit-configuration/edit-configuration.component';
import { UserPollsComponent } from './user-polls/user-polls.component';
@NgModule({
declarations: [
AdministrationComponent,
PollEditComponent,
StepperComponent,
ProfileComponent,
EditDescriptionComponent,
EditOptionsComponent,
EditConfigurationComponent,
],
declarations: [AdministrationComponent, StepperComponent, UserPollsComponent],
imports: [
AdministrationRoutingModule,
CommonModule,

View File

@ -1 +0,0 @@
<p>edit-configuration works!</p>

View File

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

View File

@ -1,12 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-edit-configuration',
templateUrl: './edit-configuration.component.html',
styleUrls: ['./edit-configuration.component.scss'],
})
export class EditConfigurationComponent implements OnInit {
constructor() {}
ngOnInit(): void {}
}

View File

@ -1,40 +0,0 @@
<div [ngClass]="{ 'is-success': pollForm.status == 'VALID' }" class="info form-status pull-right debug">
<p>Form Status: {{ pollForm.status }}</p>
</div>
<br />
<form [formGroup]="pollForm" (ngSubmit)="onSubmit()">
<div class="control is-expanded">
<div class="field">
<label for="primeType">Sondage concerne des dates ?</label>
<p-inputSwitch id="primeType" formControlName="type"></p-inputSwitch>
</div>
</div>
<div class="control is-expanded">
<label class="label" for="title">
Intitulé
<input class="input" formControlName="title" id="title" placeholder="Intitulé" type="text" />
</label>
</div>
<div class="control is-expanded">
<label class="label" for="description">
Description
<textarea
formControlName="description"
id="description"
pInputTextarea
placeholder="Description"
></textarea>
</label>
</div>
<div class="control is-expanded">
<label class="label" for="slug">
Url personnalisée
<input class="input" formControlName="slug" id="slug" placeholder="Url" type="text" />
</label>
</div>
<button class="button is-fullwidth is-primary" type="submit" [disabled]="!pollForm.valid">Créer</button>
</form>

View File

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

View File

@ -1,41 +0,0 @@
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Poll } from '../../../core/models/poll.model';
import { PollUtilsService } from '../../../core/utils/poll-utils.service';
@Component({
selector: 'app-edit-description',
templateUrl: './edit-description.component.html',
styleUrls: ['./edit-description.component.scss'],
})
export class EditDescriptionComponent implements OnInit {
@Input()
public poll?: Poll;
public pollForm: FormGroup;
constructor(private fb: FormBuilder, private pollUtilsService: PollUtilsService) {}
ngOnInit(): void {
this.pollForm = this.fb.group({
type: [this.poll ? this.poll.configuration.isAboutDate : false, [Validators.required]],
title: [this.poll ? this.poll.question : '', [Validators.required]],
description: [this.poll ? this.poll.description : ''],
slug: [this.poll ? this.poll.slug : this.generateRandomSlug(), [Validators.required]],
address: this.fb.group({
street: [''],
city: [''],
state: [''],
zip: [''],
}),
});
}
public onSubmit(): void {
console.log(this.pollForm);
}
private generateRandomSlug(): string {
return this.pollUtilsService.makeUuid();
}
}

View File

@ -1 +0,0 @@
<p>edit-options works!</p>

View File

@ -1,12 +0,0 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-edit-options',
templateUrl: './edit-options.component.html',
styleUrls: ['./edit-options.component.scss'],
})
export class EditOptionsComponent implements OnInit {
constructor() {}
ngOnInit(): void {}
}

View File

@ -1,33 +0,0 @@
<div>
<p>Form Status: {{ pollForm.status }}</p>
</div>
<br />
<form [formGroup]="pollForm" (ngSubmit)="onSubmit()">
<div class="field">
<label for="primeType">Sondage concerne des dates ?</label>
<p-inputSwitch id="primeType" formControlName="type"></p-inputSwitch>
</div>
<div class="control is-expanded">
<label class="label" for="title">
Intitulé
<input class="input" type="text" formControlName="title" placeholder="Intitulé" />
</label>
</div>
<div class="control is-expanded">
<label class="label" for="description">
Description
<textarea pInputTextarea formControlName="description" placeholder="Description"></textarea>
</label>
</div>
<div class="control is-expanded">
<label class="label" for="slug">
Url personnalisée
<input class="input" type="text" formControlName="slug" placeholder="Url" />
</label>
</div>
<button class="button is-fullwidth is-primary" type="submit" [disabled]="!pollForm.valid">Créer</button>
</form>

View File

@ -1,41 +0,0 @@
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Poll } from '../../../core/models/poll.model';
import { PollUtilsService } from '../../../core/utils/poll-utils.service';
@Component({
selector: 'app-poll-edit',
templateUrl: './poll-edit.component.html',
styleUrls: ['./poll-edit.component.scss'],
})
export class PollEditComponent implements OnInit {
@Input()
public poll?: Poll;
public pollForm: FormGroup;
constructor(private fb: FormBuilder, private pollUtilsService: PollUtilsService) {}
ngOnInit(): void {
this.pollForm = this.fb.group({
type: [this.poll ? this.poll.configuration.isAboutDate : false, [Validators.required]],
title: [this.poll ? this.poll.question : '', [Validators.required]],
description: [this.poll ? this.poll.description : ''],
slug: [this.poll ? this.poll.slug : this.generateRandomSlug(), [Validators.required]],
address: this.fb.group({
street: [''],
city: [''],
state: [''],
zip: [''],
}),
});
}
public onSubmit(): void {
console.log(this.pollForm);
}
private generateRandomSlug(): string {
return this.pollUtilsService.makeUuid();
}
}

View File

@ -1 +1,124 @@
<p-steps [model]="items" [readonly]="false" [(activeIndex)]="activeIndex"></p-steps>
<mat-vertical-stepper #stepper linear>
<mat-step [stepControl]="pollFormGroup" class="is-expanded">
<form [formGroup]="pollFormGroup">
<ng-template matStepLabel>Informations du sondage</ng-template>
<mat-form-field appearance="outline">
<mat-label>Question posée, sujet, etc.</mat-label>
<input #question matInput placeholder="Question posée, sujet" formControlName="question" required />
<button
mat-button
*ngIf="question.value"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="question.value = ''"
>
<i class="fa fa-close"></i>
</button>
</mat-form-field>
<mat-form-field appearance="outline" class="is-flex">
<mat-label>Url pour les participants</mat-label>
<span matPrefix>{{ urlPrefix }}</span>
<input #slug matInput placeholder="Url" formControlName="slug" required />
<button
mat-button
*ngIf="slug.value"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="slug.value = ''"
>
<i class="fa fa-close"></i>
</button>
</mat-form-field>
<mat-form-field appearance="outline" class="is-flex">
<mat-label>Description</mat-label>
<textarea
#description
matInput
placeholder="Description"
formControlName="description"
required
></textarea>
<button
mat-button
*ngIf="description.value"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="description.value = ''"
>
<i class="fa fa-close"></i>
</button>
</mat-form-field>
<div>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step [stepControl]="configurationFormGroup">
<form [formGroup]="configurationFormGroup">
<ng-template matStepLabel>Configuration du sondage</ng-template>
<mat-form-field appearance="outline" class="is-flex">
<mat-label>Nombre de jours avant expiration</mat-label>
<input
#expiracy
matInput
type="number"
placeholder="Nombre de jours avant expiration"
formControlName="expiracyNumberOfDays"
required
/>
<button
mat-button
*ngIf="expiracy.value"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="expiracy.value = ''"
>
<i class="fa fa-close"></i>
</button>
</mat-form-field>
<mat-checkbox class="is-flex" formControlName="areResultsPublic">
Les participants pourront consulter les résultats
</mat-checkbox>
<mat-checkbox class="is-flex" formControlName="isAboutDate">
Les choix possibles concerneront des dates
</mat-checkbox>
<mat-checkbox class="is-flex" formControlName="isProtectedByPassword">
Le sondage sera protégé par un mot de passe
</mat-checkbox>
<mat-checkbox class="is-flex" formControlName="isOwnerNotifiedByEmailOnNewVote">
Vous recevrez un mail à chaque nouvelle participation
</mat-checkbox>
<mat-checkbox class="is-flex" formControlName="isOwnerNotifiedByEmailOnNewComment">
Vous recevrez un mail à chaque nouveau commentaire
</mat-checkbox>
<mat-checkbox class="is-flex" formControlName="isMaybeAnswerAvailable">
La réponse « peut-être » sera disponible
</mat-checkbox>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button matStepperNext>Next</button>
</div>
</form>
</mat-step>
<mat-step>
<ng-template matStepLabel>Done</ng-template>
<p>You are now done.</p>
<div>
<button mat-button matStepperPrevious>Back</button>
<button mat-button (click)="stepper.reset()">Reset</button>
</div>
<div>
<button mat-button (click)="savePoll()" [disabled]="!pollFormGroup.valid || !configurationFormGroup.valid">
Enregistrer le sondage
</button>
</div>
</mat-step>
</mat-vertical-stepper>

View File

@ -1,8 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { MenuItem } from 'primeng/api';
import { WorkflowStep } from '../../../core/enums/workflow-step.enum';
import { WorkflowService } from '../../../core/services/workflow.service';
import { Observable } from 'rxjs';
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Poll } from '../../../core/models/poll.model';
import { UuidService } from '../../../core/services/uuid.service';
import { DateService } from '../../../core/services/date.service';
@Component({
selector: 'app-stepper',
@ -10,42 +11,54 @@ import { Observable } from 'rxjs';
styleUrls: ['./stepper.component.scss'],
})
export class StepperComponent implements OnInit {
public items: MenuItem[];
public itemDescription: MenuItem = {
id: '1',
label: WorkflowStep.DESCRIPTION,
title: 'Je donne une description générale du sondage',
routerLink: './description',
command: () => {},
state: { isStepCompleteAndValid: true },
disabled: false,
};
public itemOptions: MenuItem = {
id: '2',
label: WorkflowStep.CHOICES,
title: 'Je renseigne les différentes options sur lesquelles les gens vont donner leur avis',
routerLink: './options',
command: () => {},
state: { isStepCompleteAndValid: true },
disabled: false,
};
public itemConfiguration: MenuItem = {
id: '3',
label: WorkflowStep.CONFIGURATION,
title: 'Je configure le sondage',
routerLink: './configuration',
command: () => {},
state: { isStepCompleteAndValid: true },
disabled: false,
};
public activeIndex: number;
public activeStep: Observable<WorkflowStep>;
@Input()
public poll?: Poll;
constructor(private workflowService: WorkflowService) {}
public pollFormGroup: FormGroup;
public configurationFormGroup: FormGroup;
public choicesFormGroup: FormGroup;
public urlPrefix: string = window.location.origin + '/participation/';
constructor(private fb: FormBuilder, private uuidService: UuidService) {}
ngOnInit(): void {
// this.activeStep = this.workflowService.currentStep;
this.pollFormGroup = this.fb.group({
question: [this.poll ? this.poll.question : '', [Validators.required]],
slug: [this.poll ? this.poll.slug : this.uuidService.getUUID(), [Validators.required]],
description: [this.poll ? this.poll.description : ''],
});
this.items = [this.itemDescription, this.itemOptions, this.itemConfiguration];
this.configurationFormGroup = this.fb.group({
isAboutDate: [this.poll ? this.poll.configuration.isAboutDate : false, [Validators.required]],
isProtectedByPassword: [
this.poll ? this.poll.configuration.isProtectedByPassword : false,
[Validators.required],
],
isOwnerNotifiedByEmailOnNewVote: [
this.poll ? this.poll.configuration.isOwnerNotifiedByEmailOnNewVote : false,
[Validators.required],
],
isOwnerNotifiedByEmailOnNewComment: [
this.poll ? this.poll.configuration.isOwnerNotifiedByEmailOnNewComment : false,
[Validators.required],
],
isMaybeAnswerAvailable: [
this.poll ? this.poll.configuration.isMaybeAnswerAvailable : false,
[Validators.required],
],
areResultsPublic: [this.poll ? this.poll.configuration.areResultsPublic : true, [Validators.required]],
expiracyNumberOfDays: [
this.poll ? DateService.diffInDays(new Date(), this.poll.configuration.expires) : 60,
[Validators.required],
],
});
}
public savePoll(): void {
if (this.pollFormGroup.valid && this.configurationFormGroup.valid) {
console.log('Le sondage est correctement rempli, prêt à enregistrer.');
// TODO : save the poll
}
}
}

View File

@ -1,19 +1,19 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { EditOptionsComponent } from './edit-options.component';
import { UserPollsComponent } from './user-polls.component';
describe('EditOptionsComponent', () => {
let component: EditOptionsComponent;
let fixture: ComponentFixture<EditOptionsComponent>;
describe('UserPollsComponent', () => {
let component: UserPollsComponent;
let fixture: ComponentFixture<UserPollsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [EditOptionsComponent],
declarations: [UserPollsComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EditOptionsComponent);
fixture = TestBed.createComponent(UserPollsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@ -5,11 +5,11 @@ import { User } from '../../../core/models/user.model';
import { UserService } from '../../../core/services/user.service';
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.scss'],
selector: 'app-user-polls',
templateUrl: './user-polls.component.html',
styleUrls: ['./user-polls.component.scss'],
})
export class ProfileComponent implements OnInit {
export class UserPollsComponent implements OnInit {
public _user: Observable<User> = this.userService.user;
public isModalOpened = false;

View File

@ -5,8 +5,8 @@ import { ConsultationComponent } from './consultation.component';
const routes: Routes = [
{ path: '', redirectTo: 'poll', pathMatch: 'full' },
{ path: 'poll', component: ConsultationComponent },
{ path: ':slug', redirectTo: 'poll/:slug', pathMatch: 'full' },
{ path: 'poll', component: ConsultationComponent },
{ path: 'poll/:slug', component: ConsultationComponent },
];

View File

@ -4,11 +4,7 @@
</div>
</div>
<div *ngIf="_isLoading | async" class="columns p-justify-center">
<div class="column has-text-centered">
<p-progressSpinner></p-progressSpinner>
</div>
</div>
<app-spinner *ngIf="_isLoading | async"></app-spinner>
<ng-container *ngIf="!(_isLoading | async)">
<ng-container *ngIf="!(_poll | async)">

View File

@ -8,6 +8,7 @@ import { ModalService } from '../../core/services/modal.service';
import { PollService } from '../../core/services/poll.service';
import { UrlService } from '../../core/services/url.service';
import { UserService } from '../../core/services/user.service';
import { SettingsComponent } from '../../shared/components/settings/settings.component';
@Component({
selector: 'app-consultation',
@ -29,7 +30,7 @@ export class ConsultationComponent implements OnInit {
ngOnInit(): void {
if (!this.userService.isCurrentUserIdentifiable()) {
this.modalService.openSettingsComponent();
this.modalService.openModal(SettingsComponent);
}
this.urlService.loadPollFromUrl();
}

View File

@ -1,9 +1,11 @@
import { Component, Input, OnInit } from '@angular/core';
import { MatDialogConfig } from '@angular/material/dialog';
import { Answer } from '../../../core/enums/answer.enum';
import { Choice } from '../../../core/models/choice.model';
import { Poll } from '../../../core/models/poll.model';
import { ModalService } from '../../../core/services/modal.service';
import { ChoiceDetailsComponent } from '../../../shared/components/choice-details/choice-details.component';
@Component({
selector: 'app-poll-results-compact',
@ -12,8 +14,6 @@ import { ModalService } from '../../../core/services/modal.service';
})
export class PollResultsCompactComponent implements OnInit {
@Input() public poll: Poll;
public isModalOpened = false;
public choiceInModal: Choice;
public answerEnum = Answer;
constructor(private modalService: ModalService) {}
@ -21,8 +21,7 @@ export class PollResultsCompactComponent implements OnInit {
ngOnInit(): void {}
public openModal(choice: Choice): void {
this.modalService.openChoiceDetailsComponent(choice);
this.choiceInModal = choice;
this.isModalOpened = true;
const config: MatDialogConfig<Choice> = { data: choice };
this.modalService.openModal<ChoiceDetailsComponent, Choice>(ChoiceDetailsComponent, config);
}
}

View File

@ -1,6 +1,7 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { ConfirmationService, DialogModule, MessageService } from 'primeng';
import { SharedModule } from '../../shared/shared.module';
import { DateValueAccessorModule } from './custom-lib/date-value-accessor';
@ -33,6 +34,8 @@ import { ResettableInputDirective } from './ui/directives/resettable-input.direc
import { ErasableInputComponent } from './ui/erasable-input/erasable-input.component';
import { TwoLinksComponent } from './ui/two-links/two-links.component';
const PRIMENG_MODULES = [DialogModule];
@NgModule({
declarations: [
AdminComponent,
@ -63,12 +66,13 @@ import { TwoLinksComponent } from './ui/two-links/two-links.component';
VotingNavigationComponent,
],
imports: [
...PRIMENG_MODULES,
CommonModule,
OldStuffRoutingModule,
SharedModule,
TranslateModule.forChild({ extend: true }),
DateValueAccessorModule,
],
providers: [ConfigService],
providers: [ConfigService, ConfirmationService, MessageService],
})
export class OldStuffModule {}

View File

@ -1,10 +1,11 @@
import { DOCUMENT } from '@angular/common';
import { ChangeDetectorRef, Component, Inject, OnInit } from '@angular/core';
import { ToastService } from '../../../../core/services/toast.service';
import { DateUtilities } from '../../config/DateUtilities';
import { otherDefaultDates } from '../../config/defaultConfigs';
import { ConfigService } from '../../services/config.service';
import { BaseComponent } from '../example/base-page/base.component';
import { DOCUMENT } from '@angular/common';
import { MessageService } from 'primeng/api';
import { otherDefaultDates } from '../../config/defaultConfigs';
import { DateUtilities } from '../../config/DateUtilities';
@Component({
selector: 'app-dates',
@ -21,7 +22,7 @@ export class DatesComponent extends BaseComponent implements OnInit {
constructor(
public config: ConfigService,
private cd: ChangeDetectorRef,
private messageService: MessageService,
private toastService: ToastService,
private dateUtilities: DateUtilities,
@Inject(DOCUMENT) private document: any
) {
@ -128,10 +129,6 @@ export class DatesComponent extends BaseComponent implements OnInit {
this.config.dateList = [...new Set(converted)]; // add only dates that are not already present with a Set of unique items
this.showDateInterval = false;
this.messageService.add({
severity: 'success',
summary: 'Dates ajoutées',
detail: `les dates ont été ajoutées aux réponses possibles`,
});
this.toastService.display(`les dates ont été ajoutées aux réponses possibles.`);
}
}

View File

@ -1,8 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { BaseComponent } from '../example/base-page/base.component';
import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { ToastService } from '../../../../core/services/toast.service';
import { ConfigService } from '../../services/config.service';
import { MessageService } from 'primeng/api';
import { BaseComponent } from '../example/base-page/base.component';
@Component({
selector: 'app-end-confirmation',
@ -12,7 +13,7 @@ import { MessageService } from 'primeng/api';
export class EndConfirmationComponent extends BaseComponent implements OnInit {
mailToRecieve = '';
constructor(public config: ConfigService, public http: HttpClient, private messageService: MessageService) {
constructor(public config: ConfigService, public http: HttpClient, private toastService: ToastService) {
super(config);
this.mailToRecieve = this.config.myEmail;
}
@ -20,11 +21,7 @@ export class EndConfirmationComponent extends BaseComponent implements OnInit {
ngOnInit(): void {}
copyLink(str: any) {
this.messageService.add({
severity: 'success',
summary: 'Lien copié',
detail: str,
});
this.toastService.display(`Lien copié : ${str}`);
}
sendToEmail() {}

View File

@ -1,9 +1,10 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { ConfirmationService, MessageService } from 'primeng/api';
import { ConfirmationService } from 'primeng/api';
import { environment } from '../../../../environments/environment';
import { ToastService } from '../../../core/services/toast.service';
import { PollConfig } from '../config/PollConfig';
import { PollUtilities } from '../config/PollUtilities';
import { mockPoll3 } from '../mocks/mock-poll3';
@ -22,7 +23,7 @@ export class ConfigService extends PollConfig {
constructor(
private http: HttpClient,
private messageService: MessageService,
private toastService: ToastService,
private router: Router,
private utils: PollUtilities,
private confirmationService: ConfirmationService
@ -133,11 +134,9 @@ export class ConfigService extends PollConfig {
// message: 'Trouvé! Allez voir votre boite email',
this.myPolls = res;
this.loading = false;
this.messageService.add({
severity: 'success',
summary: 'Succès',
detail: `Vos infos de sondages vous ont été transmises. Allez voir votre boite email ${this.myEmail}`,
});
this.toastService.display(
`Succès : vos infos de sondages vous ont été transmises. Allez voir votre boite email ${this.myEmail}`
);
},
(e) => {
this.handleError(e);
@ -153,7 +152,7 @@ export class ConfigService extends PollConfig {
// TODO: inutile, l'utilisateur ne veut pas savoir ce que le backend répond
console.error('err', err);
this.loading = false;
this.messageService.add({ severity: 'warning', summary: "Erreur lors de l'appel ", detail: err.message });
this.toastService.display(`Erreur lors de l'appel : ${err.message}`);
}
findLocalStorageData() {
@ -282,7 +281,7 @@ export class ConfigService extends PollConfig {
return this.http.post(`${this.apiBaseHref}/poll`, config, this.utils.makeHeaders()).subscribe(
(res: any) => {
// redirect to the page to administrate the new poll
this.messageService.add({ severity: 'success', summary: 'Sondage Créé' });
this.toastService.display('Sondage Créé');
this.updateCurrentPollFromResponse(res);
@ -382,7 +381,7 @@ export class ConfigService extends PollConfig {
)
.subscribe(
(res: any) => {
this.messageService.add({ severity: 'success', summary: 'Vote mis à jour' });
this.toastService.display('Vote mis à jour');
this.updateCurrentPollFromResponse(res);
},
(e) => {
@ -409,11 +408,7 @@ export class ConfigService extends PollConfig {
console.log('comment', comment);
this.http.post(`${this.apiBaseHref}/poll/${this.pollId}/comment`, comment, this.utils.makeHeaders()).subscribe(
(res: any) => {
this.messageService.add({
severity: 'success',
summary: 'Commentaire Créé',
detail: comment.text,
});
this.toastService.display(`Commentaire créé: ${comment.text}`);
// empty comment after success
this.myComment = '';
comment.date = {
@ -443,11 +438,7 @@ export class ConfigService extends PollConfig {
.delete(`${this.apiBaseHref}/poll/${this.pollId}/comments`, this.utils.makeHeaders())
.subscribe(
(res: any) => {
this.messageService.add({
severity: 'success',
summary: 'Commentaires bien supprimés',
detail: 'Commentaires du sondage "' + this.title + '" supprimé',
});
this.toastService.display(`Les commentaires du sondage ${this.title} ont été supprimés`);
},
(e) => {
this.handleError(e);
@ -467,11 +458,7 @@ export class ConfigService extends PollConfig {
accept: () => {
this.http.delete(`${this.apiBaseHref}/poll/${this.pollId}/votes`, this.utils.makeHeaders()).subscribe(
(res: any) => {
this.messageService.add({
severity: 'success',
summary: 'Votes bien supprimés',
detail: 'Votes du sondage "' + this.title + '" supprimé',
});
this.toastService.display(`Les participations aux sondage ${this.title} ont été supprimées.`);
},
(e) => {
this.handleError(e);
@ -483,10 +470,7 @@ export class ConfigService extends PollConfig {
deletePoll() {
if (!this.pollId) {
this.messageService.add({
summary: 'this poll is not administrable, it has no ID',
severity: 'warning',
});
this.toastService.display(`this poll is not administrable, it has no ID`);
return;
}
const self = this;
@ -499,11 +483,7 @@ export class ConfigService extends PollConfig {
accept: () => {
this.http.delete(`${this.apiBaseHref}/poll/${this.pollId}`, this.utils.makeHeaders()).subscribe(
(res: any) => {
this.messageService.add({
severity: 'success',
summary: 'Sondage bien supprimé',
detail: 'sondage "' + this.title + '" supprimé',
});
this.toastService.display(`Le sondage ${this.title} a été supprimé.`);
this.router.navigate(['home']);
},
@ -524,10 +504,7 @@ export class ConfigService extends PollConfig {
updatePoll(voteStack: any) {
this.http.put(`${this.apiBaseHref}/poll/${this.pollId}`, voteStack, this.utils.makeHeaders()).subscribe(
(res: any) => {
this.messageService.add({
severity: 'success',
summary: 'Sondage mis à jour',
});
this.toastService.display(`Le sondage a été mis à jour.`);
this.updateCurrentPollFromResponse(res);
},
(e) => {
@ -635,12 +612,10 @@ export class ConfigService extends PollConfig {
}
todo(message = '') {
this.messageService.add({
severity: 'info' + message,
detail:
"cette fonctionnalité n'est pas encore disponible. Venez en discuter sur framateam.org / Ux et design libre / Framasoft",
summary: 'Work in progress',
});
this.toastService.display(
`INFO : ${message}.
Cette fonctionnalité n'est pas encore disponible. Venez en discuter sur framateam.org / Ux et design libre / Framasoft`
);
}
execStuff() {

View File

@ -1,6 +1,7 @@
import { Component, Input, OnInit } from '@angular/core';
import { ClipboardService } from 'ngx-clipboard';
import { MessageService } from 'primeng/api';
import { ToastService } from '../../../../core/services/toast.service';
@Component({
selector: 'app-copy-text',
@ -11,16 +12,12 @@ export class CopyTextComponent implements OnInit {
@Input() public textToCopy: string;
public displayContentToCopy = false;
constructor(private _clipboardService: ClipboardService, private messageService: MessageService) {}
constructor(private _clipboardService: ClipboardService, private toastService: ToastService) {}
ngOnInit(): void {}
public copy(): void {
this._clipboardService.copyFromContent(this.textToCopy);
this.messageService.add({
severity: 'success',
summary: 'Texte copié',
detail: this.textToCopy,
});
this.toastService.display(`Texte copié : ${this.textToCopy}`);
}
}

View File

@ -5,28 +5,24 @@
</div>
<div class="column">
<div class="buttons has-addons is-centered">
<button
class="button"
[ngClass]="{ 'is-selected is-success': response == 'YES' }"
(click)="vote('YES')"
>
<button class="button" [ngClass]="{ 'is-selected is-success': answer == 'YES' }" (click)="vote('YES')">
YES
</button>
<button
class="button"
[ngClass]="{ 'is-selected is-warning': response == 'MAYBE' }"
[ngClass]="{ 'is-selected is-warning': answer == 'MAYBE' }"
(click)="vote('MAYBE')"
*ngIf="poll.configuration.isMaybeAnswerAvailable"
>
MAYBE
</button>
<button class="button" [ngClass]="{ 'is-selected is-danger': response == 'NO' }" (click)="vote('NO')">
<button class="button" [ngClass]="{ 'is-selected is-danger': answer == 'NO' }" (click)="vote('NO')">
NO
</button>
</div>
</div>
<div class="column">
<div class="buttons has-addons is-right">
<div class="column is-narrow">
<div class="buttons has-addons is-right" (click)="openModal(choice)">
<button class="button is-white">
{{ choice.counts.get(answerEnum.YES) }}
<img class="image is-24x24" src="../../../assets/img/icon_voter_YES.svg" />

View File

@ -1,10 +1,13 @@
import { Component, Input, OnInit } from '@angular/core';
import { MatDialogConfig } from '@angular/material/dialog';
import { Answer } from 'src/app/core/enums/answer.enum';
import { Choice } from '../../../core/models/choice.model';
import { Poll } from '../../../core/models/poll.model';
import { User } from '../../../core/models/user.model';
import { ModalService } from '../../../core/services/modal.service';
import { PollService } from '../../../core/services/poll.service';
import { ChoiceDetailsComponent } from '../../../shared/components/choice-details/choice-details.component';
@Component({
selector: 'app-add-answer',
@ -16,15 +19,19 @@ export class AddAnswerComponent implements OnInit {
@Input() poll: Poll;
@Input() choice: Choice;
public answerEnum = Answer;
public response: Answer;
public answer: Answer;
constructor(private pollService: PollService) {}
constructor(private pollService: PollService, private modalService: ModalService) {}
ngOnInit(): void {}
public vote(response: string): void {
this.response = response as Answer;
console.log(this.response);
this.pollService.saveParticipation(this.choice, this.user, this.response);
public openModal(choice: Choice): void {
const config: MatDialogConfig<Choice> = { data: choice };
this.modalService.openModal<ChoiceDetailsComponent, Choice>(ChoiceDetailsComponent, config);
}
public vote(answer: string): void {
this.answer = answer as Answer;
this.pollService.saveParticipation(this.choice, this.user, this.answer);
}
}

View File

@ -4,11 +4,7 @@
</div>
</div>
<div *ngIf="_isLoading | async" class="columns p-justify-center">
<div class="column has-text-centered">
<p-progressSpinner></p-progressSpinner>
</div>
</div>
<app-spinner *ngIf="_isLoading | async"></app-spinner>
<ng-container *ngIf="!(_isLoading | async)">
<ng-container *ngIf="!(_poll | async)">

View File

@ -8,6 +8,7 @@ import { ModalService } from '../../core/services/modal.service';
import { PollService } from '../../core/services/poll.service';
import { UrlService } from '../../core/services/url.service';
import { UserService } from '../../core/services/user.service';
import { SettingsComponent } from '../../shared/components/settings/settings.component';
@Component({
selector: 'app-participation',
@ -29,7 +30,7 @@ export class ParticipationComponent implements OnInit {
ngOnInit(): void {
if (!this.userService.isCurrentUserIdentifiable()) {
this.modalService.openSettingsComponent();
this.modalService.openModal(SettingsComponent);
}
this.urlService.loadPollFromUrl();
}

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng';
import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { Answer } from '../../../core/enums/answer.enum';
import { Choice } from '../../../core/models/choice.model';
@ -10,16 +10,16 @@ import { Choice } from '../../../core/models/choice.model';
styleUrls: ['./choice-details.component.scss'],
})
export class ChoiceDetailsComponent implements OnInit {
public choice: Choice;
public answerEnum = Answer;
constructor(public ref: DynamicDialogRef, public config: DynamicDialogConfig) {}
constructor(
public dialogRef: MatDialogRef<ChoiceDetailsComponent>,
@Inject(MAT_DIALOG_DATA) public choice: Choice
) {}
ngOnInit(): void {
this.choice = this.config.data;
}
ngOnInit(): void {}
public closeDialog(): void {
this.ref.close();
this.dialogRef.close();
}
}

View File

@ -1,4 +1,4 @@
<div class="columns p-align-center p-justify-between">
<div class="columns">
<div class="column">
<p>Langue</p>
</div>

View File

@ -1,4 +1,4 @@
<div class="columns p-align-center p-justify-between">
<div class="columns">
<div class="column">
<p>Theme</p>
</div>

View File

@ -1,33 +1,25 @@
<p-fieldset legend="Mes données personnelles">
<app-language-selector></app-language-selector>
<app-theme-selector></app-theme-selector>
<div class="columns p-align-center p-justify-between">
<div class="columns">
<div class="column">
<p>Pseudo, nom ou prénom :</p>
</div>
<div class="column" pFocusTrap>
<input
pInputText
type="text"
[(ngModel)]="user.pseudo"
(keydown.enter)="saveChanges()"
autofocus
required
/>
<div class="column">
<input class="input" type="text" [(ngModel)]="user.pseudo" (keydown.enter)="saveChanges()" autofocus required />
</div>
</div>
<div class="columns p-align-center">
<div class="columns">
<div class="column">
<p>Email :</p>
</div>
<div class="column">
<input pInputText type="text" [(ngModel)]="user.email" (keydown.enter)="saveChanges()" required />
<input class="input" type="text" [(ngModel)]="user.email" (keydown.enter)="saveChanges()" required />
</div>
</div>
<br />
<div class="columns">
<button class="column" pButton (click)="saveChanges()">Enregistrer</button>
<button class="column button" (click)="saveChanges()">Enregistrer</button>
</div>
</p-fieldset>

View File

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng';
import { MatDialogRef } from '@angular/material/dialog';
import { Subscription } from 'rxjs';
import { User } from '../../../core/models/user.model';
@ -14,7 +14,7 @@ export class SettingsComponent implements OnInit {
public user: User;
private userSubscription: Subscription;
constructor(public ref: DynamicDialogRef, public config: DynamicDialogConfig, private userService: UserService) {}
constructor(private userService: UserService, public dialogRef: MatDialogRef<SettingsComponent>) {}
ngOnInit(): void {
this.userSubscription = this.userService.user.subscribe((user: User) => {
@ -36,6 +36,6 @@ export class SettingsComponent implements OnInit {
}
public closeDialog(): void {
this.ref.close();
this.dialogRef.close();
}
}

View File

@ -0,0 +1,5 @@
<div class="columns is-centered">
<div class="column is-2">
<mat-spinner></mat-spinner>
</div>
</div>

View File

@ -1,19 +1,19 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ProfileComponent } from './profile.component';
import { SpinnerComponent } from './spinner.component';
describe('ProfileComponent', () => {
let component: ProfileComponent;
let fixture: ComponentFixture<ProfileComponent>;
describe('SpinnerComponent', () => {
let component: SpinnerComponent;
let fixture: ComponentFixture<SpinnerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ProfileComponent],
declarations: [SpinnerComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProfileComponent);
fixture = TestBed.createComponent(SpinnerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@ -0,0 +1,8 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-spinner',
templateUrl: './spinner.component.html',
styleUrls: ['./spinner.component.scss'],
})
export class SpinnerComponent {}

View File

@ -1,27 +1,19 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatStepperModule } from '@angular/material/stepper';
import { TranslateModule } from '@ngx-translate/core';
import { ChartsModule } from 'ng2-charts';
import {
ButtonModule,
ConfirmDialogModule,
DialogModule,
DialogService,
DynamicDialogModule,
FieldsetModule,
FocusTrapModule,
InputSwitchModule,
InputTextareaModule,
MessageModule,
PanelModule,
ProgressSpinnerModule,
SidebarModule,
StepsModule,
ToastModule,
TooltipModule,
} from 'primeng';
import { ConfirmationService, MessageService } from 'primeng/api';
import { ChoiceDetailsComponent } from './components/choice-details/choice-details.component';
import { CommentsComponent } from './components/comments/comments.component';
@ -30,40 +22,38 @@ import { PageNotFoundComponent } from './components/page-not-found/page-not-foun
import { LanguageSelectorComponent } from './components/selectors/language-selector/language-selector.component';
import { ThemeSelectorComponent } from './components/selectors/theme-selector/theme-selector.component';
import { SettingsComponent } from './components/settings/settings.component';
import { SpinnerComponent } from './components/spinner/spinner.component';
const MODULE_LIST = [CommonModule, ChartsModule, FormsModule, TranslateModule];
const PRIMENG_MODULE_LIST = [
ButtonModule,
ConfirmDialogModule,
DialogModule,
DynamicDialogModule,
FieldsetModule,
FocusTrapModule,
InputSwitchModule,
InputTextareaModule,
MessageModule,
PanelModule,
ProgressSpinnerModule,
SidebarModule,
StepsModule,
ToastModule,
TooltipModule,
];
const COMPONENT_LIST = [
CommentsComponent,
const COMPONENTS = [
ChoiceDetailsComponent,
ThemeSelectorComponent,
CommentsComponent,
FeedbackComponent,
LanguageSelectorComponent,
PageNotFoundComponent,
SettingsComponent,
FeedbackComponent,
SpinnerComponent,
ThemeSelectorComponent,
];
const ANGULAR_MODULES = [CommonModule, ChartsModule, FormsModule, TranslateModule];
const MATERIAL_MODULES = [
MatButtonModule,
MatCheckboxModule,
MatDatepickerModule,
MatDialogModule,
MatFormFieldModule,
MatInputModule,
MatProgressSpinnerModule,
MatSidenavModule,
MatSlideToggleModule,
MatSnackBarModule,
MatStepperModule,
];
@NgModule({
declarations: COMPONENT_LIST,
imports: [...MODULE_LIST, ...PRIMENG_MODULE_LIST],
exports: [...MODULE_LIST, ...PRIMENG_MODULE_LIST, ...COMPONENT_LIST],
providers: [ConfirmationService, MessageService, DialogService],
entryComponents: [ChoiceDetailsComponent, SettingsComponent],
declarations: COMPONENTS,
imports: [...ANGULAR_MODULES, ...MATERIAL_MODULES],
exports: [...ANGULAR_MODULES, ...MATERIAL_MODULES, ...COMPONENTS],
})
export class SharedModule {}

View File

@ -6,6 +6,8 @@
<meta content="width=device-width, initial-scale=1" name="viewport" />
<link href="favicon.ico" rel="icon" type="image/x-icon" />
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
</head>
<body>
<app-root></app-root>

View File

@ -1,4 +1,28 @@
@charset "utf-8";
// Custom Theming for Angular Material
// For more information: https://material.angular.io/guide/theming
@import '~@angular/material/theming';
// Plus imports for other components in your app.
// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once!
@include mat-core();
// import your custom themes
@import 'theme-light.scss';
@import 'theme-dark.scss';
@import 'theme-contrast.scss';
.theme-light {
@include angular-material-theme($theme-light);
}
.theme-dark {
@include angular-material-theme($theme-dark);
}
.theme-contrast {
@include angular-material-theme($theme-contrast);
}
/*
You can add global.scss styles to this file, and also import other style files
==================================================
@ -33,3 +57,13 @@
@import './styles/partials/images';
// responsive, mobile first goal
@import './styles/partials/responsive';
// reset suggested by material-angular
html,
body {
height: 100%;
}
body {
margin: 0;
font-family: Roboto, 'Helvetica Neue', sans-serif;
}

16
src/theme-contrast.scss Normal file
View File

@ -0,0 +1,16 @@
// Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/
$theme-contrast-primary: mat-palette($mat-indigo);
$theme-contrast-accent: mat-palette($mat-pink, A200, A100, A400);
// The warn palette is optional (defaults to red).
$theme-contrast-warn: mat-palette($mat-red);
// Create the theme object (a Sass map containing all of the palettes).
$theme-contrast: mat-light-theme($theme-contrast-primary, $theme-contrast-accent, $theme-contrast-warn);
// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
// that you are using.
@include angular-material-theme($theme-contrast);

16
src/theme-dark.scss Normal file
View File

@ -0,0 +1,16 @@
// Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/
$theme-dark-primary: mat-palette($mat-indigo);
$theme-dark-accent: mat-palette($mat-pink, A200, A100, A400);
// The warn palette is optional (defaults to red).
$theme-dark-warn: mat-palette($mat-red);
// Create the theme object (a Sass map containing all of the palettes).
$theme-dark: mat-dark-theme($theme-dark-primary, $theme-dark-accent, $theme-dark-warn);
// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
// that you are using.
@include angular-material-theme($theme-dark);

16
src/theme-light.scss Normal file
View File

@ -0,0 +1,16 @@
// Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/
$theme-light-primary: mat-palette($mat-indigo);
$theme-light-accent: mat-palette($mat-pink, A200, A100, A400);
// The warn palette is optional (defaults to red).
$theme-light-warn: mat-palette($mat-red);
// Create the theme object (a Sass map containing all of the palettes).
$theme-light: mat-light-theme($theme-light-primary, $theme-light-accent, $theme-light-warn);
// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
// that you are using.
@include angular-material-theme($theme-light);

238
yarn.lock
View File

@ -217,6 +217,11 @@
glob "7.1.2"
yargs "15.3.0"
"@angular/material@^9.2.4":
version "9.2.4"
resolved "https://registry.yarnpkg.com/@angular/material/-/material-9.2.4.tgz#2666ef606fbb88d60f8e2f18c5e4f94a3dd572d8"
integrity sha512-LkoTXE6B0slvMhvfZDdPWaz4yaYLkaAp5VSPunI9pxGsPxzqEV9e210wC1/sjG/76Nk8Ep7/2z9XKac8Q9bMwA==
"@angular/platform-browser-dynamic@^9.0.7":
version "9.1.11"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-9.1.11.tgz#82af336b05e0d7b7478a2ca7f6282825b211f340"
@ -658,6 +663,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
"@babel/plugin-syntax-import-meta@^7.8.3":
version "7.10.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.1.tgz#3e59120ed8b3c2ccc5abb1cfc7aaa3ea01cd36b6"
integrity sha512-ypC4jwfIVF72og0dgvEcFRdOM2V9Qm1tu7RGmdZOlhsccyK0wisXmMObGuWEOd5jQ+K9wcIgSNftCpk2vkjUfQ==
dependencies:
"@babel/helper-plugin-utils" "^7.10.1"
"@babel/plugin-syntax-json-strings@^7.8.0", "@babel/plugin-syntax-json-strings@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
@ -1504,12 +1516,10 @@
resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-12.1.2.tgz#0c6f24249953a79cfc2d581b2cd1b5d6d338d9db"
integrity sha512-ZudJsqIxTKlLmPoqK8gJY3UpMGujR0Xm7HfXL6AR79yGRS23QqpjAhMfx4v5qUCcHMmQ9/78bW8QJLfp31c7vQ==
"@ngx-translate/http-loader@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@ngx-translate/http-loader/-/http-loader-4.0.0.tgz#8a555248ad4b7d513460fcec9da25b0447962f1d"
integrity sha512-x8LumqydWD7eX9yQTAVeoCM9gFUIGVTUjZqbxdAUavAA3qVnk9wCQux7iHLPXpydl8vyQmLoPQR+fFU+DUDOMA==
dependencies:
tslib "^1.9.0"
"@ngx-translate/http-loader@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@ngx-translate/http-loader/-/http-loader-5.0.0.tgz#2aea7b5563c6d1afdcc428cabcc8b43ec0ec2d61"
integrity sha512-8+aV7N52qed+6t4LIu4Yru/PkeBX4TR2ioXGwXzQE5syqSLTj/8TgKQIi3i2Z61ZhPxQG1qrGbapUoGQzUDVeg==
"@nodelib/fs.scandir@2.1.3":
version "2.1.3"
@ -1627,9 +1637,9 @@
"@babel/types" "^7.3.0"
"@types/chart.js@^2.7.48":
version "2.9.21"
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.21.tgz#ecb85f1aac2d7b28310df8cc9c88b6b5db1b91bd"
integrity sha512-bjwdC+SvE/c3rHYYcX8UbRmTvjEUP9Dhuk/iWXygH6EFDYf4OcofnwxBpJx+A4qU3YBr54a7wtPdswfg9Yt3rg==
version "2.9.22"
resolved "https://registry.yarnpkg.com/@types/chart.js/-/chart.js-2.9.22.tgz#e52fd922aced217867445c2913b68ce98b1c2b5f"
integrity sha512-CneMxwh2T5fyMpXE5fuprTTmFtlLyZUFq1A3laUrCgOblDzupgiohrFg3jjsTIrqRI5K4qLZdrLN4zT9/MY5Dw==
dependencies:
moment "^2.10.2"
@ -1746,11 +1756,6 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
"@types/uuid@^8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0"
integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw==
"@types/webpack-sources@^0.1.5":
version "0.1.8"
resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.8.tgz#078d75410435993ec8a0a2855e88706f3f751f81"
@ -1773,40 +1778,40 @@
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^3.0.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.2.0.tgz#7fb997f391af32ae6ca1dbe56bcefe4dd30bda14"
integrity sha512-t9RTk/GyYilIXt6BmZurhBzuMT9kLKw3fQoJtK9ayv0tXTlznXEAnx07sCLXdkN3/tZDep1s1CEV95CWuARYWA==
version "3.3.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.3.0.tgz#89518e5c5209a349bde161c3489b0ec187ae5d37"
integrity sha512-Ybx/wU75Tazz6nU2d7nN6ll0B98odoiYLXwcuwS5WSttGzK46t0n7TPRQ4ozwcTv82UY6TQoIvI+sJfTzqK9dQ==
dependencies:
"@typescript-eslint/experimental-utils" "3.2.0"
"@typescript-eslint/experimental-utils" "3.3.0"
functional-red-black-tree "^1.0.1"
regexpp "^3.0.0"
semver "^7.3.2"
tsutils "^3.17.1"
"@typescript-eslint/experimental-utils@3.2.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.2.0.tgz#4dab8fc9f44f059ec073470a81bb4d7d7d51e6c5"
integrity sha512-UbJBsk+xO9dIFKtj16+m42EvUvsjZbbgQ2O5xSTSfVT1Z3yGkL90DVu0Hd3029FZ5/uBgl+F3Vo8FAcEcqc6aQ==
"@typescript-eslint/experimental-utils@3.3.0":
version "3.3.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.3.0.tgz#d72a946e056a83d4edf97f3411cceb639b0b8c87"
integrity sha512-d4pGIAbu/tYsrPrdHCQ5xfadJGvlkUxbeBB56nO/VGmEDi/sKmfa5fGty5t5veL1OyJBrUmSiRn1R1qfVDydrg==
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/typescript-estree" "3.2.0"
"@typescript-eslint/typescript-estree" "3.3.0"
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
"@typescript-eslint/parser@^3.0.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.2.0.tgz#d9d7867456b1b8ecae9e724269b0bc932f06cbca"
integrity sha512-Vhu+wwdevDLVDjK1lIcoD6ZbuOa93fzqszkaO3iCnmrScmKwyW/AGkzc2UvfE5TCoCXqq7Jyt6SOXjsIlpqF4A==
version "3.3.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.3.0.tgz#fcae40012ded822aa8b2739a1a03a4e3c5bbb7bb"
integrity sha512-a7S0Sqn/+RpOOWTcaLw6RD4obsharzxmgMfdK24l364VxuBODXjuJM7ImCkSXEN7oz52aiZbXSbc76+2EsE91w==
dependencies:
"@types/eslint-visitor-keys" "^1.0.0"
"@typescript-eslint/experimental-utils" "3.2.0"
"@typescript-eslint/typescript-estree" "3.2.0"
"@typescript-eslint/experimental-utils" "3.3.0"
"@typescript-eslint/typescript-estree" "3.3.0"
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/typescript-estree@3.2.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.2.0.tgz#c735f1ca6b4d3cd671f30de8c9bde30843e7ead8"
integrity sha512-uh+Y2QO7dxNrdLw7mVnjUqkwO/InxEqwN0wF+Za6eo3coxls9aH9kQ/5rSvW2GcNanebRTmsT5w1/92lAOb1bA==
"@typescript-eslint/typescript-estree@3.3.0":
version "3.3.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.3.0.tgz#841ffed25c29b0049ebffb4c2071268a34558a2a"
integrity sha512-3SqxylENltEvJsjjMSDCUx/edZNSC7wAqifUU1Ywp//0OWEZwMZJfecJud9XxJ/40rAKEbJMKBOQzeOjrLJFzQ==
dependencies:
debug "^4.1.1"
eslint-visitor-keys "^1.1.0"
@ -2021,9 +2026,9 @@ acorn-node@^1.3.0:
xtend "^4.0.2"
acorn-walk@^7.0.0, acorn-walk@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e"
integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==
version "7.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
acorn@^6.2.1:
version "6.4.1"
@ -2476,13 +2481,14 @@ babel-plugin-jest-hoist@^26.0.0:
"@types/babel__traverse" "^7.0.6"
babel-preset-current-node-syntax@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz#fb4a4c51fe38ca60fede1dc74ab35eb843cb41d6"
integrity sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw==
version "0.1.3"
resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.3.tgz#b4b547acddbf963cba555ba9f9cbbb70bfd044da"
integrity sha512-uyexu1sVwcdFnyq9o8UQYsXwXflIh8LvrF5+cKrYam93ned1CStffB3+BEcsxGSgagoA3GEyjDqO4a/58hyPYQ==
dependencies:
"@babel/plugin-syntax-async-generators" "^7.8.4"
"@babel/plugin-syntax-bigint" "^7.8.3"
"@babel/plugin-syntax-class-properties" "^7.8.3"
"@babel/plugin-syntax-import-meta" "^7.8.3"
"@babel/plugin-syntax-json-strings" "^7.8.3"
"@babel/plugin-syntax-logical-assignment-operators" "^7.8.3"
"@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
@ -3049,9 +3055,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001043:
version "1.0.30001081"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001081.tgz#40615a3c416a047c5a4d45673e5257bf128eb3b5"
integrity sha512-iZdh3lu09jsUtLE6Bp8NAbJskco4Y3UDtkR3GTCJGsbMowBU5IWDFF79sV2ws7lSqTzWyKazxam2thasHymENQ==
version "1.0.30001084"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001084.tgz#00e471931eaefbeef54f46aa2203914d3c165669"
integrity sha512-ftdc5oGmhEbLUuMZ/Qp3mOpzfZLCxPYKcvGv6v2dJJ+8EdqcvZRbAGOiLmkM/PV1QGta/uwBs8/nCl6sokDW6w==
canonical-path@1.0.0:
version "1.0.0"
@ -4368,9 +4374,9 @@ ee-first@1.1.1:
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
electron-to-chromium@^1.3.413:
version "1.3.469"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.469.tgz#05a06ec9c915127ed6c9f5b657cd1d7e11a3f944"
integrity sha512-O9JM6ZsFhS0uy0S2Y3G8EoNfqio3srdxCuwuJh8tKgQKa6rf7je/xQ3TIoiEaEtpf2/qFFLAGt/xB4MjuUZqRw==
version "1.3.475"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.475.tgz#67688cc82c342f39594a412286e975eda45d8412"
integrity sha512-vcTeLpPm4+ccoYFXnepvkFt0KujdyrBU19KNEO40Pnkhta6mUi2K0Dn7NmpRcNz7BvysnSqeuIYScP003HWuYg==
elliptic@^6.0.0, elliptic@^6.5.2:
version "6.5.2"
@ -4424,7 +4430,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
enhanced-resolve@4.1.1, enhanced-resolve@^4.1.0:
enhanced-resolve@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66"
integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==
@ -4433,6 +4439,15 @@ enhanced-resolve@4.1.1, enhanced-resolve@^4.1.0:
memory-fs "^0.5.0"
tapable "^1.0.0"
enhanced-resolve@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.2.0.tgz#5d43bda4a0fd447cb0ebbe71bef8deff8805ad0d"
integrity sha512-S7eiFb/erugyd1rLb6mQ3Vuq+EXHv5cpCkNqqIkYkBgN2QdFnyCZzFBleqwGEx4lgNGYij81BWnCrFNK7vxvjQ==
dependencies:
graceful-fs "^4.1.2"
memory-fs "^0.5.0"
tapable "^1.0.0"
enquirer@^2.3.5:
version "2.3.5"
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.5.tgz#3ab2b838df0a9d8ab9e7dff235b0e8712ef92381"
@ -4478,21 +4493,21 @@ errorhandler@^1.5.1:
escape-html "~1.0.3"
es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5:
version "1.17.5"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9"
integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==
version "1.17.6"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a"
integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==
dependencies:
es-to-primitive "^1.2.1"
function-bind "^1.1.1"
has "^1.0.3"
has-symbols "^1.0.1"
is-callable "^1.1.5"
is-regex "^1.0.5"
is-callable "^1.2.0"
is-regex "^1.1.0"
object-inspect "^1.7.0"
object-keys "^1.1.1"
object.assign "^4.1.0"
string.prototype.trimleft "^2.1.1"
string.prototype.trimright "^2.1.1"
string.prototype.trimend "^1.0.1"
string.prototype.trimstart "^1.0.1"
es-to-primitive@^1.2.1:
version "1.2.1"
@ -4623,9 +4638,9 @@ eslint-config-prettier@^6.11.0:
get-stdin "^6.0.0"
eslint-plugin-prettier@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca"
integrity sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ==
version "3.1.4"
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz#168ab43154e2ea57db992a2cd097c828171f75c2"
integrity sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==
dependencies:
prettier-linter-helpers "^1.0.0"
@ -4646,9 +4661,9 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.0:
estraverse "^4.1.1"
eslint-utils@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd"
integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==
version "2.1.0"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
dependencies:
eslint-visitor-keys "^1.1.0"
@ -4882,9 +4897,9 @@ expect@^26.0.1:
jest-regex-util "^26.0.0"
express-urlrewrite@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/express-urlrewrite/-/express-urlrewrite-1.2.0.tgz#8e667b7761ff1c7ffdb0efa05d64035387c823eb"
integrity sha1-jmZ7d2H/HH/9sO+gXWQDU4fII+s=
version "1.3.0"
resolved "https://registry.yarnpkg.com/express-urlrewrite/-/express-urlrewrite-1.3.0.tgz#208c9db9a187c504378343dadb10657d7483d1e6"
integrity sha512-xy3WZqA9EIfb51FkL1R0EqW91Z8lMi9ohp/WrNxKukvQulybqvh7+OsGiw9JOD51NrGsSuWi2hqOv7GW+DGz1w==
dependencies:
debug "*"
path-to-regexp "^1.0.3"
@ -5023,9 +5038,9 @@ fast-glob@^2.0.2:
micromatch "^3.1.10"
fast-glob@^3.1.1, fast-glob@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d"
integrity sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==
version "3.2.4"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3"
integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
@ -5216,11 +5231,9 @@ follow-redirects@1.5.10:
debug "=3.1.0"
follow-redirects@^1.0.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.11.0.tgz#afa14f08ba12a52963140fe43212658897bc0ecb"
integrity sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==
dependencies:
debug "^3.0.0"
version "1.12.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.12.0.tgz#ff0ccf85cf2c867c481957683b5f91b75b25e240"
integrity sha512-JgawlbfBQKjbKegPn8vUsvJqplE7KHJuhGO4yPcb+ZOIYKSr+xobMVlfRBToZwZUUxy7lFiKBdFNloz9ui368Q==
fontkit@^1.8.0:
version "1.8.1"
@ -6087,7 +6100,7 @@ ini@1.3.5, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
inquirer@7.1.0, inquirer@^7.0.0:
inquirer@7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29"
integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==
@ -6106,6 +6119,25 @@ inquirer@7.1.0, inquirer@^7.0.0:
strip-ansi "^6.0.0"
through "^2.3.6"
inquirer@^7.0.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.2.0.tgz#63ce99d823090de7eb420e4bb05e6f3449aa389a"
integrity sha512-E0c4rPwr9ByePfNlTIB8z51kK1s2n6jrHuJeEHENl/sbq2G/S1auvibgEwNR4uSyiU+PiYHqSwsgGiXjG8p5ZQ==
dependencies:
ansi-escapes "^4.2.1"
chalk "^3.0.0"
cli-cursor "^3.1.0"
cli-width "^2.0.0"
external-editor "^3.0.3"
figures "^3.0.0"
lodash "^4.17.15"
mute-stream "0.0.8"
run-async "^2.4.0"
rxjs "^6.5.3"
string-width "^4.1.0"
strip-ansi "^6.0.0"
through "^2.3.6"
inside@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/inside/-/inside-1.0.0.tgz#db45e993573cdb3db70b9832e8285bad46424770"
@ -6207,7 +6239,7 @@ is-buffer@^1.1.5:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
is-callable@^1.1.4, is-callable@^1.1.5:
is-callable@^1.1.4, is-callable@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb"
integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==
@ -6434,7 +6466,7 @@ is-promise@^2.1.0:
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1"
integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==
is-regex@^1.0.4, is-regex@^1.0.5:
is-regex@^1.0.4, is-regex@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff"
integrity sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==
@ -7220,9 +7252,9 @@ jsprim@^1.2.2:
verror "1.10.0"
jszip@^3.1.3:
version "3.4.0"
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.4.0.tgz#1a69421fa5f0bb9bc222a46bca88182fba075350"
integrity sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==
version "3.5.0"
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.5.0.tgz#b4fd1f368245346658e781fec9675802489e15f6"
integrity sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==
dependencies:
lie "~3.3.0"
pako "~1.0.2"
@ -7375,9 +7407,9 @@ lines-and-columns@^1.1.6:
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
lint-staged@^10.1.7:
version "10.2.10"
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.2.10.tgz#f0f78bf8786bbe90e1775a0dc540f7f12b6a79b2"
integrity sha512-dgelFaNH6puUGAcU+OVMgbfpKSerNYsPSn6+nlbRDjovL0KigpsVpCu0PFZG6BJxX8gnHJqaZlR9krZamQsb0w==
version "10.2.11"
resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.2.11.tgz#713c80877f2dc8b609b05bc59020234e766c9720"
integrity sha512-LRRrSogzbixYaZItE2APaS4l2eJMjjf5MbclRZpLJtcQJShcvUzKXsNeZgsLIZ0H0+fg2tL4B59fU9wHIHtFIA==
dependencies:
chalk "^4.0.0"
cli-truncate "2.1.0"
@ -7396,9 +7428,9 @@ lint-staged@^10.1.7:
stringify-object "^3.3.0"
listr2@^2.1.0:
version "2.1.3"
resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.1.3.tgz#f527e197de12ad8c488c566921fa2da34cbc67f6"
integrity sha512-6oy3QhrZAlJGrG8oPcRp1hix1zUpb5AvyvZ5je979HCyf48tIj3Hn1TG5+rfyhz30t7HfySH/OIaVbwrI2kruA==
version "2.1.8"
resolved "https://registry.yarnpkg.com/listr2/-/listr2-2.1.8.tgz#8af7ebc70cdbe866ddbb6c80909142bd45758f1f"
integrity sha512-Op+hheiChfAphkJ5qUxZtHgyjlX9iNnAeFS/S134xw7mVSg0YVrQo1IY4/K+ElY6XgOPg2Ij4z07urUXR+YEew==
dependencies:
chalk "^4.0.0"
cli-truncate "^2.1.0"
@ -10453,6 +10485,11 @@ shellwords@^0.1.1:
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
short-unique-id@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/short-unique-id/-/short-unique-id-3.0.3.tgz#6efe5e717c32b5ec946f3c9e22f8e18eff75a9b8"
integrity sha512-g8StBeiZN4bAtJlZIZQ3C7RNRjtTdJhwgq4WRHC30+z2dbuE/A0Z51CafHsgpwJHYllW4lyH17EKiyBe4W/AeA==
signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
@ -10792,9 +10829,9 @@ stack-utils@^2.0.2:
escape-string-regexp "^2.0.0"
static-eval@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.5.tgz#f0782e66999c4b3651cda99d9ce59c507d188f71"
integrity sha512-nNbV6LbGtMBgv7e9LFkt5JV8RVlRsyJrphfAt9tOtBBW/SfnzZDf2KnS72an8e434A+9e/BmJuTxeGPvrAK7KA==
version "2.1.0"
resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.1.0.tgz#a16dbe54522d7fa5ef1389129d813fd47b148014"
integrity sha512-agtxZ/kWSsCkI5E4QifRwsaPs0P0JmZV6dkLz6ILYfFYQGn+5plctanRN+IC8dJRiFkyXHrwEE3W9Wmx67uDbw==
dependencies:
escodegen "^1.11.1"
@ -10918,7 +10955,7 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
string.prototype.trimend@^1.0.0:
string.prototype.trimend@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==
@ -10926,25 +10963,7 @@ string.prototype.trimend@^1.0.0:
define-properties "^1.1.3"
es-abstract "^1.17.5"
string.prototype.trimleft@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc"
integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.5"
string.prototype.trimstart "^1.0.0"
string.prototype.trimright@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3"
integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.5"
string.prototype.trimend "^1.0.0"
string.prototype.trimstart@^1.0.0:
string.prototype.trimstart@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==
@ -11224,9 +11243,9 @@ terser@4.6.10:
source-map-support "~0.5.12"
terser@^4.1.2, terser@^4.4.3:
version "4.7.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.7.0.tgz#15852cf1a08e3256a80428e865a2fa893ffba006"
integrity sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw==
version "4.8.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
dependencies:
commander "^2.20.0"
source-map "~0.6.1"
@ -11850,11 +11869,6 @@ uuid@^7.0.3:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
uuid@^8.0.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d"
integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==
v8-compile-cache@^2.0.3:
version "2.1.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745"