diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4837d81f..cf5f74c0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,7 +2,8 @@ image: node:latest stages: - - pages + - build +# - pages - test # - e2e @@ -10,38 +11,36 @@ cache: paths: - node_modules/ -pages: - stage: pages - script: - - yarn install --pure-lockfile - - yarn build:prod:gitlabpage - - mv dist/framadate/ public/ - artifacts: - paths: - - public - expire_in: '2 hours' - only: - - develop +#pages: +# stage: pages +# script: +# - yarn install --pure-lockfile +# - yarn build:prod:gitlabpage +# - mv dist/framadate/ public/ +# artifacts: +# paths: +# - public +# expire_in: '2 hours' +# only: +# - develop -test: - stage: test - script: - - npm i - - pkill Xvfb - - npm run test:ci - artifacts: - paths: - - coverage/ - cache: - policy: pull +#test: +# stage: test +# script: +# - npm i +# - pkill Xvfb +# - npm run test:ci +# artifacts: +# paths: +# - coverage/ +# cache: +# policy: pull -test-build: - stage: test +build: + stage: build script: - yarn install --pure-lockfile - npx ng build --prod - only: - - master cache: policy: pull diff --git a/doc/cadrage/specifications-fonctionnelles.md b/doc/cadrage/specifications-fonctionnelles.md index a6928a1f..b5d2f4d3 100644 --- a/doc/cadrage/specifications-fonctionnelles.md +++ b/doc/cadrage/specifications-fonctionnelles.md @@ -96,7 +96,7 @@ L'export d'un sondage et des résultats d'un sondage est possible au format CSV. # 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 ?* -* 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. * Boutons pour copier dans le presse-papier les liens publics et privés / admin des sondages. * Limiter le nombre de participants maximum diff --git a/mocks/db.json b/mocks/db.json index 9bd5c953..c1bf054c 100644 --- a/mocks/db.json +++ b/mocks/db.json @@ -19,7 +19,7 @@ "expires": "2020-12-31" }, "ownerId": 1, - "question": "Quelle date pour le picnic ?", + "title": "Quelle date pour le picnic ?", "description": "Gros badass picnic en plein air ! Come on !" }, { @@ -37,7 +37,7 @@ "expires": "2020-11-30" }, "ownerId": 2, - "question": "On fait quoi pendant les vacances ?", + "title": "On fait quoi pendant les vacances ?", "description": "Vacances en famille" } ], diff --git a/src/app/app.component.html b/src/app/app.component.html index 2f211c98..477450fe 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -6,10 +6,14 @@
- Dev Menu
+
+
+ menu + développeur +
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5a8dcc6c..c4e0eb3d 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,12 +1,12 @@ -import {Component, OnDestroy, OnInit} from '@angular/core'; -import {Title} from '@angular/platform-browser'; -import {Subscription} from 'rxjs'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Title } from '@angular/platform-browser'; +import { Subscription } from 'rxjs'; -import {environment} from '../environments/environment'; -import {Theme} from './core/enums/theme.enum'; -import {LanguageService} from './core/services/language.service'; -import {ThemeService} from './core/services/theme.service'; -import {MockingService} from './core/services/mocking.service'; +import { environment } from '../environments/environment'; +import { Theme } from './core/enums/theme.enum'; +import { LanguageService } from './core/services/language.service'; +import { ThemeService } from './core/services/theme.service'; +import { MockingService } from './core/services/mocking.service'; @Component({ selector: 'app-root', @@ -18,21 +18,18 @@ export class AppComponent implements OnInit, OnDestroy { public appLogo: string = environment.appLogo; public themeClass: string; public isSidebarOpened = false; + public devModeEnabled = !environment.production; private themeSubscription: Subscription; constructor( private titleService: Title, private themeService: ThemeService, - private languageService: LanguageService, - private mockingService: MockingService - ) { - } + private languageService: LanguageService // private mockingService: MockingService + ) {} ngOnInit(): void { if (!environment.production) { this.appTitle += ' [DEV]'; - // TODO: to be removed - this.mockingService.init(); } this.titleService.setTitle(this.appTitle); this.languageService.configureAndInitTranslations(); diff --git a/src/app/core/components/header/header.component.html b/src/app/core/components/header/header.component.html index fae62667..d7f209b9 100644 --- a/src/app/core/components/header/header.component.html +++ b/src/app/core/components/header/header.component.html @@ -28,24 +28,15 @@ diff --git a/src/app/core/components/header/header.component.scss b/src/app/core/components/header/header.component.scss index 2e30e421..61220d68 100644 --- a/src/app/core/components/header/header.component.scss +++ b/src/app/core/components/header/header.component.scss @@ -1,5 +1,8 @@ :host { header { + nav { + padding-right: 1em; + } .container { padding: 0; } diff --git a/src/app/core/components/header/header.component.ts b/src/app/core/components/header/header.component.ts index 78e56192..9fc1b268 100644 --- a/src/app/core/components/header/header.component.ts +++ b/src/app/core/components/header/header.component.ts @@ -21,8 +21,4 @@ export class HeaderComponent implements OnInit { constructor(private userService: UserService, private modalService: ModalService) {} public ngOnInit(): void {} - - public openDialog(): void { - this.modalService.openModal(SettingsComponent); - } } diff --git a/src/app/core/models/configuration.model.ts b/src/app/core/models/configuration.model.ts index e68aae88..bc17a948 100644 --- a/src/app/core/models/configuration.model.ts +++ b/src/app/core/models/configuration.model.ts @@ -1,7 +1,7 @@ import { environment } from '../../../environments/environment'; import { DateService } from '../services/date.service'; -export class Configuration { +export class PollConfiguration { constructor( public isAboutDate: 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; } } diff --git a/src/app/core/models/poll.model.ts b/src/app/core/models/poll.model.ts index 161363f7..2937fa84 100644 --- a/src/app/core/models/poll.model.ts +++ b/src/app/core/models/poll.model.ts @@ -2,16 +2,16 @@ import { environment } from 'src/environments/environment'; import { Choice } from './choice.model'; import { Comment } from './comment.model'; -import { Configuration } from './configuration.model'; +import { PollConfiguration } from './configuration.model'; import { User } from './user.model'; export class Poll { constructor( public owner: User, public slug: string, - public question: string, + public title: string, public description?: string, - public configuration: Configuration = new Configuration(), + public configuration: PollConfiguration = new PollConfiguration(), public comments: Comment[] = [], public choices: Choice[] = [] ) {} @@ -25,12 +25,12 @@ export class Poll { } public static adaptFromLocalJsonServer( - item: Pick + item: Pick ): Poll { const poll = new Poll( new User(item.owner.pseudo, item.owner.email, undefined), item.slug, - item.question, + item.title, item.description, item.configuration, item.comments diff --git a/src/app/core/services/poll.service.ts b/src/app/core/services/poll.service.ts index 69505181..13ded9c7 100644 --- a/src/app/core/services/poll.service.ts +++ b/src/app/core/services/poll.service.ts @@ -47,9 +47,12 @@ export class PollService implements Resolve { } public async loadPollBySlug(slug: string): Promise { - const poll: Poll | undefined = await this.apiService.getPollBySlug(slug); - console.log({ loadPollBySlugResponse: poll }); - this.updateCurrentPoll(poll); + console.log('slug', slug); + if (slug) { + const poll: Poll | undefined = await this.apiService.getPollBySlug(slug); + console.log({ loadPollBySlugResponse: poll }); + this.updateCurrentPoll(poll); + } } public updateCurrentPoll(poll: Poll): void { diff --git a/src/app/features/administration/administration-routing.module.ts b/src/app/features/administration/administration-routing.module.ts index 8b2bf8f4..aa3fae29 100644 --- a/src/app/features/administration/administration-routing.module.ts +++ b/src/app/features/administration/administration-routing.module.ts @@ -2,8 +2,12 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; 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({ imports: [RouterModule.forChild(routes)], diff --git a/src/app/features/administration/administration.component.html b/src/app/features/administration/administration.component.html index 1999090b..8b3dab9f 100644 --- a/src/app/features/administration/administration.component.html +++ b/src/app/features/administration/administration.component.html @@ -6,6 +6,7 @@
- + +

{{ 'dates.title' | translate }}

diff --git a/src/app/features/administration/administration.component.ts b/src/app/features/administration/administration.component.ts index bc1ce942..bf69d01c 100644 --- a/src/app/features/administration/administration.component.ts +++ b/src/app/features/administration/administration.component.ts @@ -14,15 +14,14 @@ import { SettingsComponent } from '../../shared/components/settings/settings.com }) export class AdministrationComponent implements OnInit, OnDestroy { public poll: Poll; + public form: Object; private routeSubscription: Subscription; constructor(private route: ActivatedRoute, private userService: UserService, private modalService: ModalService) {} ngOnInit(): void { - if (!this.userService.isCurrentUserIdentifiable()) { - this.modalService.openModal(SettingsComponent); - } this.routeSubscription = this.route.data.subscribe((data: { poll: Poll }) => { + console.log('data', data); if (data.poll) { this.poll = data.poll; } diff --git a/src/app/features/administration/administration.module.ts b/src/app/features/administration/administration.module.ts index 49d581eb..7b7d9361 100644 --- a/src/app/features/administration/administration.module.ts +++ b/src/app/features/administration/administration.module.ts @@ -7,9 +7,11 @@ import { SharedModule } from '../../shared/shared.module'; import { AdministrationRoutingModule } from './administration-routing.module'; import { AdministrationComponent } from './administration.component'; import { StepperComponent } from './stepper/stepper.component'; +import { NamingComponent } from './naming/naming.component'; +import { FormComponent } from './form/form.component'; @NgModule({ - declarations: [AdministrationComponent, StepperComponent], + declarations: [AdministrationComponent, StepperComponent, NamingComponent, FormComponent], imports: [ AdministrationRoutingModule, CommonModule, diff --git a/src/app/features/administration/form/form.component.html b/src/app/features/administration/form/form.component.html new file mode 100644 index 00000000..ea7235ca --- /dev/null +++ b/src/app/features/administration/form/form.component.html @@ -0,0 +1,145 @@ +
+

+ {{ 'creation.title' | translate }} +

+ + {{ 'creation.want' | translate }} + + + +
+
+ + + + + + + +

Choix de réponses

+
+			choicesFormArray :
+				{{ choicesFormArray | json }}
+			
+ + +
+ {{ urlPrefix }} + + {{ slug.value }} + + + +
+
+
+
+
+

Version complète du formulaire

+ + Nombre de jours avant expiration + + + + + Les participants pourront consulter les résultats + + + Les choix possibles concerneront des dates + + + Le sondage sera protégé par un mot de passe + + + Vous recevrez un mail à chaque nouvelle participation + + + Vous recevrez un mail à chaque nouveau commentaire + + + La réponse « peut-être » sera disponible + + + +
+
+ +
+poll :
+		{{ poll | json }}
+
+
diff --git a/src/app/features/administration/form/form.component.scss b/src/app/features/administration/form/form.component.scss new file mode 100644 index 00000000..83c66da9 --- /dev/null +++ b/src/app/features/administration/form/form.component.scss @@ -0,0 +1,7 @@ +:host { + input, + textarea { + padding: 0.5em; + border: solid #eee; + } +} diff --git a/src/app/features/administration/form/form.component.spec.ts b/src/app/features/administration/form/form.component.spec.ts new file mode 100644 index 00000000..62a17377 --- /dev/null +++ b/src/app/features/administration/form/form.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [FormComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/administration/form/form.component.ts b/src/app/features/administration/form/form.component.ts new file mode 100644 index 00000000..62446626 --- /dev/null +++ b/src/app/features/administration/form/form.component.ts @@ -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 }); + } +} diff --git a/src/app/features/administration/naming/naming.component.html b/src/app/features/administration/naming/naming.component.html new file mode 100644 index 00000000..00dc4e45 --- /dev/null +++ b/src/app/features/administration/naming/naming.component.html @@ -0,0 +1 @@ +

naming works!

diff --git a/src/app/features/administration/naming/naming.component.scss b/src/app/features/administration/naming/naming.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/features/administration/naming/naming.component.spec.ts b/src/app/features/administration/naming/naming.component.spec.ts new file mode 100644 index 00000000..8611ccbd --- /dev/null +++ b/src/app/features/administration/naming/naming.component.spec.ts @@ -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; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [NamingComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NamingComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/features/administration/naming/naming.component.ts b/src/app/features/administration/naming/naming.component.ts new file mode 100644 index 00000000..2a04cfd4 --- /dev/null +++ b/src/app/features/administration/naming/naming.component.ts @@ -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 {} +} diff --git a/src/app/features/administration/stepper/stepper.component.html b/src/app/features/administration/stepper/stepper.component.html index 1f158b82..00d5d761 100644 --- a/src/app/features/administration/stepper/stepper.component.html +++ b/src/app/features/administration/stepper/stepper.component.html @@ -3,34 +3,10 @@
Informations du sondage - Question posée, sujet, etc. - - - - - Url pour les participants - {{ urlPrefix }} - - + Titre + + Description