Merge branch 'testing-admin-rework' into 'develop'

Testing admin rework

See merge request framasoft/framadate/funky-framadate-front!45
This commit is contained in:
ty kayn 2020-10-31 16:43:17 +01:00
commit c3972694cb
33 changed files with 437 additions and 181 deletions

View File

@ -2,7 +2,8 @@
image: node:latest image: node:latest
stages: stages:
- pages - build
# - pages
- test - test
# - e2e # - e2e
@ -10,38 +11,36 @@ cache:
paths: paths:
- node_modules/ - node_modules/
pages: #pages:
stage: pages # stage: pages
script: # script:
- yarn install --pure-lockfile # - yarn install --pure-lockfile
- yarn build:prod:gitlabpage # - yarn build:prod:gitlabpage
- mv dist/framadate/ public/ # - mv dist/framadate/ public/
artifacts: # artifacts:
paths: # paths:
- public # - public
expire_in: '2 hours' # expire_in: '2 hours'
only: # only:
- develop # - develop
test: #test:
stage: test # stage: test
script: # script:
- npm i # - npm i
- pkill Xvfb # - pkill Xvfb
- npm run test:ci # - npm run test:ci
artifacts: # artifacts:
paths: # paths:
- coverage/ # - coverage/
cache: # cache:
policy: pull # policy: pull
test-build: build:
stage: test stage: build
script: script:
- yarn install --pure-lockfile - yarn install --pure-lockfile
- npx ng build --prod - npx ng build --prod
only:
- master
cache: cache:
policy: pull policy: pull

View File

@ -96,7 +96,7 @@ L'export d'un sondage et des résultats d'un sondage est possible au format CSV.
# Nouveautés secondaires # Nouveautés secondaires
* Choix de réponses possibles. Proposer de ne répondre que «oui» ou rien, ou aller dans la nuance en proposant «oui», «peut-être», «non», « ? ». *# Redondance ou le choix de réponses possibles de la première phrase concerne un autre choix?* * Choix de réponses possibles. Proposer de ne répondre que «oui» ou rien, ou aller dans la nuance en proposant «oui», «peut-être», «non», « ? ». *# Redondance ou le choix de réponses possibles de la première phrase concerne un autre choix?*
* Insertion d'images dans le sondage de type texte, avec des URL uniquement. Une seule image par question possible ou rien. * Insertion d'images dans le sondage de type texte, avec des URL uniquement. Une seule image par title possible ou rien.
* Thème sombre. * Thème sombre.
* Boutons pour copier dans le presse-papier les liens publics et privés / admin des sondages. * Boutons pour copier dans le presse-papier les liens publics et privés / admin des sondages.
* Limiter le nombre de participants maximum * Limiter le nombre de participants maximum

View File

@ -19,7 +19,7 @@
"expires": "2020-12-31" "expires": "2020-12-31"
}, },
"ownerId": 1, "ownerId": 1,
"question": "Quelle date pour le picnic ?", "title": "Quelle date pour le picnic ?",
"description": "Gros badass picnic en plein air ! Come on !" "description": "Gros badass picnic en plein air ! Come on !"
}, },
{ {
@ -37,7 +37,7 @@
"expires": "2020-11-30" "expires": "2020-11-30"
}, },
"ownerId": 2, "ownerId": 2,
"question": "On fait quoi pendant les vacances ?", "title": "On fait quoi pendant les vacances ?",
"description": "Vacances en famille" "description": "Vacances en famille"
} }
], ],

View File

@ -6,10 +6,14 @@
<mat-sidenav-content> <mat-sidenav-content>
<div id="big_container" [class]="themeClass"> <div id="big_container" [class]="themeClass">
<div class="container"> <div class="container">
<mat-slide-toggle (change)="sidenav.toggle()">Dev Menu</mat-slide-toggle>
<app-header [appTitle]="appTitle" [appLogo]="appLogo"></app-header> <app-header [appTitle]="appTitle" [appLogo]="appLogo"></app-header>
<main> <main>
<router-outlet></router-outlet> <router-outlet></router-outlet>
<div *ngIf="devModeEnabled">
<br />
<mat-slide-toggle (change)="sidenav.toggle()" label="dev menu"> </mat-slide-toggle> menu
développeur
</div>
</main> </main>
<app-footer></app-footer> <app-footer></app-footer>
<app-feedback></app-feedback> <app-feedback></app-feedback>

View File

@ -1,12 +1,12 @@
import {Component, OnDestroy, OnInit} from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import {Title} from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import {Subscription} from 'rxjs'; import { Subscription } from 'rxjs';
import {environment} from '../environments/environment'; import { environment } from '../environments/environment';
import {Theme} from './core/enums/theme.enum'; import { Theme } from './core/enums/theme.enum';
import {LanguageService} from './core/services/language.service'; import { LanguageService } from './core/services/language.service';
import {ThemeService} from './core/services/theme.service'; import { ThemeService } from './core/services/theme.service';
import {MockingService} from './core/services/mocking.service'; import { MockingService } from './core/services/mocking.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -18,21 +18,18 @@ export class AppComponent implements OnInit, OnDestroy {
public appLogo: string = environment.appLogo; public appLogo: string = environment.appLogo;
public themeClass: string; public themeClass: string;
public isSidebarOpened = false; public isSidebarOpened = false;
public devModeEnabled = !environment.production;
private themeSubscription: Subscription; private themeSubscription: Subscription;
constructor( constructor(
private titleService: Title, private titleService: Title,
private themeService: ThemeService, private themeService: ThemeService,
private languageService: LanguageService, private languageService: LanguageService // private mockingService: MockingService
private mockingService: MockingService ) {}
) {
}
ngOnInit(): void { ngOnInit(): void {
if (!environment.production) { if (!environment.production) {
this.appTitle += ' [DEV]'; this.appTitle += ' [DEV]';
// TODO: to be removed
this.mockingService.init();
} }
this.titleService.setTitle(this.appTitle); this.titleService.setTitle(this.appTitle);
this.languageService.configureAndInitTranslations(); this.languageService.configureAndInitTranslations();

View File

@ -28,24 +28,15 @@
<div id="navbarBasicExample" class="navbar-menu"> <div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start"> <div class="navbar-start">
<a class="navbar-item" routerLink="/administration" routerLinkActive="is-active"> <a class="navbar-item btn btn--primary" routerLink="administration" routerLinkActive="is-active">
{{ 'config.title' | translate }} <i class="fa fa-plus-circle"></i> {{ 'config.title' | translate }}
</a>
<a class="navbar-item" routerLink="user/polls" routerLinkActive="is-active">
{{ 'config.find_my_polls' | translate }}
</a> </a>
</div> </div>
<div class="navbar-end"> <div class="navbar-end">
<div class="navbar-item" #container> <a class="navbar-item btn btn-primary" routerLink="user/polls" routerLinkActive="is-active">
<div class="buttons has-addons is-centered clickable" (click)="openDialog()"> <i class="fa fa-user"></i> {{ 'config.find_my_polls' | translate }}
<button class="button is-static"><i class="fa fa-user-circle" aria-hidden="true"></i></button> </a>
<button class="button is-static" *ngIf="_user | async">
{{ (_user | async)?.pseudo || 'anonyme' }}
</button>
<button class="button is-static"><i class="fa fa-cogs" aria-hidden="true"></i></button>
</div>
</div>
</div> </div>
</div> </div>
</nav> </nav>

View File

@ -1,5 +1,8 @@
:host { :host {
header { header {
nav {
padding-right: 1em;
}
.container { .container {
padding: 0; padding: 0;
} }

View File

@ -21,8 +21,4 @@ export class HeaderComponent implements OnInit {
constructor(private userService: UserService, private modalService: ModalService) {} constructor(private userService: UserService, private modalService: ModalService) {}
public ngOnInit(): void {} public ngOnInit(): void {}
public openDialog(): void {
this.modalService.openModal(SettingsComponent);
}
} }

View File

@ -1,7 +1,7 @@
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { DateService } from '../services/date.service'; import { DateService } from '../services/date.service';
export class Configuration { export class PollConfiguration {
constructor( constructor(
public isAboutDate: boolean = false, public isAboutDate: boolean = false,
public isProtectedByPassword: boolean = false, public isProtectedByPassword: boolean = false,
@ -16,7 +16,7 @@ export class Configuration {
) )
) {} ) {}
public static isArchived(configuration: Configuration): boolean { public static isArchived(configuration: PollConfiguration): boolean {
return configuration.expires ? DateService.isDateInPast(configuration.expires) : undefined; return configuration.expires ? DateService.isDateInPast(configuration.expires) : undefined;
} }
} }

View File

@ -2,16 +2,16 @@ import { environment } from 'src/environments/environment';
import { Choice } from './choice.model'; import { Choice } from './choice.model';
import { Comment } from './comment.model'; import { Comment } from './comment.model';
import { Configuration } from './configuration.model'; import { PollConfiguration } from './configuration.model';
import { User } from './user.model'; import { User } from './user.model';
export class Poll { export class Poll {
constructor( constructor(
public owner: User, public owner: User,
public slug: string, public slug: string,
public question: string, public title: string,
public description?: string, public description?: string,
public configuration: Configuration = new Configuration(), public configuration: PollConfiguration = new PollConfiguration(),
public comments: Comment[] = [], public comments: Comment[] = [],
public choices: Choice[] = [] public choices: Choice[] = []
) {} ) {}
@ -25,12 +25,12 @@ export class Poll {
} }
public static adaptFromLocalJsonServer( public static adaptFromLocalJsonServer(
item: Pick<Poll, 'owner' | 'question' | 'description' | 'slug' | 'configuration' | 'comments' | 'choices'> item: Pick<Poll, 'owner' | 'title' | 'description' | 'slug' | 'configuration' | 'comments' | 'choices'>
): Poll { ): Poll {
const poll = new Poll( const poll = new Poll(
new User(item.owner.pseudo, item.owner.email, undefined), new User(item.owner.pseudo, item.owner.email, undefined),
item.slug, item.slug,
item.question, item.title,
item.description, item.description,
item.configuration, item.configuration,
item.comments item.comments

View File

@ -47,9 +47,12 @@ export class PollService implements Resolve<Poll> {
} }
public async loadPollBySlug(slug: string): Promise<void> { public async loadPollBySlug(slug: string): Promise<void> {
const poll: Poll | undefined = await this.apiService.getPollBySlug(slug); console.log('slug', slug);
console.log({ loadPollBySlugResponse: poll }); if (slug) {
this.updateCurrentPoll(poll); const poll: Poll | undefined = await this.apiService.getPollBySlug(slug);
console.log({ loadPollBySlugResponse: poll });
this.updateCurrentPoll(poll);
}
} }
public updateCurrentPoll(poll: Poll): void { public updateCurrentPoll(poll: Poll): void {

View File

@ -2,8 +2,12 @@ import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { AdministrationComponent } from './administration.component'; import { AdministrationComponent } from './administration.component';
import { NamingComponent } from './naming/naming.component';
const routes: Routes = [{ path: '', component: AdministrationComponent }]; const routes: Routes = [
{ path: '', component: AdministrationComponent },
{ path: 'naming', component: NamingComponent },
];
@NgModule({ @NgModule({
imports: [RouterModule.forChild(routes)], imports: [RouterModule.forChild(routes)],

View File

@ -6,6 +6,7 @@
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<!-- <app-stepper [poll]="poll"></app-stepper>--> <app-admin-form [poll]="poll"></app-admin-form>
<h1 class="title is-1"><i class="fa fa-calendar" aria-hidden="true"></i> {{ 'dates.title' | translate }}</h1>
</div> </div>
</div> </div>

View File

@ -14,15 +14,14 @@ import { SettingsComponent } from '../../shared/components/settings/settings.com
}) })
export class AdministrationComponent implements OnInit, OnDestroy { export class AdministrationComponent implements OnInit, OnDestroy {
public poll: Poll; public poll: Poll;
public form: Object;
private routeSubscription: Subscription; private routeSubscription: Subscription;
constructor(private route: ActivatedRoute, private userService: UserService, private modalService: ModalService) {} constructor(private route: ActivatedRoute, private userService: UserService, private modalService: ModalService) {}
ngOnInit(): void { ngOnInit(): void {
if (!this.userService.isCurrentUserIdentifiable()) {
this.modalService.openModal(SettingsComponent);
}
this.routeSubscription = this.route.data.subscribe((data: { poll: Poll }) => { this.routeSubscription = this.route.data.subscribe((data: { poll: Poll }) => {
console.log('data', data);
if (data.poll) { if (data.poll) {
this.poll = data.poll; this.poll = data.poll;
} }

View File

@ -7,9 +7,11 @@ import { SharedModule } from '../../shared/shared.module';
import { AdministrationRoutingModule } from './administration-routing.module'; import { AdministrationRoutingModule } from './administration-routing.module';
import { AdministrationComponent } from './administration.component'; import { AdministrationComponent } from './administration.component';
import { StepperComponent } from './stepper/stepper.component'; import { StepperComponent } from './stepper/stepper.component';
import { NamingComponent } from './naming/naming.component';
import { FormComponent } from './form/form.component';
@NgModule({ @NgModule({
declarations: [AdministrationComponent, StepperComponent], declarations: [AdministrationComponent, StepperComponent, NamingComponent, FormComponent],
imports: [ imports: [
AdministrationRoutingModule, AdministrationRoutingModule,
CommonModule, CommonModule,

View File

@ -0,0 +1,145 @@
<div class="admin-form">
<h1 i18n>
{{ 'creation.title' | translate }}
</h1>
<span class="pre-selector" i18n>
{{ 'creation.want' | translate }}
</span>
<button class="btn btn--warning">
Reset all
</button>
<div class="simple">
<form [formGroup]="pollFormGroup">
<label for="title">Titre</label>
<input
#title
matInput
placeholder="title posée, sujet"
formControlName="title"
id="title"
autofocus="autofocus"
(change)="updateSlug()"
required
/>
<button
mat-button
*ngIf="title.value"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="title.value = ''"
>
<i class="fa fa-close"></i>
</button>
<label for="descr">Description</label>
<textarea
#description
matInput
id="descr"
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>
<h2>Choix de réponses</h2>
<pre class="debug padded warning">
choicesFormArray :
{{ choicesFormArray | json }}
</pre
>
<label for="slug"
>Url pour les participants
<button
mat-button
*ngIf="slug.value"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="slug.value = ''"
>
<i class="fa fa-close"></i>
</button>
</label>
<br />
<span
>{{ urlPrefix }}
<strong>
{{ slug.value }}
</strong>
</span>
<input #slug matInput id="slug" placeholder="Url" formControlName="slug" required />
<br />
</form>
</div>
<div class="complete" *ngIf="longFormVersionEnabled">
<form [formGroup]="configurationFormGroup">
<h2>Version complète du formulaire</h2>
<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>
<button
mat-button
(click)="createPoll()"
[disabled]="!pollFormGroup.valid || !configurationFormGroup.valid"
>
Enregistrer le sondage
</button>
</form>
</div>
<pre class="debug padded warning">
poll :
{{ poll | json }}
</pre
>
</div>

View File

@ -0,0 +1,7 @@
:host {
input,
textarea {
padding: 0.5em;
border: solid #eee;
}
}

View File

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

View File

@ -0,0 +1,83 @@
import { Component, Input, OnInit } from '@angular/core';
import { Poll } from '../../../core/models/poll.model';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { UuidService } from '../../../core/services/uuid.service';
import { DateService } from '../../../core/services/date.service';
import { ApiService } from '../../../core/services/api.service';
import { Choice } from '../../../core/models/choice.model';
@Component({
selector: 'app-admin-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.scss'],
})
export class FormComponent implements OnInit {
@Input()
public poll?: Poll;
public pollFormGroup: FormGroup;
public configurationFormGroup: FormGroup;
public choicesFormArray: FormArray; // possible choices to answer
public longFormVersionEnabled = true;
public urlPrefix: string = window.location.origin + '/participation/';
constructor(private fb: FormBuilder, private uuidService: UuidService, private apiService: ApiService) {}
ngOnInit(): void {
this.pollFormGroup = this.fb.group({
title: [this.poll ? this.poll.title : '', [Validators.required]],
slug: [this.poll ? this.poll.slug : this.uuidService.getUUID(), [Validators.required]],
description: [this.poll ? this.poll.description : ''],
});
// add dynamically elements to add choices
this.choicesFormArray = this.fb.array([
{
choices: this.poll.choices.forEach((elem: Choice) => {
return {
label: [elem.label, [Validators.required]],
imageUrl: [elem.imageUrl, null],
};
}),
},
]);
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 createPoll(): void {
if (this.pollFormGroup.valid && this.configurationFormGroup.valid) {
console.log('Le sondage est correctement rempli, prêt à enregistrer.');
// TODO : save the poll
this.apiService.createPoll(this.poll);
}
}
public updateSlug() {
let newValueFormatted = 'TODO';
this.pollFormGroup.patchValue({ slug: newValueFormatted });
}
}

View File

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

View File

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

View File

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

View File

@ -3,34 +3,10 @@
<form [formGroup]="pollFormGroup"> <form [formGroup]="pollFormGroup">
<ng-template matStepLabel>Informations du sondage</ng-template> <ng-template matStepLabel>Informations du sondage</ng-template>
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Question posée, sujet, etc.</mat-label> <mat-label>Titre</mat-label>
<input #question matInput placeholder="Question posée, sujet" formControlName="question" required /> <input #title matInput placeholder="Question posée, sujet" formControlName="title" 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>
<mat-form-field appearance="outline" class="is-flex"> <mat-form-field appearance="outline" class="is-flex">
<mat-label>Description</mat-label> <mat-label>Description</mat-label>
<textarea <textarea
@ -51,7 +27,21 @@
<i class="fa fa-close"></i> <i class="fa fa-close"></i>
</button> </button>
</mat-form-field> </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>
<div> <div>
<button mat-button matStepperNext>Next</button> <button mat-button matStepperNext>Next</button>
</div> </div>
@ -60,7 +50,7 @@
<mat-step [stepControl]="configurationFormGroup"> <mat-step [stepControl]="configurationFormGroup">
<form [formGroup]="configurationFormGroup"> <form [formGroup]="configurationFormGroup">
<ng-template matStepLabel>Configuration du sondage</ng-template> <ng-template matStepLabel>PollConfiguration du sondage</ng-template>
<mat-form-field appearance="outline" class="is-flex"> <mat-form-field appearance="outline" class="is-flex">
<mat-label>Nombre de jours avant expiration</mat-label> <mat-label>Nombre de jours avant expiration</mat-label>
<input <input

View File

@ -24,12 +24,13 @@ export class StepperComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
this.pollFormGroup = this.fb.group({ this.pollFormGroup = this.fb.group({
question: [this.poll ? this.poll.question : '', [Validators.required]], question: [this.poll ? this.poll.title : '', [Validators.required]],
slug: [this.poll ? this.poll.slug : this.uuidService.getUUID(), [Validators.required]], slug: [this.poll ? this.poll.slug : this.uuidService.getUUID(), [Validators.required]],
description: [this.poll ? this.poll.description : ''], description: [this.poll ? this.poll.description : ''],
}); });
this.configurationFormGroup = this.fb.group({ this.configurationFormGroup = this.fb.group({
title: [this.poll ? this.poll.configuration : false, [Validators.required]],
isAboutDate: [this.poll ? this.poll.configuration.isAboutDate : false, [Validators.required]], isAboutDate: [this.poll ? this.poll.configuration.isAboutDate : false, [Validators.required]],
isProtectedByPassword: [ isProtectedByPassword: [
this.poll ? this.poll.configuration.isProtectedByPassword : false, this.poll ? this.poll.configuration.isProtectedByPassword : false,

View File

@ -8,7 +8,7 @@
<div class="column"> <div class="column">
<div class="card"> <div class="card">
<header class="card-header"> <header class="card-header">
<p class="card-header-title">{{ poll.question }}</p> <p class="card-header-title">{{ poll.title }}</p>
<p class="card-header-icon">author : {{ poll.owner?.pseudo }}</p> <p class="card-header-icon">author : {{ poll.owner?.pseudo }}</p>
</header> </header>
<div class="card-content"> <div class="card-content">

View File

@ -2,7 +2,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Configuration } from '../../core/models/configuration.model'; import { PollConfiguration } from '../../core/models/configuration.model';
import { Poll } from '../../core/models/poll.model'; import { Poll } from '../../core/models/poll.model';
import { ModalService } from '../../core/services/modal.service'; import { ModalService } from '../../core/services/modal.service';
import { UserService } from '../../core/services/user.service'; import { UserService } from '../../core/services/user.service';
@ -33,7 +33,7 @@ export class ConsultationComponent implements OnInit, OnDestroy {
this.routeSubscription = this.activatedRoute.data.subscribe((data: { poll: Poll }) => { this.routeSubscription = this.activatedRoute.data.subscribe((data: { poll: Poll }) => {
if (data.poll) { if (data.poll) {
this.poll = data.poll; this.poll = data.poll;
this.isArchived = Configuration.isArchived(data.poll.configuration); this.isArchived = PollConfiguration.isArchived(data.poll.configuration);
} else { } else {
this.router.navigate(['/page-not-found']); this.router.navigate(['/page-not-found']);
} }

View File

@ -1,17 +1,17 @@
<div class="container has-text-centered"> <div class="container has-text-centered">
<ng-container *ngIf="['REGISTERED', 'ADMIN'].includes((_user | async)?.role)"> <div class="columns">
<div class="columns"> <div class="column">
<div class="column"> <h1>Mes sondages</h1>
<h1>Mes sondages</h1>
</div>
</div> </div>
</div>
<div *ngIf="pollsAreLoaded">
<div class="columns"> <div class="columns">
<div class="column"> <div class="column">
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth"> <table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
<thead></thead> <thead></thead>
<tbody> <tbody>
<tr *ngFor="let poll of (_user | async)?.polls"> <tr *ngFor="let poll of (_user | async)?.polls">
<th>{{ poll.question }}</th> <th>{{ poll.title }}</th>
<td> <td>
<a routerLink="{{ '../../poll/' + poll.slug + '/consultation' }}"> <a routerLink="{{ '../../poll/' + poll.slug + '/consultation' }}">
{{ poll.slug }} {{ poll.slug }}
@ -22,38 +22,15 @@
</table> </table>
</div> </div>
</div> </div>
</ng-container> </div>
<ng-container *ngIf="['ANONYMOUS'].includes((_user | async)?.role)"> <div class="columns">
<div class="columns"> <div class="column">
<div class="column"> <form (submit)="sendRetrieveEmail()">
<a class="button is-primary" role="button" routerLink="/"> <input type="email" autofocus="autofocus" placeholder="contact@exemple.com" />
Jai un compte, je me connecte <button class="button is-primary">
</a> envoyez-moi la liste par email
</div>
</div>
<div class="columns">
<div class="column">
<span>OU</span>
</div>
</div>
<div class="columns">
<div class="column">
<button class="button is-primary" (click)="toggleModal()">
Je nai pas de compte : envoyez-moi la liste par email
</button> </button>
</div> </form>
</div>
</ng-container>
</div>
<div class="modal" [class.is-active]="isModalOpened">
<div class="modal-background" (click)="toggleModal()"></div>
<div class="modal-content has-background-light">
<div class="field">
<div class="control">
<input class="input" type="email" placeholder="Email" />
<button class="button is-primary is-fullwidth">Envoyez le mail !</button>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -12,12 +12,13 @@ import { UserService } from '../../../core/services/user.service';
export class UserPollsComponent implements OnInit { export class UserPollsComponent implements OnInit {
public _user: Observable<User> = this.userService.user; public _user: Observable<User> = this.userService.user;
public isModalOpened = false; public isModalOpened = false;
public pollsAreLoaded = false;
constructor(private userService: UserService) {} constructor(private userService: UserService) {}
ngOnInit(): void {} ngOnInit(): void {}
public toggleModal(): void { sendRetrieveEmail() {
this.isModalOpened = !this.isModalOpened; alert('TODO');
} }
} }

View File

@ -1,46 +1,40 @@
import {Routes} from "@angular/router"; import { Routes } from '@angular/router';
import {HomeComponent} from "./core/components/home/home.component"; import { HomeComponent } from './core/components/home/home.component';
import {PollService} from "./core/services/poll.service"; import { PollService } from './core/services/poll.service';
import {PageNotFoundComponent} from "./shared/components/page-not-found/page-not-found.component"; import { PageNotFoundComponent } from './shared/components/page-not-found/page-not-found.component';
export const routes: Routes = [ export const routes: Routes = [
{path: '', component: HomeComponent}, { path: '', component: HomeComponent },
{ {
path: 'user', path: 'user',
loadChildren: () => import('./features/user-profile/user-profile.module') loadChildren: () => import('./features/user-profile/user-profile.module').then((m) => m.UserProfileModule),
.then((m) => m.UserProfileModule),
}, },
{ {
path: 'administration', path: 'administration',
loadChildren: () => loadChildren: () =>
import('./features/administration/administration.module') import('./features/administration/administration.module').then((m) => m.AdministrationModule),
.then((m) => m.AdministrationModule), // resolve: {poll: PollService},
resolve: {poll: PollService},
}, },
{ {
path: 'poll/:slug/administration', path: 'poll/:slug/administration',
loadChildren: () => loadChildren: () =>
import('./features/administration/administration.module') import('./features/administration/administration.module').then((m) => m.AdministrationModule),
.then((m) => m.AdministrationModule), resolve: { poll: PollService },
resolve: {poll: PollService},
}, },
{ {
path: 'poll/:slug/consultation', path: 'poll/:slug/consultation',
loadChildren: () => import('./features/consultation/consultation.module') loadChildren: () => import('./features/consultation/consultation.module').then((m) => m.ConsultationModule),
.then((m) => m.ConsultationModule), resolve: { poll: PollService },
resolve: {poll: PollService},
}, },
{ {
path: 'poll/:slug/participation', path: 'poll/:slug/participation',
loadChildren: () => import('./features/participation/participation.module') loadChildren: () => import('./features/participation/participation.module').then((m) => m.ParticipationModule),
.then((m) => m.ParticipationModule), resolve: { poll: PollService },
resolve: {poll: PollService},
}, },
{ {
path: 'oldstuff', path: 'oldstuff',
loadChildren: () => import('./features/old-stuff/old-stuff.module') loadChildren: () => import('./features/old-stuff/old-stuff.module').then((m) => m.OldStuffModule),
.then((m) => m.OldStuffModule),
}, },
{path: 'page-not-found', component: PageNotFoundComponent}, { path: 'page-not-found', component: PageNotFoundComponent },
{path: '**', redirectTo: 'page-not-found', pathMatch: 'full'}, { path: '**', redirectTo: 'page-not-found', pathMatch: 'full' },
]; ];

View File

@ -16,11 +16,7 @@ export class SettingsComponent implements OnInit {
constructor(private userService: UserService, public dialogRef: MatDialogRef<SettingsComponent>) {} constructor(private userService: UserService, public dialogRef: MatDialogRef<SettingsComponent>) {}
ngOnInit(): void { ngOnInit(): void {}
this.userSubscription = this.userService.user.subscribe((user: User) => {
this.user = user;
});
}
ngOnDestroy(): void { ngOnDestroy(): void {
if (this.userSubscription) { if (this.userSubscription) {

View File

@ -6,6 +6,7 @@ const backendApiUrlsInDev = {
export const environment = { export const environment = {
production: true, production: true,
appTitle: 'FramaDate', appTitle: 'FramaDate',
appVersion: '2.0.0',
appLogo: '/assets/img/logo.png', appLogo: '/assets/img/logo.png',
api: { api: {
baseHref: backendApiUrlsInDev.remote, baseHref: backendApiUrlsInDev.remote,

View File

@ -10,7 +10,8 @@ const backendApiUrlsInDev = {
export const environment = { export const environment = {
production: false, production: false,
appTitle: 'FramaDate', appTitle: 'FramaDate',
appLogo: '/assets/img/icon_voter_yes.png', appVersion: '2.0.0',
appLogo: 'assets/img/logo.png',
api: { api: {
baseHref: backendApiUrlsInDev.local, baseHref: backendApiUrlsInDev.local,
endpoints: { endpoints: {