Merge branch 'features/replace_primeng_by_material' into 'develop'

replace primeng by material && improvments

See merge request framasoft/framadate/funky-framadate-front!41
archived-develop
seraph_ino 3 years ago
commit d722103c05

@ -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 ?",

@ -1,116 +1,116 @@
{
"name": "framadate-funky-frontend",
"version": "1.0.0",
"license": "AGPL-3.0-or-later",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build:prod": "ng build --prod",
"build:prod:stats": "ng build --prod --stats-json",
"build:prod:gitlabpage": "ng build --prod --baseHref=/framadate/funky-framadate-front/",
"build:prod:demobliss": "ng build --prod --baseHref=https://framadate-api.cipherbliss.com",
"test": "jest",
"test:watch": "jest --watch",
"test:ci": "jest --ci",
"lint": "prettier --write \"src/**/*.{js,jsx,ts,tsx,md,html,css,scss}\"",
"e2e": "ng e2e",
"format:check": "prettier --list-different \"src/{app,environments,assets}/**/*{.ts,.js,.json,.css,.scss}\"",
"format:all": "prettier --write \"src/**/*.{js,jsx,ts,tsx,md,html,css,scss}\"",
"trans": "ng xi18n --output-path=src/locale --i18n-locale=fr",
"compodoc": "compodoc -p tsconfig.app.json",
"mock:server": "json-server --port 8000 --watch ./mocks/db.json --routes ./mocks/routes.json",
"start:proxy": "ng serve --proxy-config proxy.conf.json",
"start:proxymock": "concurrently --kill-others \"yarn mock:server\" \"yarn start:proxy\""
},
"private": false,
"dependencies": {
"@angular/animations": "^9.1.1",
"@angular/cdk": "^9.2.2",
"@angular/common": "^9.0.7",
"@angular/compiler": "^9.0.7",
"@angular/core": "^9.0.7",
"@angular/forms": "^9.0.7",
"@angular/localize": "^9.1.1",
"@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",
"angular-date-value-accessor": "^1.0.2",
"axios": "^0.19.2",
"bulma": "^0.9.0",
"bulma-switch": "^2.0.0",
"chart.js": "^2.9.3",
"fork-awesome": "^1.1.7",
"ng2-charts": "^2.3.0",
"ngx-clipboard": "^13.0.0",
"ngx-markdown": "^9.0.0",
"ngx-webstorage": "^5.0.0",
"primeng": "^9.0.6",
"quill": "^1.3.7",
"rxjs": "^6.5.5",
"rxjs-compat": "^6.5.5",
"tslib": "<2.0.0",
"uuid": "^8.0.0",
"zone.js": "^0.10.3"
},
"devDependencies": {
"@angular-builders/jest": "^9.0.1",
"@angular-devkit/build-angular": "^0.901.2",
"@angular/cli": "^9.1.2",
"@angular/compiler-cli": "^9.1.1",
"@angular/language-service": "^9.0.7",
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.5",
"@babel/preset-typescript": "^7.9.0",
"@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",
"concurrently": "^5.2.0",
"eslint": "^7.0.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.3",
"husky": "^4.2.5",
"jest": "^26.0.0",
"jest-environment-jsdom-sixteen": "^1.0.3",
"jest-preset-angular": "^8.1.3",
"json-server": "^0.16.1",
"lint-staged": "^10.1.7",
"prettier": "^2.0.5",
"protractor": "~7.0.0",
"ts-jest": "^26.0.0",
"ts-mockito": "^2.5.0",
"ts-node": "^8.10.1",
"typescript": "<3.9.0"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,md,html,css,scss}": [
"prettier --write",
"git add"
],
"*.js": [
"prettier --write"
]
},
"jest": {
"preset": "jest-preset-angular",
"setupFilesAfterEnv": [
"<rootDir>/src/jest.setup.ts"
],
"testEnvironment": "jest-environment-jsdom-sixteen",
"transform": {
"^.+\\.(ts|html)$": "ts-jest",
"^.+\\.jsx?$": "babel-jest"
}
}
"name": "framadate-funky-frontend",
"version": "1.0.0",
"license": "AGPL-3.0-or-later",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build:prod": "ng build --prod",
"build:prod:stats": "ng build --prod --stats-json",
"build:prod:gitlabpage": "ng build --prod --baseHref=/framadate/funky-framadate-front/",
"build:prod:demobliss": "ng build --prod --baseHref=https://framadate-api.cipherbliss.com",
"test": "jest",
"test:watch": "jest --watch",
"test:ci": "jest --ci",
"lint": "prettier --write \"src/**/*.{js,jsx,ts,tsx,md,html,css,scss}\"",
"e2e": "ng e2e",
"format:check": "prettier --list-different \"src/{app,environments,assets}/**/*{.ts,.js,.json,.css,.scss}\"",
"format:all": "prettier --write \"src/**/*.{js,jsx,ts,tsx,md,html,css,scss}\"",
"trans": "ng xi18n --output-path=src/locale --i18n-locale=fr",
"compodoc": "compodoc -p tsconfig.app.json",
"mock:server": "json-server --port 8000 --watch ./mocks/db.json --routes ./mocks/routes.json",
"start:proxy": "ng serve --proxy-config proxy.conf.json",
"start:proxymock": "concurrently --kill-others \"yarn mock:server\" \"yarn start:proxy\""
},
"private": false,
"dependencies": {
"@angular/animations": "^9.1.1",
"@angular/cdk": "^9.2.2",
"@angular/common": "^9.0.7",
"@angular/compiler": "^9.0.7",
"@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": "^5.0.0",
"angular-date-value-accessor": "^1.0.2",
"axios": "^0.19.2",
"bulma": "^0.9.0",
"bulma-switch": "^2.0.0",
"chart.js": "^2.9.3",
"fork-awesome": "^1.1.7",
"ng2-charts": "^2.3.0",
"ngx-clipboard": "^13.0.0",
"ngx-markdown": "^9.0.0",
"ngx-webstorage": "^5.0.0",
"primeng": "^9.0.6",
"quill": "^1.3.7",
"rxjs": "^6.5.5",
"rxjs-compat": "^6.5.5",
"short-unique-id": "^3.0.3",
"tslib": "<2.0.0",
"zone.js": "^0.10.3"
},
"devDependencies": {
"@angular-builders/jest": "^9.0.1",
"@angular-devkit/build-angular": "^0.901.2",
"@angular/cli": "^9.1.2",
"@angular/compiler-cli": "^9.1.1",
"@angular/language-service": "^9.0.7",
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.9.5",
"@babel/preset-typescript": "^7.9.0",
"@compodoc/compodoc": "^1.1.11",
"@types/jest": "^26.0.0",
"@types/node": "^14.0.1",
"@typescript-eslint/eslint-plugin": "^3.0.0",
"@typescript-eslint/parser": "^3.0.0",
"babel-jest": "^26.0.0",
"concurrently": "^5.2.0",
"eslint": "^7.0.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.3",
"husky": "^4.2.5",
"jest": "^26.0.0",
"jest-environment-jsdom-sixteen": "^1.0.3",
"jest-preset-angular": "^8.1.3",
"json-server": "^0.16.1",
"lint-staged": "^10.1.7",
"prettier": "^2.0.5",
"protractor": "~7.0.0",
"ts-jest": "^26.0.0",
"ts-mockito": "^2.5.0",
"ts-node": "^8.10.1",
"typescript": "<3.9.0"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,md,html,css,scss}": [
"prettier --write",
"git add"
],
"*.js": [
"prettier --write"
]
},
"jest": {
"preset": "jest-preset-angular",
"setupFilesAfterEnv": [
"<rootDir>/src/jest.setup.ts"
],
"testEnvironment": "jest-environment-jsdom-sixteen",
"transform": {
"^.+\\.(ts|html)$": "ts-jest",
"^.+\\.jsx?$": "babel-jest"
}
}
}

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

@ -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>

@ -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();
});

@ -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);
}
}

@ -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,50 +18,12 @@
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<a class="navbar-item" routerLink="/" routerLinkActive="is-active">
Home
<a class="navbar-item" routerLink="administration" routerLinkActive="is-active">
Créer un sondage
</a>
<a class="navbar-item" routerLink="administration/user-polls" routerLinkActive="is-active">
Mes sondages
</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">
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">

@ -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, private apiService: ApiService) {}
constructor(private userService: UserService, private modalService: ModalService) {}
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);
}
}

@ -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>

@ -1,19 +1,58 @@
<nav>
<a class="button" routerLink="oldstuff/home" routerLinkActive="active">
<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>
</div>
</div>
<hr />
<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" 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>
<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" 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>
<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>

@ -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[] = [];
constructor(private apiService: ApiService, private userService: UserService) {}
public ngOnInit(): void {
this.getSlugs();
}
ngOnInit(): void {}
public async getSlugs(): Promise<void> {
this.slugsAvailables = await this.apiService.getAllPollsSlugs();
}
}

@ -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],
})

@ -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);
}
}

@ -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;
})
);

@ -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', () => {

@ -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();
}

@ -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,
});
}
}

@ -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')];

@ -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);
}
}

@ -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>> {

@ -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', () => {

@ -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);
}
}

@ -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);

@ -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', () => {

@ -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();
}
}

@ -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;
}
}

@ -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({

@ -1,12 +1,21 @@
<div class="container has-text-centered">
<div class="columns">
<div class="column">
<app-stepper></app-stepper>
</div>
</div>
<div class="columns">
<div class="column">
<router-outlet></router-outlet>
</div>
<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 [poll]="poll"></app-stepper>
</div>
</div>
</ng-container>
</ng-container>

@ -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();
}
}

@ -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,

@ -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();
});
});

@ -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 {}
}

@ -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>

@ -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();
});
});

@ -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({