parent
18a7d4781a
commit
e524320457
@ -1,42 +0,0 @@
|
||||
import { AdminComponent } from '../pages/admin/admin.component';
|
||||
import { AnswersComponent } from '../pages/answers/answers.component';
|
||||
import { BaseComponent } from '../pages/base-page/base.component';
|
||||
import { CreateOrRetrieveComponent } from '../pages/create-or-retrieve/create-or-retrieve.component';
|
||||
import { DatesComponent } from '../pages/dates/dates.component';
|
||||
import { EndConfirmationComponent } from '../pages/end-confirmation/end-confirmation.component';
|
||||
import { HomeComponent } from '../pages/home/home.component';
|
||||
import { KindComponent } from '../pages/kind/kind.component';
|
||||
import { PasswordComponent } from '../pages/password/password.component';
|
||||
import { PicturesComponent } from '../pages/pictures/pictures.component';
|
||||
import { PollDisplayComponent } from '../pages/poll-display/poll-display.component';
|
||||
import { ResumeComponent } from '../pages/resume/resume.component';
|
||||
import { VisibilityComponent } from '../pages/visibility/visibility.component';
|
||||
import { VotingChoiceComponent } from '../pages/voting/voting-choice/voting-choice.component';
|
||||
import { VotingComponent } from '../pages/voting/voting.component';
|
||||
import { PollGraphicComponent } from '../poll-graphic/poll-graphic.component';
|
||||
|
||||
/**
|
||||
* each step in the form is a component
|
||||
*/
|
||||
export const Routes = [
|
||||
{ path: '', redirectTo: 'step/creation', pathMatch: 'full' },
|
||||
{ path: 'home', component: HomeComponent },
|
||||
{ path: 'base', component: BaseComponent },
|
||||
{ path: 'step/base', component: BaseComponent },
|
||||
{ path: 'step/creation', component: CreateOrRetrieveComponent },
|
||||
{ path: 'step/date', component: DatesComponent },
|
||||
{ path: 'step/kind', component: KindComponent },
|
||||
{ path: 'step/answers', component: AnswersComponent },
|
||||
{ path: 'step/admin', component: AdminComponent },
|
||||
{ path: 'step/pictures', component: PicturesComponent },
|
||||
{ path: 'step/visibility', component: VisibilityComponent },
|
||||
{ path: 'step/resume', component: ResumeComponent },
|
||||
{ path: 'step/end', component: EndConfirmationComponent },
|
||||
{ path: 'graphic/:poll', component: PollGraphicComponent },
|
||||
{ path: 'vote/poll/id/:poll', component: PollDisplayComponent },
|
||||
{ path: 'vote/poll/slug/:pollSlug', component: PollDisplayComponent },
|
||||
{ path: 'votingchoice', component: VotingChoiceComponent },
|
||||
{ path: 'voting', component: VotingComponent },
|
||||
{ path: 'step/password', component: PasswordComponent },
|
||||
{ path: '**', redirectTo: '/home', pathMatch: 'full' },
|
||||
];
|
@ -1,24 +0,0 @@
|
||||
export var graphOptions = {
|
||||
legend: { display: false },
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
gridLines: { drawBorder: false, display: false },
|
||||
display: false,
|
||||
stacked: true,
|
||||
ticks: {
|
||||
beginAtZero: true,
|
||||
maxRotation: 0,
|
||||
minRotation: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
gridLines: { drawBorder: true, display: false },
|
||||
display: true,
|
||||
stacked: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [CommonModule],
|
||||
})
|
||||
export class PollAdministrationModule {}
|
@ -0,0 +1,8 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [CommonModule],
|
||||
})
|
||||
export class PollParticipationModule {}
|
@ -1,12 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PollService } from './poll.service';
|
||||
|
||||
describe('PollServiceService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: PollService = TestBed.get(PollService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,11 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class PollService {
|
||||
private baseHref: string = environment.baseApiHref;
|
||||
|
||||
constructor() {}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ConfigService } from './config.service';
|
||||
|
||||
describe('ConfigService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: ConfigService = TestBed.get(ConfigService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,4 @@
|
||||
export enum AnswerGranularity {
|
||||
BASIC = 'BASIC',
|
||||
COMPLEX = 'COMPLEX',
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export enum AnswerType {
|
||||
YES = 'YES',
|
||||
NO = 'NO',
|
||||
MAYBE = 'MAYBE',
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
export enum PollType {
|
||||
CLASSIC = 'CLASSIC',
|
||||
DATES = 'DATES',
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export enum Theme {
|
||||
LIGHT = 'LIGHT',
|
||||
DARK = 'DARK',
|
||||
RED = 'RED',
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export interface DateOption {
|
||||
timeList: any;
|
||||
literal: string;
|
||||
date_object?: object;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import { AnswerType } from '../enums/answer-type.enum';
|
||||
import { PollOption } from './poll-options.model';
|
||||
|
||||
export class Answer {
|
||||
constructor(public pollOption: PollOption, public type: AnswerType, public userPseudo: string) {}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { AnswerGranularity } from '../enums/answer-granularity.enum';
|
||||
|
||||
export class PollConfig {
|
||||
constructor(
|
||||
public allowSeveralHours = true,
|
||||
public isVisibleToAnyoneWithTheLink: boolean = true,
|
||||
public answerType: AnswerGranularity = AnswerGranularity.BASIC,
|
||||
public creationDate: Date = new Date(),
|
||||
public expirationDate?: Date,
|
||||
public canVotersModifyTheirAnswers = true,
|
||||
public isProtectedByPassword: boolean = false
|
||||
) {}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
import { isValid } from 'date-fns';
|
||||
|
||||
export class PollOption {
|
||||
constructor(public label: string, public url?: string, public subOptions?: PollOption[]) {}
|
||||
|
||||
public isDatePoll(): boolean {
|
||||
return isValid(this.label);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
import { PollType } from '../enums/poll-type.enum';
|
||||
import { Answer } from './answer.model';
|
||||
import { PollConfig } from './poll-config.model';
|
||||
import { PollOption } from './poll-options.model';
|
||||
import { User } from './user.model';
|
||||
|
||||
export class Poll {
|
||||
constructor(
|
||||
public id: string,
|
||||
public slug: string,
|
||||
public type: PollType,
|
||||
public title: string,
|
||||
public description: string,
|
||||
public owner: User,
|
||||
public config: PollConfig,
|
||||
public options: PollOption[] = [],
|
||||
public answers: Answer[] = []
|
||||
) {}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
import { Theme } from '../enums/theme.enum';
|
||||
|
||||
export class UIConfig {
|
||||
constructor(public isMenuVisible = true, public theme: Theme.LIGHT) {}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
import { Poll } from './poll.model';
|
||||
|
||||
export class User {
|
||||
constructor(
|
||||
public isOwner: boolean = false,
|
||||
public pseudo?: string,
|
||||
public email?: string,
|
||||
public polls?: Poll[]
|
||||
) {}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ApiService } from './api.service';
|
||||
|
||||
describe('ApiService', () => {
|
||||
let service: ApiService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(ApiService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,195 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import axios, { AxiosResponse } from 'axios';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
import { Poll } from '../models/poll.model';
|
||||
import { User } from '../models/user.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ApiService {
|
||||
////////////
|
||||
// CREATE //
|
||||
////////////
|
||||
public async savePoll(poll: Poll): Promise<void> {
|
||||
try {
|
||||
await axios.post(`${environment.api.baseHref}${environment.api.endpoints.poll.root}`, {
|
||||
params: { config: poll.config },
|
||||
});
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
public async saveVote(poll: Poll): Promise<void> {
|
||||
try {
|
||||
// TODO: add the votestack in the params
|
||||
await axios.post(
|
||||
`${environment.api.baseHref}${environment.api.endpoints.poll.root}/${poll.id}${environment.api.endpoints.vote}`,
|
||||
{ params: { voteStack: {} } }
|
||||
);
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
public async saveComment(poll: Poll, comment: string): Promise<void> {
|
||||
try {
|
||||
// TODO: add the comment in the params
|
||||
await axios.post(
|
||||
`${environment.api.baseHref}${environment.api.endpoints.poll.root}/${poll.id}${environment.api.endpoints.comment}`,
|
||||
{ params: { comment } }
|
||||
);
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
//////////
|
||||
// READ //
|
||||
//////////
|
||||
public async isSlugAvailable(slug: string): Promise<boolean> {
|
||||
try {
|
||||
// TODO: what is the return of the API ? could it be changed to an object { isAvailable: true } ?
|
||||
const response: AxiosResponse = await axios.get(
|
||||
`${environment.api.baseHref}${environment.api.endpoints.slug.isAvailable}/${slug}`
|
||||
);
|
||||
if (response) {
|
||||
const { isAvailable } = response.data;
|
||||
return isAvailable;
|
||||
}
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
public async sendUserPollsByEmail(email: string): Promise<Poll[]> {
|
||||
// If user is not authenticated: the list of polls is send to user's email by the backend.
|
||||
try {
|
||||
const response: AxiosResponse<Poll[]> = await axios.get<Poll[]>(
|
||||
`${environment.api.baseHref}${environment.api.endpoints.poll.byEmail.sendEmail}/${email}`
|
||||
);
|
||||
return response ? response.data : [];
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
public async getUserPollsByEmail(user: User): Promise<Poll[]> {
|
||||
// If user is authenticated : retrieve polls & display directly in frontend.
|
||||
// TODO: Backend should handle this case. Actually the endpoint doesn't exist in backend.
|
||||
try {
|
||||
const response: AxiosResponse<Poll[]> = await axios.get<Poll[]>(
|
||||
`${environment.api.baseHref}${environment.api.endpoints.poll.byEmail.retrieve}/${user.email}`
|
||||
);
|
||||
return response ? response.data : [];
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
public async getPollsByUrl(url: string): Promise<Poll[]> {
|
||||
try {
|
||||
const response: AxiosResponse<Poll[]> = await axios.get<Poll[]>(
|
||||
`${environment.api.baseHref}${environment.api.endpoints.poll.byUrl}/${url}`
|
||||
);
|
||||
return response ? response.data : [];
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
public async getPollById(id: string, password?: string): Promise<Poll[]> {
|
||||
try {
|
||||
const response: AxiosResponse<Poll[]> = await axios.get<Poll[]>(
|
||||
`${environment.api.baseHref}${environment.api.endpoints.poll.root}/${id}`,
|
||||
password ? { params: { password } } : {}
|
||||
);
|
||||
return response ? response.data : [];
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
////////////
|
||||
// UPDATE //
|
||||
////////////
|
||||
public async updatePoll(poll: Poll): Promise<void> {
|
||||
try {
|
||||
// TODO: implement the params when entities are finalized.
|
||||
await axios.put(`${environment.api.baseHref}${environment.api.endpoints.poll.root}/${poll.id}`, {
|
||||
params: { voteStack: {}, token: '' },
|
||||
});
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
public async updateVote(voteStack: any): Promise<void> {
|
||||
try {
|
||||
// TODO: implement the params when entities are finalized.
|
||||
await axios.patch(`${environment.api.baseHref}${environment.api.endpoints.vote.root}/${voteStack.id}`, {
|
||||
params: { voteStack: {}, token: '' },
|
||||
});
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
////////////
|
||||
// DELETE //
|
||||
////////////
|
||||
|
||||
public async deletePoll(poll: Poll): Promise<void> {
|
||||
try {
|
||||
await axios.delete(`${environment.api.baseHref}${environment.api.endpoints.poll.root}${poll.id}`, {});
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
public async deletePollVotes(poll: Poll): Promise<void> {
|
||||
try {
|
||||
// TODO: update endpoint in Backend
|
||||
await axios.delete(
|
||||
`${environment.api.baseHref}${environment.api.endpoints.poll.root}${poll.id}${environment.api.endpoints.vote.root}`
|
||||
);
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
public async deletePollComments(poll: Poll): Promise<void> {
|
||||
try {
|
||||
// TODO: modify endpoint in Backend
|
||||
await axios.delete(
|
||||
`${environment.api.baseHref}${environment.api.endpoints.poll.root}${poll.id}${environment.api.endpoints.comment.root}`
|
||||
);
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// PRIVATE METHODS //
|
||||
/////////////////////
|
||||
private handleError(error): void {
|
||||
if (error.response) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
console.log(error.response.data);
|
||||
console.log(error.response.status);
|
||||
console.log(error.response.headers);
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
console.log(error.request);
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.log('Error', error.message);
|
||||
}
|
||||
console.log(error.config);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CommentService } from './comment.service';
|
||||
|
||||
describe('CommentService', () => {
|
||||
let service: CommentService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(CommentService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,15 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { Poll } from '../models/poll.model';
|
||||
import { ApiService } from './api.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CommentService {
|
||||
constructor(private apiService: ApiService) {}
|
||||
|
||||
public saveComment(poll: Poll, comment: string): void {
|
||||
this.apiService.saveComment(poll, comment);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DateUtilsService } from './date-utils.service';
|
||||
|
||||
describe('DateUtilsService', () => {
|
||||
let service: DateUtilsService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(DateUtilsService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,50 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { addDays, differenceInDays, format } from 'date-fns';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DateUtilsService {
|
||||
public static addDaysToDate(days: number, date: Date): Date {
|
||||
return addDays(date, days);
|
||||
}
|
||||
|
||||
public static diffInDays(dateLeft: Date, dateRight: Date): number {
|
||||
return differenceInDays(dateLeft, dateRight);
|
||||
}
|
||||
|
||||
public static formatDate(date): string {
|
||||
return format(date, 'yyyy-MM-dd');
|
||||
}
|
||||
|
||||
public static orderDates(): Date[] {
|
||||
// TODO: to implement
|
||||
const datesOrdered: Date[] = [];
|
||||
return datesOrdered;
|
||||
}
|
||||
|
||||
public static getDatesInRange(d1: Date, d2: Date, interval: number): Date[] {
|
||||
// TODO: refacto this
|
||||
d1 = new Date(d1);
|
||||
d2 = new Date(d2);
|
||||
const dates = [];
|
||||
while (+d1 < +d2) {
|
||||
dates.push({
|
||||
literal: this.formateDate(d1),
|
||||
date_object: d1,
|
||||
});
|
||||
d1.setDate(d1.getDate() + interval);
|
||||
}
|
||||
return [...dates];
|
||||
}
|
||||
|
||||
public static getDoubleDigits(str: string): string {
|
||||
// TODO: ça sert à quoi ça ?
|
||||
// Parce que ajouter 2 caractère à une string et ensuite slicer à partir du 2ème caractère, euh…
|
||||
return ('00' + str).slice(-2);
|
||||
}
|
||||
|
||||
private isInChronologicalOrder(date1: Date, date2: Date): boolean {
|
||||
return date1 < date2;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PollUtilsService } from './poll-utils.service';
|
||||
|
||||
describe('PollUtilsService', () => {
|
||||
let service: PollUtilsService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(PollUtilsService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,47 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class PollUtilsService {
|
||||
public makeUuid(): string {
|
||||
// TODO: how to be sure the uuid generated in front is available in backend ?
|
||||
// It could be a better way to generate uuids in backend.
|
||||
return uuidv4();
|
||||
}
|
||||
|
||||
/**
|
||||
* make a uniq slug for the current poll creation
|
||||
* @param str
|
||||
*/
|
||||
public makeSlug(config: PollConfig): string {
|
||||
let str = '';
|
||||
str =
|
||||
config.creationDate.getFullYear() +
|
||||
'_' +
|
||||
(config.creationDate.getMonth() + 1) +
|
||||
'_' +
|
||||
config.creationDate.getDate() +
|
||||
'_' +
|
||||
config.myName +
|
||||
'_' +
|
||||
config.title;
|
||||
str = str.replace(/^\s+|\s+$/g, ''); // trim
|
||||
str = str.toLowerCase();
|
||||
|
||||
// remove accents, swap ñ for n, etc
|
||||
const from = 'àáäâèéëêìíïîòóöôùúüûñç·/_,:;';
|
||||
const to = 'aaaaeeeeiiiioooouuuunc------';
|
||||
for (let i = 0, l = from.length; i < l; i++) {
|
||||
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
|
||||
}
|
||||
|
||||
str = str
|
||||
.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
|
||||
.replace(/\s+/g, '-') // collapse whitespace and replace by -
|
||||
.replace(/-+/g, '-'); // collapse dashes
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PollService } from './poll.service';
|
||||
|
||||
describe('PollService', () => {
|
||||
let service: PollService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(PollService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,42 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
|
||||
import { Poll } from '../models/poll.model';
|
||||
import { ApiService } from './api.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class PollService {
|
||||
private _poll: BehaviorSubject<Poll | undefined> = new BehaviorSubject<Poll | undefined>(undefined);
|
||||
|
||||
constructor(private apiService: ApiService) {}
|
||||
|
||||
public get poll(): Observable<Poll | undefined> {
|
||||
return this._poll.asObservable();
|
||||
}
|
||||
|
||||
public updateCurrentPoll(poll: Poll): void {
|
||||
this._poll.next(poll);
|
||||
}
|
||||
|
||||
public savePoll(poll: Poll): void {
|
||||
this.apiService.savePoll(poll);
|
||||
}
|
||||
|
||||
public saveVote(poll: Poll): void {
|
||||
this.apiService.saveVote(poll);
|
||||
}
|
||||
|
||||