add fetch of config with admin key

This commit is contained in:
Tykayn 2021-11-12 15:59:44 +01:00 committed by tykayn
parent 96ef61c541
commit b9aea18d34
20 changed files with 180 additions and 33 deletions

View File

@ -3,7 +3,7 @@ import { RouterModule } from '@angular/router';
import { routes } from './routes-framadate'; import { routes } from './routes-framadate';
@NgModule({ @NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true })], imports: [RouterModule.forRoot(routes, { useHash: true, enableTracing: true })],
exports: [RouterModule], exports: [RouterModule],
}) })
export class AppRoutingModule {} export class AppRoutingModule {}

View File

@ -123,4 +123,10 @@
</div> </div>
</div> </div>
</div> </div>
<a
class="has-text-black"
href="http://localhost:4200/#/administration/key/8Ubcg2YI99f69xz946cn4O64bQAebi11012P70trL5372qJ9JOem1Ks2fz7XD0b09p-8Ubcg2YI99f69xz946cn4O64bQAeb"
>
<i class="fa fa-hand-paper-o"></i> test admin link to edit poll
</a>
</header> </header>

View File

@ -136,7 +136,9 @@ export class ApiService {
} }
} }
////////// /**
* get all polls published by the API
*/
public async getAllAvailablePolls(): Promise<Poll[]> { public async getAllAvailablePolls(): Promise<Poll[]> {
// TODO: used for facilities in DEV, should be removed in production // TODO: used for facilities in DEV, should be removed in production
try { try {
@ -147,10 +149,32 @@ export class ApiService {
} }
} }
public async getPollByCustomUrl(slug: string): Promise<Poll | undefined> { /**
* get one poll by its admin key
* @param admin_key
*/
public async getPollByAdminKey(admin_key: string): Promise<any | undefined> {
try { try {
console.log('fetch API : asking for poll with custom_url=' + slug); console.log('fetch API : asking for poll with admin_key=' + admin_key);
const response: AxiosResponse<Poll> = await this.axiosInstance.get<Poll>(`${this.pollsEndpoint}/${slug}`); const response: AxiosResponse<Poll> = await this.axiosInstance.get<any>(
`${this.pollsEndpoint}/admin/${admin_key}`
);
return response && response.data && !Array.isArray(response.data) ? response.data : undefined;
} catch (error) {
if (error.response?.status === 404) {
return undefined;
} else {
ApiService.handleError(error);
}
}
}
public async getPollByCustomUrl(custom_url: string): Promise<Poll | undefined> {
try {
console.log('fetch API : asking for poll with custom_url=' + custom_url);
const response: AxiosResponse<Poll> = await this.axiosInstance.get<Poll>(
`${this.pollsEndpoint}/${custom_url}`
);
return response && response.data && !Array.isArray(response.data) ? response.data : undefined; return response && response.data && !Array.isArray(response.data) ? response.data : undefined;
} catch (error) { } catch (error) {
@ -162,12 +186,12 @@ export class ApiService {
} }
} }
public async getPollByCustomUrlWithHash(slug: string, hash: string): Promise<Poll | undefined> { public async getPollByCustomUrlWithHash(custom_url: string, hash: string): Promise<Poll | undefined> {
try { try {
const response: AxiosResponse<Poll> = await this.axiosInstance.get<Poll>( const response: AxiosResponse<Poll> = await this.axiosInstance.get<Poll>(
`${this.pollsEndpoint}/${slug}/pass/${hash}` `${this.pollsEndpoint}/${custom_url}/pass/${hash}`
); );
console.log('fetch API : asking for poll with custom_url=' + slug, { response }); console.log('fetch API : asking for poll with custom_url=' + custom_url, { response });
return response && response.data && !Array.isArray(response.data) ? response.data : undefined; return response && response.data && !Array.isArray(response.data) ? response.data : undefined;
} catch (error) { } catch (error) {
@ -181,11 +205,11 @@ export class ApiService {
} }
} }
public async getSlug(slug: string): Promise<boolean> { public async getSlug(custom_url: string): Promise<boolean> {
try { try {
// TODO: scenario should be : if we can get this custom_url, it exists. if not, it doesn't. It's just a GET. // TODO: scenario should be : if we can get this custom_url, it exists. if not, it doesn't. It's just a GET.
const response: AxiosResponse = await this.axiosInstance.get( const response: AxiosResponse = await this.axiosInstance.get(
`${this.pollsEndpoint}${this.slugsEndpoint}/${slug}` `${this.pollsEndpoint}${this.slugsEndpoint}/${custom_url}`
); );
if (response?.status !== 404) { if (response?.status !== 404) {
return false; return false;
@ -201,7 +225,7 @@ export class ApiService {
//////////// ////////////
// UPDATE // // UPDATE //
////////////
public async sendUpdateVoteStack(vote_stack: Stack) { public async sendUpdateVoteStack(vote_stack: Stack) {
try { try {
return await this.axiosInstance.patch( return await this.axiosInstance.patch(

View File

@ -65,6 +65,7 @@ export class PollService implements Resolve<Poll> {
) { ) {
this.createFormGroup(); this.createFormGroup();
// fill in the next 3 days of the calendar date picker
this.calendar = [ this.calendar = [
this.DateUtilitiesService.addDaysToDate(1, new Date()), this.DateUtilitiesService.addDaysToDate(1, new Date()),
this.DateUtilitiesService.addDaysToDate(2, new Date()), this.DateUtilitiesService.addDaysToDate(2, new Date()),
@ -80,7 +81,7 @@ export class PollService implements Resolve<Poll> {
} }
/** /**
* add example values to the form * add example values to the form for demo env
*/ */
setDemoValues(): void { setDemoValues(): void {
this.addChoice('orange'); this.addChoice('orange');
@ -125,8 +126,6 @@ export class PollService implements Resolve<Poll> {
whoModifiesAnswers: ['', [Validators.required]], whoModifiesAnswers: ['', [Validators.required]],
whoCanChangeAnswers: ['', [Validators.required]], whoCanChangeAnswers: ['', [Validators.required]],
isAboutDate: [true, [Validators.required]], isAboutDate: [true, [Validators.required]],
// startDateInterval: ['', [Validators.required]],
// endDateInterval: ['', [Validators.required]],
expiresDaysDelay: [60, []], expiresDaysDelay: [60, []],
maxCountOfAnswers: [300, []], maxCountOfAnswers: [300, []],
isZeroKnoledge: [false, [Validators.required]], isZeroKnoledge: [false, [Validators.required]],
@ -148,6 +147,9 @@ export class PollService implements Resolve<Poll> {
return form; return form;
} }
/**
* set default configs to the form
*/
public patchFormDefaultValues() { public patchFormDefaultValues() {
this.form.patchValue({ this.form.patchValue({
title: 'mon titre de sondage', title: 'mon titre de sondage',
@ -169,6 +171,9 @@ export class PollService implements Resolve<Poll> {
this.setDefaultDatesForInterval(); this.setDefaultDatesForInterval();
} }
/**
* get a new slug from form title and creation date
*/
public updateSlug(): void { public updateSlug(): void {
console.log('this.form.value', this.form.value); console.log('this.form.value', this.form.value);
this.form.patchValue({ custom_url: this.makeSlug(this.form) }); this.form.patchValue({ custom_url: this.makeSlug(this.form) });
@ -176,6 +181,7 @@ export class PollService implements Resolve<Poll> {
/** /**
* auto fetch a poll when route is looking for one in the administration pattern * auto fetch a poll when route is looking for one in the administration pattern
* DO NOT USE - needs refacto
* @param route * @param route
* @param state * @param state
*/ */
@ -259,7 +265,7 @@ export class PollService implements Resolve<Poll> {
* update poll and parse its fields * update poll and parse its fields
* @param poll * @param poll
*/ */
public updateCurrentPoll(poll: Poll): void { public updateCurrentPoll(poll: Poll): Poll {
console.log('this.storageService.vote_stack.id', this.storageService.vote_stack.id); 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) { if (!this.storageService.vote_stack.id || this.storageService.vote_stack.poll_custom_url !== poll.custom_url) {
@ -272,8 +278,9 @@ export class PollService implements Resolve<Poll> {
// this.storageService.setChoicesForVoteStack(poll.choices); // this.storageService.setChoicesForVoteStack(poll.choices);
} }
this.toastService.display('sondage bien mis à jour', 'success');
this._poll.next(poll); this._poll.next(poll);
this.toastService.display(`sondage ${poll.title} bien mis à jour`, 'success');
return poll;
} }
/** /**
@ -577,7 +584,9 @@ export class PollService implements Resolve<Poll> {
public getAdministrationUrlFromForm(): string { public getAdministrationUrlFromForm(): string {
// admin_key is filled after creation // admin_key is filled after creation
return `${environment.frontDomain}#/admin/${this.admin_key}/consultation`; // example http://localhost:4200/#/administration/8Ubcg2YI99f69xz946cn4O64bQAeb
return `${environment.frontDomain}#/administration/${this.admin_key}`;
} }
public getParticipationUrl(): string { public getParticipationUrl(): string {
@ -648,6 +657,13 @@ export class PollService implements Resolve<Poll> {
return array_dates; return array_dates;
} }
patchFormWithPoll(poll: Poll) {
this.form.patchValue({
...poll,
isAboutDate: poll.kind == 'date',
});
}
/** /**
* @description convert to API version 1 data transition object * @description convert to API version 1 data transition object
*/ */

View File

@ -7,12 +7,15 @@ import { StepThreeComponent } from './form/steps/step-three/step-three.component
import { StepFourComponent } from './form/steps/step-four/step-four.component'; import { StepFourComponent } from './form/steps/step-four/step-four.component';
import { StepFiveComponent } from './form/steps/step-five/step-five.component'; import { StepFiveComponent } from './form/steps/step-five/step-five.component';
import { StepOneComponent } from './form/steps/step-one/step-one.component'; import { StepOneComponent } from './form/steps/step-one/step-one.component';
import { SuccessComponent } from './success/success.component';
import { AdminConsultationComponent } from './consultation/consultation.component';
const routes: Routes = [ const routes: Routes = [
{ {
path: '', path: '',
component: AdministrationComponent, component: AdministrationComponent,
}, },
{ path: 'key/:admin_key', component: AdminConsultationComponent },
{ {
path: 'step', path: 'step',
children: [ children: [
@ -23,6 +26,10 @@ const routes: Routes = [
{ path: '5', component: StepFiveComponent }, { path: '5', component: StepFiveComponent },
], ],
}, },
{
path: 'success',
component: SuccessComponent,
},
]; ];
@NgModule({ @NgModule({

View File

@ -20,7 +20,7 @@ export class AdministrationComponent implements OnInit, OnDestroy {
ngOnInit(): void { ngOnInit(): void {
this.routeSubscription = this.route.data.subscribe((data: { poll: Poll }) => { this.routeSubscription = this.route.data.subscribe((data: { poll: Poll }) => {
console.log('data', data); console.log('routeSubscription data', data);
if (data.poll) { if (data.poll) {
this.poll = data.poll; this.poll = data.poll;
} }

View File

@ -27,6 +27,7 @@ import { IntervalComponent } from './form/date/interval/interval.component';
import { DayListComponent } from './form/date/list/day/day-list.component'; import { DayListComponent } from './form/date/list/day/day-list.component';
import { PickerComponent } from './form/date/picker/picker.component'; import { PickerComponent } from './form/date/picker/picker.component';
import { TimeListComponent } from './form/date/list/time/time-list.component'; import { TimeListComponent } from './form/date/list/time/time-list.component';
import { AdminConsultationComponent } from './consultation/consultation.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -50,6 +51,7 @@ import { TimeListComponent } from './form/date/list/time/time-list.component';
DayListComponent, DayListComponent,
PickerComponent, PickerComponent,
TimeListComponent, TimeListComponent,
AdminConsultationComponent,
], ],
imports: [ imports: [
AdministrationRoutingModule, AdministrationRoutingModule,

View File

@ -0,0 +1,16 @@
<div class="admin-consultation min-height padded">
<h2 class="title is-2">Consulter le sondage</h2>
<button class="btn is-primary" [routerLink]="'/administration'">
<i class="fa fa-pencil"></i>
modifier
</button>
<div>
<h2>{{ form.value.title }}</h2>
<div *ngIf="poll">
<h2>{{ poll.title }}</h2>
Créé le {{ poll.created_at | date }} par {{ poll.owner.pseudo }}, {{ poll.owner.email }}
</div>
</div>
</div>

View File

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

View File

@ -0,0 +1,54 @@
import { Component, OnInit } from '@angular/core';
import { PollService } from '../../../core/services/poll.service';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { ApiService } from '../../../core/services/api.service';
import { FormGroup } from '@angular/forms';
import { Poll } from '../../../core/models/poll.model';
@Component({
selector: 'app-admin-consultation',
templateUrl: './consultation.component.html',
styleUrls: ['./consultation.component.scss'],
})
export class AdminConsultationComponent implements OnInit {
private admin_key: string;
public form: FormGroup;
public poll: any;
constructor(
private pollService: PollService,
private apiService: ApiService,
private _Activatedroute: ActivatedRoute,
private router: Router
) {
this.poll = this.pollService._poll.getValue();
this.form = this.pollService.form;
}
ngOnInit(): void {
this._Activatedroute.paramMap.subscribe((params: ParamMap) => {
this.admin_key = params.get('admin_key');
if (!this.admin_key) {
this.router.navigate('page-not-found');
}
this.apiService.getPollByAdminKey(this.admin_key).then(
(res) => {
this.pollService.updateCurrentPoll(res.poll);
this.patchFormLoadedWithPoll();
this.form = this.pollService.form;
this.poll = this.pollService._poll.getValue();
console.log('formulaire patché', this.pollService.form, this.pollService.poll);
},
(err) => {
if (!this.admin_key) {
this.router.navigate('page-not-found');
}
}
);
});
}
patchFormLoadedWithPoll() {
this.pollService.patchFormWithPoll(this.pollService._poll.getValue());
}
}

View File

@ -1,7 +1,7 @@
<div class="step"> <div class="step">
<div class="min-height"> <div class="min-height">
<form action="#" [formGroup]="pollService.form"> <form action="#" [formGroup]="pollService.form">
<app-stepper [step_current]="pollService.step_current" [step_max]="5"></app-stepper> <app-stepper [step_current]="4" [step_max]="pollService.step_max"></app-stepper>
<div class="creator-infos"> <div class="creator-infos">
<label class="" for="creatorEmail"> <label class="" for="creatorEmail">

View File

@ -2,6 +2,7 @@ import { Component, Input, OnInit } from '@angular/core';
import { PollService } from '../../../../../core/services/poll.service'; import { PollService } from '../../../../../core/services/poll.service';
import { ApiService } from '../../../../../core/services/api.service'; import { ApiService } from '../../../../../core/services/api.service';
import { environment } from '../../../../../../environments/environment'; import { environment } from '../../../../../../environments/environment';
import { Router } from '@angular/router';
@Component({ @Component({
selector: 'app-step-four', selector: 'app-step-four',
@ -15,12 +16,14 @@ export class StepFourComponent implements OnInit {
step_max: any; step_max: any;
@Input() @Input()
form: any; form: any;
constructor(public pollService: PollService, private apiService: ApiService) {} constructor(private router: Router, public pollService: PollService, private apiService: ApiService) {}
ngOnInit(): void {} ngOnInit(): void {}
createPoll() { createPoll() {
let apiData = this.pollService.newPollFromForm(); let apiData = this.pollService.newPollFromForm();
this.apiService.createPoll(apiData); this.apiService.createPoll(apiData).then((resp) => {
this.router.navigate(['administration/success']);
});
} }
} }

View File

@ -44,7 +44,7 @@
<p class="note"> <p class="note">
Note : Le sondage sera supprimé {{ pollService.form.value.default_expiracy_days_from_now }} jours Note : Le sondage sera supprimé {{ pollService.form.value.default_expiracy_days_from_now }} jours
après la date de sa dernière modification. après la date de sa dernière modification.
<span class="expiracy-detail" *ngIf="poll.expiracy_date"> <span class="expiracy-detail" *ngIf="pollService.form.value.expiracy_date">
Le {{ pollService.form.value.expiracy_date | date: 'short' }} Le {{ pollService.form.value.expiracy_date | date: 'short' }}
</span> </span>
</p> </p>

View File

@ -19,5 +19,5 @@ a {
padding: 2em; padding: 2em;
} }
.has-background-success { .has-background-success {
background: $logo_color_2; background: $logo_color_2 !important;
} }

View File

@ -7,6 +7,7 @@ import { PasswordPromptComponent } from './password/password-prompt/password-pro
const routes: Routes = [ const routes: Routes = [
{ path: 'secure/:pass_hash', component: ConsultationComponent }, { path: 'secure/:pass_hash', component: ConsultationComponent },
{ path: 'prompt', component: PasswordPromptComponent }, { path: 'prompt', component: PasswordPromptComponent },
{ path: 'simple', component: WipTodoComponent }, { path: 'simple', component: WipTodoComponent },
{ path: 'table', component: WipTodoComponent }, { path: 'table', component: WipTodoComponent },

View File

@ -8,7 +8,7 @@
</div> </div>
<div class="message is-warning" *ngIf="poll && poll.admin_key"> <div class="message is-warning" *ngIf="poll && poll.admin_key">
<div class="message-body"> <div class="message-body">
vous êtes admin de ce sondage vous êtes admin de ce sondage et pouvez le modifier
</div> </div>
</div> </div>

View File

@ -20,13 +20,6 @@ export const routes: Routes = [
data: { animation: 'AdminPage' }, data: { animation: 'AdminPage' },
loadChildren: () => loadChildren: () =>
import('./features/administration/administration.module').then((m) => m.AdministrationModule), import('./features/administration/administration.module').then((m) => m.AdministrationModule),
// resolve: { poll: PollService },
},
{
path: 'admin/:admin_key',
loadChildren: () =>
import('./features/administration/administration.module').then((m) => m.AdministrationModule),
// resolve: { poll: PollService },
}, },
{ {
path: 'poll/:custom_url/consultation', path: 'poll/:custom_url/consultation',

View File

@ -73,7 +73,7 @@
"continue": "Voyons ce que ça donne" "continue": "Voyons ce que ça donne"
}, },
"resume": { "resume": {
"title": "Et c'est tout pour nous !", "title": "Félicitations !",
"admins": "Côté administrateur-ice-eux", "admins": "Côté administrateur-ice-eux",
"users": "Côté sondés", "users": "Côté sondés",
"links_mail": "Recevoir les liens par e-mail" "links_mail": "Recevoir les liens par e-mail"

View File

@ -16,7 +16,8 @@ export const environment = {
autofill_participation: true, autofill_participation: true,
// autofill: false, // autofill: false,
showDemoWarning: false, showDemoWarning: false,
autoSendNewPoll: true, // autoSendNewPoll: true,
autoSendNewPoll: false,
interval_days_default: 7, interval_days_default: 7,
expiresDaysDelay: 60, expiresDaysDelay: 60,
maxCountOfAnswers: 150, maxCountOfAnswers: 150,