parent
355fed53f3
commit
ae29749670
@ -1,19 +1,20 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
project: './tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||
'prettier/@typescript-eslint',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
rules: {},
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'prettier/@typescript-eslint',
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
project: './tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/unbound-method': ['error', { ignoreStatic: true }],
|
||||
},
|
||||
};
|
||||
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"/api/v1/*": "/$1",
|
||||
"/owners/:email/": "/owners?email=:email",
|
||||
"/choices": "/choices?_embed=answers",
|
||||
"/polls/:slug": "/polls?slug=:slug&_expand=owner&_embed=choices&_embed=comments",
|
||||
"/polls/:slug/choices": "/choices?pollSlug=:slug&_embed=answers",
|
||||
"/polls/:slug/comments": "/comments?pollSlug=:slug"
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"/api/v1/*": {
|
||||
"target": "http://localhost:8000",
|
||||
"secure": false,
|
||||
"pathRewrite": {
|
||||
"^/api/v1": ""
|
||||
},
|
||||
"changeOrigin": false,
|
||||
"logLevel": "debug"
|
||||
}
|
||||
}
|
@ -1,18 +1,12 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { MockingService } from '../../../services/mocking.service';
|
||||
import { Poll } from '../../../models/poll.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-navigation',
|
||||
templateUrl: './navigation.component.html',
|
||||
styleUrls: ['./navigation.component.scss'],
|
||||
})
|
||||
export class NavigationComponent implements OnInit {
|
||||
public pollsDatabase: Poll[] = [];
|
||||
constructor(private mockingService: MockingService) {}
|
||||
constructor() {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.pollsDatabase = this.mockingService.pollsDatabase;
|
||||
}
|
||||
ngOnInit(): void {}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
export enum Response {
|
||||
export enum ResponseType {
|
||||
YES = 'YES',
|
||||
NO = 'NO',
|
||||
MAYBE = 'MAYBE',
|
@ -1,6 +0,0 @@
|
||||
import { Response } from '../enums/response.enum';
|
||||
import { Choice } from './choice.model';
|
||||
|
||||
export class Answer {
|
||||
constructor(public author: string, public choice: Choice, public response: Response) {}
|
||||
}
|
@ -1,5 +1,38 @@
|
||||
import { Response } from '../enums/response.enum';
|
||||
import { ResponseType } from '../enums/response-type.enum';
|
||||
|
||||
export class Choice {
|
||||
constructor(public label: string, public responses: Response[] = [Response.YES, Response.NO]) {}
|
||||
constructor(
|
||||
public label: string,
|
||||
public imageUrl?: string,
|
||||
public participants: Map<ResponseType, Set<string>> = new Map<ResponseType, Set<string>>([
|
||||
[ResponseType.YES, new Set<string>()],
|
||||
[ResponseType.NO, new Set<string>()],
|
||||
[ResponseType.MAYBE, new Set<string>()],
|
||||
]),
|
||||
public counts: Map<ResponseType, number> = new Map<ResponseType, number>([
|
||||
[ResponseType.YES, 0],
|
||||
[ResponseType.NO, 0],
|
||||
[ResponseType.MAYBE, 0],
|
||||
])
|
||||
) {}
|
||||
|
||||
public updateParticipation(pseudo: string, responseType: ResponseType): void {
|
||||
this.removeParticipant(pseudo);
|
||||
this.participants.get(responseType).add(pseudo);
|
||||
this.updateCounts();
|
||||
}
|
||||
|
||||
public removeParticipant(pseudo: string): void {
|
||||
for (const responseType of Object.values(ResponseType)) {
|
||||
if (this.participants.get(responseType).has(pseudo)) {
|
||||
this.participants.get(responseType).delete(pseudo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public updateCounts(): void {
|
||||
for (const responseType of Object.values(ResponseType)) {
|
||||
this.counts.set(responseType, this.participants.get(responseType).size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,13 @@
|
||||
export class Comment {
|
||||
constructor(public author: string, public content: string) {}
|
||||
constructor(public author: string, public content: string, public dateCreated: Date) {}
|
||||
|
||||
public static sortChronologically(a: Comment, b: Comment): number {
|
||||
if (a.dateCreated < b.dateCreated) {
|
||||
return -1;
|
||||
}
|
||||
if (a.dateCreated > b.dateCreated) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,24 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { DateUtilsService } from '../utils/date-utils.service';
|
||||
|
||||
export class Configuration {
|
||||
constructor(
|
||||
public isAboutDate: boolean = false,
|
||||
public slug: string = uuidv4(),
|
||||
public isProtectedByPassword: boolean = false,
|
||||
public isOwnerNotifiedByEmail: { onNewVote: boolean; onNewComment: boolean } = {
|
||||
onNewVote: false,
|
||||
onNewComment: false,
|
||||
},
|
||||
public isMaybeAnswerAvailable: boolean = false,
|
||||
public creationDate: Date = new Date(Date.now()),
|
||||
public expirationDate: Date = DateUtilsService.addDaysToDate(
|
||||
public areResultsPublic: boolean = false,
|
||||
public dateCreated: Date = new Date(Date.now()),
|
||||
public expires: Date = DateUtilsService.addDaysToDate(
|
||||
environment.poll.defaultConfig.expiracyInDays,
|
||||
new Date(Date.now())
|
||||
)
|
||||
) {}
|
||||
|
||||
public getAdministrationUrl(): string {
|
||||
return `${environment.api.baseHref}/administration/${environment.api.endpoints.polls}/${this.slug}`;
|
||||
}
|
||||
|
||||
public getParticipationUrl(): string {
|
||||
return `${environment.api.baseHref}/participation/${environment.api.endpoints.polls}/${this.slug}`;
|
||||
public static isArchived(configuration: Configuration): boolean {
|
||||
return DateUtilsService.isDateInPast(configuration.expires);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
import { ResponseType } from '../enums/response-type.enum';
|
||||
import { Choice } from './choice.model';
|
||||
|
||||
export class PollUserAnswers {
|
||||
constructor(public pseudo: string, public token: string, public responsesByChoices: Map<Choice, ResponseType>) {}
|
||||
}
|
@ -1,17 +1,89 @@
|
||||
import { Answer } from './answer.model';
|
||||
import { environment } from 'src/environments/environment';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { ResponseType } from '../enums/response-type.enum';
|
||||
import { PollUserAnswers } from './poll-user-answers.model';
|
||||
import { Choice } from './choice.model';
|
||||
import { Comment } from './comment.model';
|
||||
import { Configuration } from './configuration.model';
|
||||
import { Question } from './question.model';
|
||||
import { User } from './user.model';
|
||||
|
||||
export class Poll {
|
||||
constructor(
|
||||
public owner: User,
|
||||
public question: Question,
|
||||
public choices: Choice[],
|
||||
public question: string,
|
||||
public description?: string,
|
||||
public slug: string = uuidv4(),
|
||||
public configuration: Configuration = new Configuration(),
|
||||
public answers: Answer[] = [],
|
||||
public comments: Comment[] = []
|
||||
public comments: Comment[] = [],
|
||||
public choices: Choice[] = [],
|
||||
public answersByChoiceByParticipant: Map<string, Map<string, ResponseType>> = new Map<
|
||||
string,
|
||||
Map<string, ResponseType>
|
||||
>(),
|
||||
public answers: PollUserAnswers[] = []
|
||||
) {}
|
||||
|
||||
public getAdministrationUrl(): string {
|
||||
return `${environment.api.baseHref}/administration/polls/${this.slug}`;
|
||||
}
|
||||
|
||||
public getParticipationUrl(): string {
|
||||
return `${environment.api.baseHref}/participation/polls/${this.slug}`;
|
||||
}
|
||||
|
||||
public static adaptFromLocalJsonServer(
|
||||
item: Pick<
|
||||
Poll,
|
||||
| 'owner'
|
||||
| 'question'
|
||||
| 'description'
|
||||
| 'slug'
|
||||
| 'configuration'
|
||||
| 'comments'
|
||||
| 'choices'
|
||||
| 'answersByChoiceByParticipant'
|
||||
| 'answers'
|
||||
>
|
||||
): Poll {
|
||||
const poll = new Poll(
|
||||
new User(item.owner.pseudo, item.owner.email, undefined),
|
||||
item.question,
|
||||
item.description,
|
||||
item.slug,
|
||||
item.configuration,
|
||||
item.comments
|
||||
.map(
|
||||
(c: Pick<Comment, 'author' | 'content' | 'dateCreated'>) =>
|
||||
new Comment(c.author, c.content, new Date(c.dateCreated))
|
||||
)
|
||||
.sort(Comment.sortChronologically),
|
||||
item.choices.map((c: Pick<Choice, 'label' | 'imageUrl' | 'participants' | 'imageUrl'>) => {
|
||||
const choice = new Choice(c.label, c.imageUrl, new Map(c.participants));
|
||||
choice.participants.forEach((value, key) => {
|
||||
choice.participants.set(key, new Set(value));
|
||||
});
|
||||
choice.updateCounts();
|
||||
return choice;
|
||||
})
|
||||
);
|
||||
|
||||
// handle answersByChoiceByParticipant
|
||||
for (const [pseudo, answersByChoice] of Object.entries(item.answersByChoiceByParticipant)) {
|
||||
if (!poll.answersByChoiceByParticipant.has(pseudo)) {
|
||||
poll.answersByChoiceByParticipant.set(pseudo, new Map<string, ResponseType>());
|
||||
}
|
||||
for (const [choiceLabel, answer] of Object.entries(answersByChoice)) {
|
||||
poll.answersByChoiceByParticipant.get(pseudo).set(choiceLabel, answer as ResponseType);
|
||||
}
|
||||
}
|
||||
|
||||
// handle answers
|
||||
poll.answers = item.answers.map(
|
||||
(pollUserAnswers: Pick<PollUserAnswers, 'pseudo' | 'token' | 'responsesByChoices'>) =>
|
||||
new PollUserAnswers(pollUserAnswers.pseudo, pollUserAnswers.token, pollUserAnswers.responsesByChoices)
|
||||
);
|
||||
|
||||
return poll;
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
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();
|
||||
});
|
||||
});
|
@ -1,15 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|