diff --git a/src/app/core/services/poll.service.ts b/src/app/core/services/poll.service.ts index 333dcca7..200800ef 100644 --- a/src/app/core/services/poll.service.ts +++ b/src/app/core/services/poll.service.ts @@ -11,6 +11,8 @@ import { UserService } from './user.service'; import { UuidService } from './uuid.service'; import { HttpClient } from '@angular/common/http'; import { environment } from '../../../environments/environment'; +import { StorageService } from './storage.service'; +import { Title } from '@angular/platform-browser'; import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { DOCUMENT } from '@angular/common'; @@ -18,6 +20,7 @@ import { DateChoice, TimeSlices } from '../models/dateChoice.model'; import { DateUtilitiesService } from './date.utilities.service'; import { Owner } from '../models/owner.model'; import { Stack } from '../models/stack.model'; +import { Vote } from '../models/vote.model'; @Injectable({ providedIn: 'root', @@ -50,9 +53,11 @@ export class PollService implements Resolve { private http: HttpClient, private router: Router, private apiService: ApiService, + private storageService: StorageService, private userService: UserService, private uuidService: UuidService, private toastService: ToastService, + private titleService: Title, public DateUtilitiesService: DateUtilitiesService, public route: ActivatedRoute, @Inject(DOCUMENT) private document: any, @@ -89,9 +94,6 @@ export class PollService implements Resolve { whoModifiesAnswers: 'everybody', whoCanChangeAnswers: 'everybody', isProtectedByPassword: false, - isOwnerNotifiedByEmailOnNewVote: false, - isOwnerNotifiedByEmailOnNewComment: false, - isMaybeAnswerAvailable: false, richTextMode: false, areResultsPublic: true, expiracyNumberOfDays: 60, @@ -99,12 +101,6 @@ export class PollService implements Resolve { this.automaticSlug(); } - public enrichVoteStackWithCurrentPollChoicesDefaultVotes(vote_stack: Stack) { - console.log('vote_stack', vote_stack); - this.toastService.display('TODO refill vote stack'); - // this.form.patchValue(vote_stack) - } - /** * set the poll slug from other data of the poll */ @@ -120,19 +116,29 @@ export class PollService implements Resolve { creatorEmail: ['', [Validators.required]], custom_url: [this.uuidService.getUUID(), [Validators.required]], description: ['', [Validators.required]], + password: ['', [Validators.required]], choices: new FormArray([]), whoModifiesAnswers: ['', [Validators.required]], whoCanChangeAnswers: ['', [Validators.required]], isAboutDate: [true, [Validators.required]], startDateInterval: ['', [Validators.required]], endDateInterval: ['', [Validators.required]], + expiresDaysDelay: ['', [Validators.required]], + maxCountOfAnswers: ['', [Validators.required]], + isZeroKnoledge: [false, [Validators.required]], isProtectedByPassword: [false, [Validators.required]], - isOwnerNotifiedByEmailOnNewVote: [false, [Validators.required]], - isOwnerNotifiedByEmailOnNewComment: [false, [Validators.required]], - isMaybeAnswerAvailable: [false, [Validators.required]], + isOwnerNotifiedByEmailOnNewVote: [true, [Validators.required]], + isOwnerNotifiedByEmailOnNewComment: [true, [Validators.required]], areResultsPublic: [true, [Validators.required]], richTextMode: [false, [Validators.required]], - expiracyNumberOfDays: [60, [Validators.required, Validators.min(0)]], + isYesAnswerAvailable: [true, [Validators.required]], + isMaybeAnswerAvailable: [true, [Validators.required]], + isNoAnswerAvailable: [true, [Validators.required]], + allowComments: [true, [Validators.required]], + hasMaxCountOfAnswers: [300, [Validators.required]], + useVoterUniqueLink: [false, [Validators.required]], + voterEmailList: ['', [Validators.required]], + allowNewDateTime: [60, [Validators.required, Validators.min(0)]], }); this.form = form; return form; @@ -149,30 +155,36 @@ export class PollService implements Resolve { * @param state */ public async resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { + console.log('resolve route,state', route, state); const segments: string[] = state.url.split('/'); - const wantedSlug: string = segments.includes('poll') ? segments[segments.indexOf('poll') + 1] : ''; - if (!wantedSlug && state.url.includes('administration')) { - // creation of new poll - const poll = new Poll(this.userService.getCurrentUser(), this.uuidService.getUUID(), ''); - this._poll.next(poll); - this.router.navigate(['poll/' + poll.custom_url + '/administration']); - } + const wantedcustom_url: string = segments.includes('poll') ? segments[segments.indexOf('poll') + 1] : ''; + if ( !this._poll.getValue() || !this._poll.getValue().custom_url || - this._poll.getValue().custom_url !== wantedSlug + this._poll.getValue().custom_url !== wantedcustom_url ) { - await this.loadPollByCustomUrl(wantedSlug); + if (this.pass_hash) { + this.storageService.vote_stack.pass_hash = this.pass_hash; + await this.loadPollBycustom_urlWithPasswordHash(wantedcustom_url, this.pass_hash); + } else { + await this.loadPollBycustom_url(wantedcustom_url); + } } - if (this._poll.getValue()) { - return this._poll.getValue(); + const loadedPoll = this._poll.getValue(); + if (loadedPoll) { + this.storageService.vote_stack.poll_custom_url = loadedPoll.custom_url; + return loadedPoll; } else { this.router.navigate(['page-not-found']); return; } } - getAllAvailablePolls() { + /** + * get all polls + */ + getAllAvailablePolls(): void { const baseHref = environment.api.version.apiV1.baseHref; console.log('getAllAvailablePolls baseHref', baseHref); const headers = ApiService.makeHeaders(); @@ -186,22 +198,56 @@ export class PollService implements Resolve { } } - public async loadPollByCustomUrl(slug: string): Promise { - if (slug) { - const poll: Poll | undefined = await this.apiService.getPollByCustomUrl(slug); - console.log({ loadPollBySlugResponse: poll }); - this.updateCurrentPoll(poll); - } - } - public async loadPollByCustomUrlWithPasswordHash(slug: string, pass_hash: string): Promise { - if (slug) { - const poll: Poll | undefined = await this.apiService.getPollByCustomUrlWithHash(slug, pass_hash); - console.log({ loadPollBySlugResponse: poll }); - this.updateCurrentPoll(poll); + public async loadPollBycustom_url(custom_url: string): Promise { + if (custom_url) { + const poll: Poll | undefined = await this.apiService.getPollByCustomUrl(custom_url); + + if (poll) { + this.updateCurrentPoll(poll); + this.titleService.setTitle(`☑️ ${poll.title} - ${environment.appTitle}`); + } else { + this.toastService.display(`sondage ${custom_url} non trouvé`); + this.router.navigate(['page-not-found']); + } + } else { + this.toastService.display(`sondage sans custom url : ${custom_url}`); } } + public async loadPollBycustom_urlWithPasswordHash(custom_url: string, hash: string): Promise { + if (custom_url) { + const poll: Poll | undefined = await this.apiService.getPollByCustomUrlWithHash(custom_url, hash); + + if (poll) { + this.updateCurrentPoll(poll); + this.titleService.setTitle(`☑️ ${poll.title} - ${environment.appTitle}`); + } else { + this.toastService.display(`sondage ${custom_url} non trouvé`); + this.router.navigate(['page-not-found']); + } + } else { + this.toastService.display(`sondage sans custom url : ${custom_url}`); + } + } + + /** + * update poll and parse its fields + * @param poll + */ public updateCurrentPoll(poll: Poll): void { + console.log('this.storageService.vote_stack.id', this.storageService.vote_stack.id); + + if (!this.storageService.vote_stack.id || this.storageService.vote_stack.poll_custom_url !== poll.custom_url) { + console.log('set base choices', poll.choices); + // set the choices only the first time the poll loads, or if we changed the poll + console.log( + 'this.storageService.vote_stack.poll_custom_url', + this.storageService.vote_stack.poll_custom_url + ); + // this.storageService.setChoicesForVoteStack(poll.choices); + } + + this.toastService.display('sondage bien mis à jour', 'success'); this._poll.next(poll); } @@ -491,77 +537,107 @@ export class PollService implements Resolve { return list; } - getParticipationUrl() { - let poll = this._poll.getValue(); - + public getParticipationUrl(): string { // http://localhost:4200/#/poll/dessin-anime/consultation // TODO handle secure access // http://localhost:4200/#/poll/citron/consultation/secure/1c01ed9c94fc640a1be864f197ff808c - return window.location.host + '/#/poll/' + poll.custom_url + '/consultation'; + let url = ''; + if (this._poll && this._poll.getValue) { + const polltemp = this._poll.getValue(); + if (polltemp) { + url = `${environment.frontDomain}#/poll/${polltemp.custom_url}/consultation`; + } + } + // TODO handle pass access + return url; + } + public getAdministrationUrl(): string { + let url = ''; + if (this._poll && this._poll.getValue) { + const polltemp = this._poll.getValue(); + if (polltemp) { + url = `${environment.frontDomain}#/poll/admin/${polltemp.admin_key}`; + } + } + return url; } + /** + * enrich vote stack with missing default votes + * @param vote_stack + */ + enrichVoteStackWithCurrentPollChoicesDefaultVotes(vote_stack: Stack) { + if (this._poll && this._poll.getValue) { + const polltemp = this._poll.getValue(); + polltemp.choices.map((choice) => { + // for each vote, if it has the choice_id, do nothing, else, add a default vote + if (!this.findExistingVoteFromChoiceId(choice.id, vote_stack.votes)) { + vote_stack.votes.push(new Vote(choice.id)); + } + }); + } + } + /** + * find an existing vote in vote_stack from its choice_id + * @param choice_id + * @param votes + */ + findExistingVoteFromChoiceId(choice_id: number, votes: Vote[]) { + return votes.find((vote: Vote) => { + if (vote.choice_id === choice_id) { + return vote; + } + }); + } convertCalendarDatesToChoices(array_dates) { return array_dates; } - newPollFromForm(form: any): any { - const newpoll = new Poll( - this.userService.getCurrentUser(), - this.uuidService.getUUID(), - form.controls.title.value - ); - /** - * convert to API version 1 config poll - */ - const apiV1Poll = { - menuVisible: true, - expiracyDateDefaultInDays: newpoll.default_expiracy_days_from_now, - deletionDateAfterLastModification: newpoll.default_expiracy_days_from_now, - pollType: newpoll.kind ? 'date' : 'classic', // classic or dates - title: newpoll.title, - description: newpoll.description, - myName: newpoll.owner.pseudo, - myComment: '', - isAdmin: true, // when we create a poll, we are admin on it - myVoteStack: {}, - myTempVoteStack: 0, - myEmail: newpoll.owner.email, - myPolls: [], // list of retrieved polls from the backend api - /* - date specific poll, we have the choice to setup different hours (timeList) for all possible dates (dateList), or use the same hours for all dates - */ - allowSeveralHours: 'true', - // access - visibility: newpoll.areResultsPublic, // visible to one with the link: - voteChoices: newpoll.isMaybeAnswerAvailable ? 'yes, maybe, no' : 'yes', // possible answers to a vote choice: only "yes", "yes, maybe, no" - created_at: new Date(), - expirationDate: '', // expiracy date - voteStackId: null, // id of the vote stack to update - pollId: null, // id of the current poll when created. data given by the backend api - pollSlug: null, // id of the current poll when created. data given by the backend api - currentPoll: null, // current poll selected with createPoll or getPoll of ConfigService - passwordAccess: false, - password: newpoll.password, - customUrl: newpoll.custom_url, // custom slug in the url, must be unique - customUrlIsUnique: null, // given by the backend - urlSlugPublic: null, - urlPublic: null, - urlAdmin: null, - adminKey: '', // key to change config of the poll - owner_modifier_token: '', // key to change a vote stack - canModifyAnswers: newpoll.modification_policy, // bool for the frontend selector - whoModifiesAnswers: newpoll.modification_policy, // everybody, self, nobody (: just admin) - whoCanChangeAnswers: newpoll.modification_policy, // everybody, self, nobody (: just admin) - dateList: newpoll.dateChoices, // sets of days as strings, config to set identical time for days in a special days poll - timeList: newpoll.timeSlices, // ranges of time expressed as strings + /** + * @description convert to API version 1 data transition object + * @param form + */ + newPollFromForm(form: any): Poll { + const newOwner = this.storageService.vote_stack.owner; - answers: newpoll.choices, - // modals - displayConfirmVoteModalAdmin: false, - }; - console.log('apiV1Poll', apiV1Poll); - return apiV1Poll; + const newpoll = new Poll(newOwner, form.value.custom_url, form.value.title); + + const pollKeys = Object.keys(newpoll); + const formFields = Object.keys(form.value); + newpoll.allowed_answers = []; + + for (const pk of pollKeys) { + if (formFields.indexOf(pk) !== -1) { + const field = form.value[pk]; + newpoll[pk] = field; + } else { + console.log('manque pollKey', pk); + } + } + + if (form.value.isYesAnswerAvailable) { + newpoll.allowed_answers.push('yes'); + } + if (form.value.isMaybeAnswerAvailable) { + newpoll.allowed_answers.push('maybe'); + } + if (form.value.isNoAnswerAvailable) { + newpoll.allowed_answers.push('no'); + } + newpoll.description = form.value.description; + newpoll.has_several_hours = form.value.hasSeveralHours; + newpoll.hasSeveralHours = form.value.hasSeveralHours; + newpoll.max_count_of_answers = form.value.allowComments; + newpoll.maxCountOfAnswers = form.value.maxCountOfAnswers; + newpoll.password = form.value.password; + newpoll.kind = form.value.kind; + newpoll.allow_comments = form.value.allowComments; + // merge choices from storage + newpoll.choices = Object.assign([], this.storageService.choices); + newpoll.dateChoices = Object.assign([], this.storageService.dateChoices); + newpoll.timeSlices = Object.assign([], this.storageService.timeSlices); + return newpoll; } } diff --git a/src/app/features/administration/form/advanced-config/advanced-config.component.html b/src/app/features/administration/form/advanced-config/advanced-config.component.html index 9dce29a0..d00c48af 100644 --- a/src/app/features/administration/form/advanced-config/advanced-config.component.html +++ b/src/app/features/administration/form/advanced-config/advanced-config.component.html @@ -103,20 +103,19 @@ Réponses proposées - - - - La réponse « oui » sera disponible +
La réponse « peut-être » sera disponible +
La réponse « non » sera disponible +

diff --git a/src/app/features/administration/form/advanced-config/advanced-config.component.scss b/src/app/features/administration/form/advanced-config/advanced-config.component.scss index 3d5e4870..eb0e5380 100644 --- a/src/app/features/administration/form/advanced-config/advanced-config.component.scss +++ b/src/app/features/administration/form/advanced-config/advanced-config.component.scss @@ -1,3 +1,8 @@ .title { margin-top: 2em; } +.mat-checkbox { + img { + margin-left: 1em; + } +} diff --git a/src/app/features/administration/form/steps/step-five/step-five.component.html b/src/app/features/administration/form/steps/step-five/step-five.component.html index 8d476bc0..d6309d29 100644 --- a/src/app/features/administration/form/steps/step-five/step-five.component.html +++ b/src/app/features/administration/form/steps/step-five/step-five.component.html @@ -1,76 +1,10 @@ -
-

- Félicitations, votre sondage " {{ pollService.form.value.title }} " est créé -

- Un récapitulatif par email vous a été envoyé. Partagez-le au monde avec ce lien: Administrez-le avec cet autre lien: -
+ -

- {{ 'resume.title' | translate }} -

-
-

{{ 'resume.admins' | translate }}

-

- Votre sondage « - - {{ pollService.form.value.title }} - - » a bien été créé ! -

-

- Voici les liens d’accès au sondage, conservez-les soigneusement ! (Si vous les perdez vous pourrez toujours - les recevoir par email) -

- -

- Pour accéder au sondage et à tous ses paramètres : - {{ pollService.form.value.urlAdmin }} -

- - - Voir le sondage coté administrateur·ice - -

- Note : Le sondage sera supprimé {{ pollService.form.value.deletionDateAfterLastModification }} jours après - la date de sa dernière modification. -

-
-
-

{{ 'resume.users' | translate }}

-

- Pour accéder au sondage : - {{ pollService.urlPrefix + '/#/poll/' + pollService.form.value.custom_url + '/consultation' }} - -

- - - Voir le sondage - -
-
-

{{ 'resume.links_mail' | translate }}

- - Voir le sondage - -
-
-
- -
+
diff --git a/src/app/features/administration/form/steps/step-five/step-five.component.ts b/src/app/features/administration/form/steps/step-five/step-five.component.ts index 04f429d2..cfd6c4fb 100644 --- a/src/app/features/administration/form/steps/step-five/step-five.component.ts +++ b/src/app/features/administration/form/steps/step-five/step-five.component.ts @@ -12,7 +12,7 @@ export class StepFiveComponent implements OnInit { @Input() step_max: any; @Input() public form: FormGroup; poll: any; - constructor(public pollService: PollService, private apiService: ApiService) {} + constructor(public pollService: PollService) {} ngOnInit(): void {} askInitFormDefault() { @@ -21,10 +21,5 @@ export class StepFiveComponent implements OnInit { } } - createPoll() { - let apiData = this.pollService.newPollFromForm(this.pollService._poll); - this.apiService.createPoll(apiData); - } - automaticSlug() {} } diff --git a/src/app/features/administration/form/steps/step-four/step-four.component.html b/src/app/features/administration/form/steps/step-four/step-four.component.html index 89bd173a..bf501989 100644 --- a/src/app/features/administration/form/steps/step-four/step-four.component.html +++ b/src/app/features/administration/form/steps/step-four/step-four.component.html @@ -1,7 +1,7 @@
- +
-
-

{{ 'creation.advanced' | translate }}

- -
- - -
- {{ urlPrefix }} {{ pollService.form.controls.custom_url.value }} - - - - - -
-
- 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 - +
@@ -119,8 +55,13 @@
-
diff --git a/src/app/features/administration/form/steps/step-four/step-four.component.ts b/src/app/features/administration/form/steps/step-four/step-four.component.ts index 0d29cf10..ad497bd0 100644 --- a/src/app/features/administration/form/steps/step-four/step-four.component.ts +++ b/src/app/features/administration/form/steps/step-four/step-four.component.ts @@ -1,5 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; import { PollService } from '../../../../../core/services/poll.service'; +import { ApiService } from '../../../../../core/services/api.service'; @Component({ selector: 'app-step-four', @@ -13,7 +14,12 @@ export class StepFourComponent implements OnInit { step_max: any; @Input() form: any; - constructor(public pollService: PollService) {} + constructor(public pollService: PollService, private apiService: ApiService) {} ngOnInit(): void {} + + createPoll() { + let apiData = this.pollService.newPollFromForm(this.pollService._poll); + this.apiService.createPoll(apiData); + } } diff --git a/src/app/features/administration/success/success.component.html b/src/app/features/administration/success/success.component.html index 3b37584b..2876aaee 100644 --- a/src/app/features/administration/success/success.component.html +++ b/src/app/features/administration/success/success.component.html @@ -6,7 +6,7 @@

Votre sondage « - {{ poll.title }} + {{ pollService.form.value.title }} » a bien été créé !

@@ -31,7 +31,7 @@

Pour accéder au sondage et à tous ses paramètres :
- {{ pollService.getAdministrationUrl() }} @@ -42,10 +42,10 @@

- Note : Le sondage sera supprimé {{ poll.default_expiracy_days_from_now }} jours après la date de sa - dernière modification. + Note : Le sondage sera supprimé {{ pollService.form.value.default_expiracy_days_from_now }} jours + après la date de sa dernière modification. - Le {{ poll.expiracy_date | date: 'short' }} + Le {{ pollService.form.value.expiracy_date | date: 'short' }}

@@ -54,7 +54,7 @@

Pour voir le sondage :
- {{ pollService.getParticipationUrl() }}

@@ -80,7 +80,7 @@

- + Voir le sondage côté public

diff --git a/src/app/features/administration/success/success.component.ts b/src/app/features/administration/success/success.component.ts index 8fc9a4bb..41af1f86 100644 --- a/src/app/features/administration/success/success.component.ts +++ b/src/app/features/administration/success/success.component.ts @@ -16,11 +16,12 @@ export class SuccessComponent { window: any = window; environment = environment; constructor(public pollService: PollService, private dateUtils: DateUtilitiesService, private titleService: Title) { - this.titleService.setTitle(environment.appTitle + ' - 🎉 succès de création de sondage -'); + this.titleService.setTitle( + environment.appTitle + ' - 🎉 succès de création de sondage - ' + this.pollService.form.value.title + ); this.pollService.poll.subscribe((newpoll: Poll) => { this.poll = newpoll; - // this.poll.expiracy_date = this.getExpiracyDateFromPoll(this.poll); }); }