get poll data in vote choices

This commit is contained in:
Baptiste Lemoine 2020-01-15 17:55:22 +01:00
parent 4296c5c9f9
commit 5657f57fc7
23 changed files with 392 additions and 114 deletions

View File

@ -17,7 +17,7 @@ module.exports = function (config) {
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/framadate'),
reports: ['html', 'lcovonly', 'text-summary'],
reports: ['html', 'lcovonly', 'name-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],

View File

@ -45,6 +45,7 @@ import {ConfigService} from "./services/config.service";
import {PollService} from "./services/poll.service";
import {ToastModule} from 'primeng/toast';
import {MessageService} from "primeng";
import { PollDisplayComponent } from './pages/poll-display/poll-display.component';
export class MyMissingTranslationHandler implements MissingTranslationHandler {
handle(params: MissingTranslationHandlerParams) {
@ -83,6 +84,7 @@ export function HttpLoaderFactory(http: HttpClient) {
VoteChoiceComponent,
AdminComponent,
SelectorComponent,
PollDisplayComponent,
],
imports: [

View File

@ -11,6 +11,7 @@ import {BaseComponent} from '../pages/base-page/base.component';
import {HomeComponent} from "../pages/home/home.component";
import {PollGraphicComponent} from '../poll-graphic/poll-graphic.component';
import {VoteChoiceComponent} from "../vote-choice/vote-choice.component";
import {PollDisplayComponent} from "../pages/poll-display/poll-display.component";
/**
* each step in the form is a component
@ -31,6 +32,7 @@ export const Routes =
{path: 'step/resume', component: ResumeComponent},
{path: 'step/end', component: EndConfirmationComponent},
{path: 'graphic/:poll', component: PollGraphicComponent},
{path: 'vote/poll/id/:poll', component: PollDisplayComponent},
{path: 'votechoice', component: VoteChoiceComponent},
{path: '**', redirectTo: '/home', pathMatch: 'full'},
]

View File

@ -0,0 +1,186 @@
export const mockPoll3 = {
"message": "your poll config",
"data": {
"id": 3,
"title": "dessin animé préféré",
"customUrl": null,
"description": "choisissez votre animé préféré",
"creationDate": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"expiracyDate": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"owner": {
"__initializer__": {},
"__cloner__": {},
"__isInitialized__": false
},
"kind": "text",
"allowedAnswers": [
"yes"
],
"modificationPolicy": "self",
"mailOnComment": null,
"mailOnVote": null,
"hideResults": null,
"showResultEvenIfPasswords": null,
"votes": {},
"stacksOfVotes": {},
"choices": {},
"comments": {},
"defaultExpiracyDaysFromNow": 60
},
"stacks_count": 2,
"stacks": [
{
"pseudo": "Wulfila",
"votes": {}
},
{
"pseudo": "Tykayn",
"votes": {}
}
],
"choices_count": 7,
"choices": [
{
"id": 5,
"name": "Vic le viking",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
},
{
"id": 6,
"name": "Boumbo petite automobile",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
},
{
"id": 7,
"name": "Les mystérieuses cités d'or",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
},
{
"id": 8,
"name": "Les mondes engloutis",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
},
{
"id": 9,
"name": "Foot 2 rue",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
},
{
"id": 10,
"name": "Le chat, la vache, et l'océan",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
},
{
"id": 11,
"name": "Digimon",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
}
],
"comments": []
}

View File

@ -18,26 +18,31 @@
type de formulaire: {{config.pollType}}
</li>
</ul>
<button
class="btn btn--primary"
i18n
(click)="config.createPoll()"
>
Envoyer le formulaire
</button>
<button
class="btn btn--primary"
i18n
(click)="config.getPollById(1, 'example password')"
>
get poll 1
</button>
<button
class="btn btn--primary"
i18n
(click)="config.getMyPolls( 'tktest@tktest.com')"
>
get my polls
</button>
<button class="btn btn--success" (click)="launchToast()">
launch success toast
</button>
<a [routerLink]="'/vote/poll/id/3'" class="btn btn--success">
See example of vote page
</a>
</div>
<button
class="btn btn--primary"
i18n
(click)="config.createPoll()"
>
Envoyer le formulaire
</button>
<button
class="btn btn--primary"
i18n
(click)="config.getPollById(1, 'example password')"
>
get poll 1
</button>
<button
class="btn btn--primary"
i18n
(click)="config.getMyPolls( 'tktest@tktest.com')"
>
get my polls
</button>

View File

@ -23,4 +23,8 @@ export class DebuggerComponent implements OnInit {
return true;
}
launchToast() {
this.config.handleError({message: "hop"})
}
}

View File

@ -3,11 +3,11 @@
<!--Infos-->
<h2>Informations du sondage</h2>
<label for="title">Le titre du sondage est</label>
<input type="text" name="title">
<input type="name" name="title">
<label for="desc">et sa description :</label>
<textarea name="desc"> </textarea>
<label for="name">Je peux aussi préciser mon nom si je le souhaite :</label>
<input type="text" name="name">
<input type="name" name="name">
<!--Params-->
<h2>Paramètres</h2>
<h3>Visibilité des réponses</h3>

View File

@ -11,7 +11,7 @@
<li #answers *ngFor="let answer of config.answers; index as i;trackBy trackFunction"
class="answer-item">
<input
type="text"
type="name"
class="answer"
[(ngModel)]="answer.text"
(keyup.enter)="addAnswer()"

View File

@ -56,14 +56,17 @@
/>
</form>
</section>
<section class="list-my-polls">
<section class="list-my-polls" *ngIf="!config.loading">
<ul class="poll-list" *ngFor="let poll of config.myPolls">
<li> poll</li>
</ul>
<div class="loading">
<i class="fa fa-refresh fa-spin fa-3x fa-fw"></i>
</div>
<div class="no-polls" *ngIf="!config.loading && (! config.myPolls || !config.myPolls.length)">
<div class="no-polls" *ngIf="! config.myPolls || !config.myPolls.length">
Aucun sondage.
</div>
</section>
<div class="loading" *ngIf="config.loading">
<i class="fa fa-refresh fa-spin fa-3x fa-fw"></i>
</div>

View File

@ -1,7 +1,6 @@
import {Component, OnInit} from '@angular/core';
import {BaseComponent} from "../base-page/base.component";
import {ConfigService} from "../../services/config.service";
import {PollService} from "../../services/poll.service";
@Component({
selector: 'framadate-create-or-retrieve',
@ -12,8 +11,7 @@ export class CreateOrRetrieveComponent extends BaseComponent implements OnInit {
loadedMyPolls: boolean = false;
constructor(public config: ConfigService,
public pollService: PollService) {
constructor(public config: ConfigService) {
super(config);
}

View File

@ -89,7 +89,7 @@
<input
[(ngModel)]="choice.literal"
name="timeChoices_{{id}}"
type="text"
type="name"
>
<button (click)="config.timeList.splice(id, 1)" class="btn btn-warning">X</button>
</div>
@ -133,7 +133,7 @@
<input
[(ngModel)]="time.literal"
name="dateTime_{{id}}_Choices_{{idTime}}"
type="text"
type="name"
>
<button
(click)="choice.timeList.splice(idTime, 1)"

View File

@ -36,7 +36,7 @@
{{"creation.choose_title"|translate}}
</label>
<input
type="text"
type="name"
id="poll_title"
name="poll_title"
placeholder="{{'creation.choose_title_placeholder'|translate}}"
@ -49,7 +49,7 @@
{{"creation.name"|translate}} :
</label>
<input
type="text"
type="name"
name="my_name"
id="my_name"
placeholder="{{'creation.name_placeholder'|translate}}"

View File

@ -104,9 +104,9 @@
<label for="">Un label pour les labelliser tous</label>
<h3>Input text</h3>
<input type="text" name="" id=""><br>
<input type="text" name="" id="" value="texte">
<h3>Input name</h3>
<input type="name" name="" id=""><br>
<input type="name" name="" id="" value="texte">
<h3>Input email</h3>
<input type="email" name="" id=""><br>
@ -198,9 +198,9 @@
</article>
<article>
<h2>Label + input text</h2>
<h2>Label + input name</h2>
<label for="test-text">Ceci est un label un peu long mais pas trop</label>
<input type="text" name="test-text" id="test-text">
<input type="name" name="test-text" id="test-text">
</article>
<article>
@ -220,7 +220,7 @@
</article>
<article>
<h2>Input text with info</h2>
<h2>Input name with info</h2>
<a href="https://sketch.cloud/s/00A80/a/MAl5q7">like here</a>
</article>

View File

@ -0,0 +1,16 @@
<div class="poll">
<h1>{{pollConfigFetched.data.title}}</h1>
<p>{{pollConfigFetched.data.description}}</p>
<p class="creationDate">{{pollConfigFetched.data.creationDate.date}}</p>
<p class="expiracyDate">{{pollConfigFetched.data.creationDate.date}}</p>
<p class="votants">
{{pollConfigFetched.stacks_count}} votants,
{{pollConfigFetched.choices_count}} choix,
</p>
</div>
<div class="list-of-choices">
<div *ngFor="let choice of pollConfigFetched.choices">
<framadate-vote-choice [choice]="choice"></framadate-vote-choice>
</div>
</div>

View File

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

View File

@ -0,0 +1,23 @@
import {Component, OnInit} from '@angular/core';
import {BaseComponent} from "../base-page/base.component";
import {ConfigService} from "../../services/config.service";
import {mockPoll3} from "../../config/mock-poll3";
@Component({
selector: 'framadate-poll-display',
templateUrl: './poll-display.component.html',
styleUrls: ['./poll-display.component.scss']
})
export class PollDisplayComponent extends BaseComponent implements OnInit {
private pollConfigFetched = mockPoll3;
constructor(public config: ConfigService) {
super(config);
}
ngOnInit() {
// fetch poll with its ID or slug.
// store it in the poll property here
}
}

View File

@ -103,7 +103,7 @@
{{"visibility.access_url_key"|translate}}
</label>
<br>
<input type="text"
<input type="name"
class="input-lg"
name="url"
id="url"
@ -133,7 +133,7 @@
min="8"
*ngIf="!showCustomPassword"
[(ngModel)]="config.password">
<input type="text"
<input type="name"
name="password_visible"
id="password_visible"
min="8"

View File

@ -114,11 +114,7 @@ export class ConfigService extends PollConfig {
detail: 'Via MessageService'
});
}, (e) => {
this.messageService.add(
{
severity: 'warning',
summary: "Erreur lors de l'appel "
});
this.handleError(e)
}
)
}

View File

@ -23,6 +23,9 @@
<a [routerLink]="'/step/kind'" i18n>
Page démo
</a>
<a [routerLink]="'/vote/poll/id/3'" i18n>
Sondage dessins animés
</a>
<a [routerLink]="'/graphic/toto'" i18n>
Graphique
</a>

View File

@ -1,7 +1,10 @@
<div class="choicebox"><!-- add .choicebox--active to most voted -->
<div class="choicebox selection-{{choice.answer}}"><!-- add .choicebox--active to most voted -->
<div class="text title ">
{{choice.name}}
</div>
<div class="choicebox__subject">
<!-- TEXT CASE --><!--
<p class="choicebox__txt">
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nulla nobis nam culpa !
@ -13,11 +16,14 @@
--><!-- IMG CASE -->
<!-- DATE CASE -->
<div class="choicebox__date" *ngIf="choice.date">
{{choice.date | date:'EEE'}} <span class="choicebox__day">{{choice.date | date:'dd'}}</span> {{choice.date | date:'LLL'}}
</div>
<div class="choicebox__hour">
08:00
<div class="dates" *ngIf="choice.date">
<div class="choicebox__date">
{{choice.date | date:'EEE'}} <span
class="choicebox__day">{{choice.date | date:'dd'}}</span> {{choice.date | date:'LLL'}}
</div>
<div class="choicebox__hour">
08:00
</div>
</div>
<!-- DATE CASE -->
@ -26,32 +32,46 @@
<div class="choicebox__actions">
<!-- show only the yes check if the config is set to simpleAnswer -->
<!-- add .choicebox__btn--active to selected <button> -->
<button class="choicebox__btn choicebox__btn--yes" type="button">
<img src="../../assets/img/check.svg" (click)="setAnswserTo('yes')" alt="">
<span class="simple-answer">
<button class="choicebox__btn choicebox__btn--yes" type="button"
[ngClass]="{'choicebox__btn--active': choice.answer === 'yes'}"
(click)="setAnswserTo('yes')">
<img src="../../assets/img/check.svg" alt="">
</button>
<button class="choicebox__btn choicebox__btn--maybe" type="button">
<img src="../../assets/img/check-2.svg" (click)="setAnswserTo('maybe')" alt="" *ngIf="!choice.simpleAnswer">
</span>
<span class="complex-answers" *ngIf="!choice.simpleAnswer">
<button class="choicebox__btn choicebox__btn--maybe" type="button"
[ngClass]="{'choicebox__btn--active': choice.answer === 'maybe'}"
(click)="setAnswserTo('maybe')">
<img src="../../assets/img/check-2.svg" alt="">
</button>
<button class="choicebox__btn choicebox__btn--no" type="button">
<img src="../../assets/img/croix.svg" (click)="setAnswserTo('no')" alt="" *ngIf="!choice.simpleAnswer">
<button class="choicebox__btn choicebox__btn--no" type="button"
[ngClass]="{'choicebox__btn--active': choice.answer === 'no'}"
(click)="setAnswserTo('no')"
>
<img src="../../assets/img/croix.svg" alt="">
</button>
</span>
</div>
<div class="choicebox__count">
<button type="button" aria-describedby="choicebox-tooltip" class="choicebox__votes">
<button type="button" aria-describedby="choicebox-tooltip" class="choicebox__votes"
*ngIf="choice.votes.count">
<div class="choicebox__vote">
{{choice.votesCount.yes}}
{{choice.votes.count.yes}}
<img width="20px" height="21px" src="../../assets/img/votant-sur.svg" alt="">
</div>
<div class="choicebox__vote">
{{choice.votesCount.maybe}}
{{choice.votes.count.maybe}}
<img width="22px" height="24px" src="../../assets/img/votant-pas-sur.svg" alt="">
</div>
<div class="choicebox__tooltip" id="choicebox-tooltip">
<div class="choicebox__tooltiplist">
<div class="choicebox__tooltipttl">
<img width="20px" height="21px" src="../../assets/img/votant-sur.svg" alt="">
{{choice.votesCount.yes}} "Oui"
{{choice.votes.count.yes}} "Oui"
</div>
<ul>
<li>Lorem</li>
@ -63,7 +83,7 @@
<div class="choicebox__tooltiplist">
<div class="choicebox__tooltipttl">
<img width="22px" height="24px" src="../../assets/img/votant-pas-sur.svg" alt="">
{{choice.votesCount.maybe}} "Peut-être"
{{choice.votes.count.maybe}} "Peut-être"
</div>
<ul>
<li>Lorem</li>
@ -82,4 +102,4 @@
Choix ayant reçu le plus de votes
</div>
</div>
</div>
</div>

View File

@ -8,19 +8,17 @@
@import "../../assets/scss/variables";
// -- VARIABLES
// ----------------------------
$box-padding : 2rem;
$box-border-width : .6rem;
$btn-size : 5rem;
$btn-margin-x : 1rem;
$btn-margin-y : 1.5rem;
$btn-wrap-size : calc(2 * #{$btn-size} + 4 * #{$btn-margin-x});
$img-maxheight : 12rem;
$breakpoint-responsive : 640px; // à définir
$box-padding: 2rem;
$box-border-width: .6rem;
$btn-size: 5rem;
$btn-margin-x: 1rem;
$btn-margin-y: 1.5rem;
$btn-wrap-size: calc(2 * #{$btn-size} + 4 * #{$btn-margin-x});
$img-maxheight: 12rem;
$breakpoint-responsive: 640px; // à définir
// -- GLOBAL
@ -34,11 +32,17 @@ $breakpoint-responsive : 640px; // à définir
border-left: $box-border-width solid transparent;
background-color: $white;
box-shadow: 0 0 .6rem 0 rgba($black, .2);
&--active {
padding-left: $box-padding;
border-left-color: $primary_color;
}
&.selection-yes {
font-weight: 700;
background: #e9bdeb;
}
@media (min-width: $breakpoint-responsive) {
display: flex;
align-items: center;
@ -58,7 +62,6 @@ $breakpoint-responsive : 640px; // à définir
}
// -- DATE
// ----------------------------
@ -79,7 +82,6 @@ $breakpoint-responsive : 640px; // à définir
}
// -- IMG
// ----------------------------
@ -89,7 +91,6 @@ $breakpoint-responsive : 640px; // à définir
}
// -- TXT
// ----------------------------
@ -99,7 +100,6 @@ $breakpoint-responsive : 640px; // à définir
}
// -- VOTE BTNS
// ----------------------------
@ -129,25 +129,29 @@ $breakpoint-responsive : 640px; // à définir
align-items: center;
justify-content: center;
margin: $btn-margin-y $btn-margin-x;
border: .1rem solid $primary_color;
border: .3rem solid #ccc9c9;
background-color: transparent;
border-radius: 50%;
@media (min-width: $breakpoint-responsive) {
margin-top: 0;
margin-bottom: 0;
}
&:focus,
&:hover,
&:active {
border-color: #ccc9c9;
background-color: #f7f7f7;
}
//&:focus,
//&:hover,
//&:active {
//
// border-color: #ccc9c9;
// background-color: #f7f7f7;
//
// &--active {
// border-color: #bf83c2 !important;
// }
//}
&--maybe {
position: relative;
top: calc( (#{$btn-size} + 2 * #{$btn-margin-y}) / 2 );
top: calc((#{$btn-size} + 2 * #{$btn-margin-y}) / 2);
@media (min-width: $breakpoint-responsive) {
top: auto;
left: auto;
@ -155,13 +159,11 @@ $breakpoint-responsive : 640px; // à définir
}
&--active {
border-width: .3rem;
border-color: #bf83c2;
}
}
// -- VOTE COUNT
// ----------------------------
@ -195,6 +197,7 @@ $breakpoint-responsive : 640px; // à définir
.choicebox__vote {
display: inline-block;
vertical-align: middle;
& + .choicebox__vote {
margin-left: 1.5rem;
}
@ -203,6 +206,7 @@ $breakpoint-responsive : 640px; // à définir
.choicebox__countxt {
display: none;
margin-top: .5rem;
.choicebox--active & {
display: block;
@media (min-width: $breakpoint-responsive) {
@ -212,7 +216,6 @@ $breakpoint-responsive : 640px; // à définir
}
// -- TOOLTIP
// ----------------------------
@ -265,6 +268,7 @@ $breakpoint-responsive : 640px; // à définir
& + .choicebox__tooltiplist {
padding-left: 3rem;
}
ul {
max-height: 11rem;
overflow: auto;

View File

@ -1,21 +1,22 @@
import {Component, Input} from '@angular/core';
import {Component, ElementRef, Input} from '@angular/core';
interface VoteChoice {
votesCount: {
votes?: {
yes: number
no: number
maybe: number
notAnswered: number
};
text?: string;
name?: string;
date?: Date;
answer: 'yes' | 'no' | 'maybe' | null;
simpleAnswer: boolean; // enable if we display only a togglable "yes"
simpleAnswer?: boolean
false; // enable if we display only a togglable "yes"
}
/**
* each vote choice takes a configuration from the container of all choices.
* this component is used to select a date choice, or a text answer
* this component is used to select a date choice, or a name answer
*/
@Component({
selector: 'framadate-vote-choice',
@ -24,20 +25,9 @@ interface VoteChoice {
})
export class VoteChoiceComponent {
@Input() choice: VoteChoice = {
date: new Date(),
text: 'description ',
votesCount: {
yes: 0,
no: 0,
maybe: 0,
notAnswered: 0
},
simpleAnswer: false,
answer: null
};
@Input() private choice: any;
constructor() {
constructor(private el: ElementRef) {
}
@ -53,6 +43,7 @@ export class VoteChoiceComponent {
} else {
this.choice.answer = newAnswer;
}
this.el.nativeElement.blur();
}
}