floating hint about form errors

This commit is contained in:
Tykayn 2021-05-18 10:47:16 +02:00 committed by tykayn
parent f66603d187
commit 7ef4e4d4a4
14 changed files with 180 additions and 198 deletions

View File

@ -1,8 +1,9 @@
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { Poll } from '../../core/models/poll.model'; import { Poll } from '../../core/models/poll.model';
import { DOCUMENT } from '@angular/common';
@Component({ @Component({
selector: 'app-administration', selector: 'app-administration',
@ -13,7 +14,7 @@ export class AdministrationComponent implements OnInit, OnDestroy {
public poll: Poll; public poll: Poll;
private routeSubscription: Subscription; private routeSubscription: Subscription;
constructor(private route: ActivatedRoute) {} constructor(private route: ActivatedRoute, @Inject(DOCUMENT) private document: any) {}
ngOnInit(): void { ngOnInit(): void {
this.routeSubscription = this.route.data.subscribe((data: { poll: Poll }) => { this.routeSubscription = this.route.data.subscribe((data: { poll: Poll }) => {
@ -21,6 +22,15 @@ export class AdministrationComponent implements OnInit, OnDestroy {
if (data.poll) { if (data.poll) {
this.poll = data.poll; this.poll = data.poll;
} }
// focus on first field of the creation form
const firstField = this.document.querySelector('app-admin-form select');
if (firstField) {
console.log('focus on ', firstField);
firstField.focus();
} else {
console.log('no first field of form');
}
}); });
} }

View File

@ -74,7 +74,7 @@
<mat-checkbox class="is-not-flex" formControlName="isProtectedByPassword"> <mat-checkbox class="is-not-flex" formControlName="isProtectedByPassword">
Le sondage sera protégé par un mot de passe Le sondage sera protégé par un mot de passe
</mat-checkbox> </mat-checkbox>
<br />
<input <input
*ngIf="form.value.isProtectedByPassword" *ngIf="form.value.isProtectedByPassword"
#password #password
@ -93,28 +93,32 @@
<mat-checkbox class="is-not-flex" formControlName="isOwnerNotifiedByEmailOnNewVote"> <mat-checkbox class="is-not-flex" formControlName="isOwnerNotifiedByEmailOnNewVote">
Vous recevrez un mail à chaque nouvelle participation Vous recevrez un mail à chaque nouvelle participation
</mat-checkbox> </mat-checkbox>
<br />
<mat-checkbox class="is-not-flex" formControlName="isOwnerNotifiedByEmailOnNewComment"> <mat-checkbox class="is-not-flex" formControlName="isOwnerNotifiedByEmailOnNewComment">
Vous recevrez un mail à chaque nouveau commentaire Vous recevrez un mail à chaque nouveau commentaire
</mat-checkbox> </mat-checkbox>
<h3 class="title is-3"> <div class="proposed-answers">
<i class="fa fa-check-square"></i> <h3 class="title is-3">
Réponses proposées <i class="fa fa-check-square"></i>
</h3> Réponses proposées
</h3>
<img class="image is-24x24 pull-right" src="assets/img/icon_voter_YES.svg" /> <img class="image is-24x24 pull-right" src="assets/img/icon_voter_YES.svg" />
<img class="image is-24x24 pull-right" src="assets/img/icon_voter_MAYBE.svg" /> <img class="image is-24x24 pull-right" src="assets/img/icon_voter_MAYBE.svg" />
<img class="image is-24x24 pull-right" src="assets/img/icon_voter_NO.svg" /> <img class="image is-24x24 pull-right" src="assets/img/icon_voter_NO.svg" />
<mat-checkbox class="is-not-flex" formControlName="isYesAnswerAvailable">
La réponse « oui » sera disponible
</mat-checkbox>
<mat-checkbox class="is-not-flex" formControlName="isMaybeAnswerAvailable">
La réponse « peut-être » sera disponible
</mat-checkbox>
<mat-checkbox class="is-not-flex" formControlName="isNoAnswerAvailable">
La réponse « non » sera disponible
</mat-checkbox>
<mat-checkbox class="is-not-flex" formControlName="isYesAnswerAvailable">
La réponse « oui » sera disponible
</mat-checkbox>
<br />
<mat-checkbox class="is-not-flex" formControlName="isMaybeAnswerAvailable">
La réponse « peut-être » sera disponible
</mat-checkbox>
<br />
<mat-checkbox class="is-not-flex" formControlName="isNoAnswerAvailable">
La réponse « non » sera disponible
</mat-checkbox>
</div>
<h3 class="title is-3"> <h3 class="title is-3">
<i class="fa fa-user-secret"></i> <i class="fa fa-user-secret"></i>
Restrictions visiteurs Restrictions visiteurs
@ -122,6 +126,7 @@
<mat-checkbox class="is-not-flex" formControlName="allowComments"> <mat-checkbox class="is-not-flex" formControlName="allowComments">
Autoriser les commentaires Autoriser les commentaires
</mat-checkbox> </mat-checkbox>
<br />
<mat-checkbox class="is-not-flex" formControlName="hasMaxCountOfAnswers"> <mat-checkbox class="is-not-flex" formControlName="hasMaxCountOfAnswers">
Nombre de réponses limitées à ce nombre Nombre de réponses limitées à ce nombre
</mat-checkbox> </mat-checkbox>
@ -145,7 +150,7 @@
<app-wip-todo></app-wip-todo> <app-wip-todo></app-wip-todo>
<div> <div>
<mat-checkbox class="is-not-flex" formControlName="isProtectedByPassword"> <mat-checkbox class="is-not-flex" formControlName="useVoterUniqueLink">
Spécifier un lien unique de vote à des participants définis Spécifier un lien unique de vote à des participants définis
</mat-checkbox> </mat-checkbox>
<p> <p>
@ -153,6 +158,13 @@
avec une clé permettant de voter pour n'importe qui ayant le lien. Nécessite: évolution de la BDD et de avec une clé permettant de voter pour n'importe qui ayant le lien. Nécessite: évolution de la BDD et de
l'API l'API
</p> </p>
<textarea
name="voterEmailList"
id="voterEmailList"
cols="30"
rows="10"
formControlName="voterEmailList"
></textarea>
</div> </div>
<mat-checkbox class="is-not-flex" formControlName="allowNewDateTime"> <mat-checkbox class="is-not-flex" formControlName="allowNewDateTime">

View File

@ -25,9 +25,9 @@
> >
<i class="fa fa-close"></i> <i class="fa fa-close"></i>
</button> </button>
<mat-error class="hint deletable-field-hint" *ngIf="form.controls.title.invalid">{{ <mat-error class="hint deletable-field-hint" *ngIf="form.controls.title.invalid">
getErrorMessage(form.controls.title) {{ getErrorMessage(form.controls.title) }}</mat-error
}}</mat-error> >
<br /> <br />
<label for="creatorEmail"> <label for="creatorEmail">

View File

@ -61,22 +61,13 @@
</main> </main>
<app-picker [form]="form" *ngIf="displayDatePicker"></app-picker> <app-picker [form]="form" *ngIf="displayDatePicker"></app-picker>
<app-errors-list [form]="form"></app-errors-list> <app-errors-list [form]="form"></app-errors-list>
<p>
<i>
{{ 'choices.helper' | translate }}
</i>
</p>
{{ 'choices.answer_preset_1' | translate }}
{{ 'choices.add' | translate }}
{{ 'choices.continue' | translate }}
<button <button
[disabled]="!form.valid && form.touched" [disabled]="!form.valid && form.touched"
class="btn is-success is-fixed-bottom" class="btn is-success is-fixed-bottom"
(click)="createPoll()" (click)="createPoll()"
> >
<i class="fa fa-save"></i> <i class="fa fa-save"></i>
Enregistrer le sondage (vérifier la validité) Enregistrer le sondage
</button> </button>
<button class="btn is-success is-fixed-bottom" (click)="createPoll()" *ngIf="!environment.production"> <button class="btn is-success is-fixed-bottom" (click)="createPoll()" *ngIf="!environment.production">
<i class="fa fa-save"></i> <i class="fa fa-save"></i>
@ -97,15 +88,6 @@
> >
</footer> </footer>
<hr /> <hr />
<div class="validation">
<div class="has-background-danger" *ngIf="!form.valid && form.touched">
le formulaire est invalide
<pre>
{{ form.errors | json }}
</pre
>
</div>
</div>
</form> </form>
</div> </div>
</div> </div>

View File

@ -22,7 +22,7 @@ export class FormComponent implements OnInit {
public form: FormGroup; public form: FormGroup;
public displayDatePicker = false; public displayDatePicker = false;
public advancedDisplayEnabled = false; public advancedDisplayEnabled = true;
public show_debug_data = false; public show_debug_data = false;
public currentStep = 'base'; public currentStep = 'base';
public steps = ['base', 'choices', 'advanced']; public steps = ['base', 'choices', 'advanced'];
@ -50,35 +50,13 @@ export class FormComponent implements OnInit {
initFormDefault(showDemoValues = environment.autofill): void { initFormDefault(showDemoValues = environment.autofill): void {
const creationDate = new Date(); const creationDate = new Date();
// choices of date are managed outside of this form
this.form = this.fb.group({ this.form = this.fb.group({
title: ['', [Validators.required, Validators.minLength(5)]], title: ['', [Validators.required, Validators.minLength(5)]],
creatorPseudo: ['', [Validators.required]], creatorPseudo: ['', [Validators.required]],
creatorEmail: ['', [Validators.required]], creatorEmail: ['', [Validators.required]],
custom_url: [this.pollUtilitiesService.makeUuid(), [Validators.required]], custom_url: [this.pollUtilitiesService.makeUuid(), [Validators.required]],
description: ['', [Validators.required]], description: ['', [Validators.required]],
// choices: this.fb.array([
// this.fb.group({
// label: ['', [Validators.required]],
// imageUrl: ['', [Validators.required]],
// }),
// ]),
// dateChoices: this.fb.array([
// this.fb.group({
// label: ['', [Validators.required]],
// // if we have enabled detailed time choices per date choice, we have to make a time property for each date choice
// timeChoices: this.fb.array([
// this.fb.group({
// label: ['', [Validators.required]],
// }),
// ]),
// }),
// ]),
// // these time choice are meant to be the same for each day
// timeChoices: this.fb.array([
// this.fb.group({
// label: ['', [Validators.required]],
// }),
// ]),
kind: ['', [Validators.required]], kind: ['', [Validators.required]],
areResultsPublic: [true, [Validators.required]], areResultsPublic: [true, [Validators.required]],
whoCanChangeAnswers: ['everybody', [Validators.required]], whoCanChangeAnswers: ['everybody', [Validators.required]],
@ -91,10 +69,12 @@ export class FormComponent implements OnInit {
isNoAnswerAvailable: [false, [Validators.required]], isNoAnswerAvailable: [false, [Validators.required]],
isAboutDate: [true, [Validators.required]], isAboutDate: [true, [Validators.required]],
isZeroKnoledge: [false, [Validators.required]], isZeroKnoledge: [false, [Validators.required]],
useVoterUniqueLink: [false, [Validators.required]],
expiresDaysDelay: [60, [Validators.required, Validators.min(1)]], expiresDaysDelay: [60, [Validators.required, Validators.min(1)]],
maxCountOfAnswers: [150, [Validators.required, Validators.min(1)]], maxCountOfAnswers: [150, [Validators.required, Validators.min(1)]],
allowComments: [true, [Validators.required]], allowComments: [true, [Validators.required]],
password: ['', []], password: ['', []],
voterEmailList: ['', []],
dateCreated: [creationDate, [Validators.required]], dateCreated: [creationDate, [Validators.required]],
hasSeveralHours: [false, [Validators.required]], hasSeveralHours: [false, [Validators.required]],
hasMaxCountOfAnswers: [true, [Validators.required, Validators.min(1)]], hasMaxCountOfAnswers: [true, [Validators.required, Validators.min(1)]],

View File

@ -52,10 +52,6 @@ export class ConsultationComponent implements OnInit, OnDestroy {
if (newpoll) { if (newpoll) {
this.isArchived = new Date(newpoll.expiracy_date) < new Date(); this.isArchived = new Date(newpoll.expiracy_date) < new Date();
this.poll.is_archived = this.isArchived; this.poll.is_archived = this.isArchived;
if (!environment.production) {
// this.addVoteStack();
}
} }
}); });

View File

@ -9,6 +9,10 @@
<strong class="marged has-background-warning total-errors" *ngIf="form && totalErrors"> <strong class="marged has-background-warning total-errors" *ngIf="form && totalErrors">
{{ totalErrors }} erreurs de validation {{ totalErrors }} erreurs de validation
</strong> </strong>
<strong class="marged has-background-warning total-errors fixed-hint" *ngIf="form && totalErrors">
{{ totalErrors }}
<i class="fa fa-warning"></i>
</strong>
<ul> <ul>
<li *ngFor="let m of messages"> <li *ngFor="let m of messages">

View File

@ -0,0 +1,9 @@
.fixed-hint {
position: fixed;
left: 9em;
bottom: 3em;
padding: 1em;
border-radius: 1em;
color: white;
border: solid 2px white;
}

View File

@ -19,7 +19,7 @@ export class ErrorsListComponent implements OnInit {
} }
getFormValidationErrors(): void { getFormValidationErrors(): void {
// console.log('%c ==>> Validation Errors: ', 'color: red; font-weight: bold; font-size:25px;'); console.log('%c ==>> Validation Errors: ', 'color: red; font-weight: bold; font-size:22px;');
let totalErrors = 0; let totalErrors = 0;
this.messages = []; this.messages = [];

View File

@ -141,6 +141,10 @@
"selectors": { "selectors": {
"lang": "Select the lang" "lang": "Select the lang"
}, },
"validation" : {
"You must enter a value": "You must enter a EEEE"
},
"You must enter a value": "You must enter a valueeeeeeee",
"SENTENCES": { "SENTENCES": {
"create-a-poll": "Create a poll", "create-a-poll": "Create a poll",
"define-dates-or-subjects-to-choose-from": "Define dates or subjects to choose from", "define-dates-or-subjects-to-choose-from": "Define dates or subjects to choose from",

View File

@ -144,6 +144,10 @@
"selectors": { "selectors": {
"lang": "Sélectionner la langue" "lang": "Sélectionner la langue"
}, },
"validation" : {
"You must enter a value": "You must enter a EEEE"
},
"You must enter a value": "You must enter a valueeeeeeee",
"SENTENCES": { "SENTENCES": {
"create-a-poll": "Créez un sondage", "create-a-poll": "Créez un sondage",
"define-dates-or-subjects-to-choose-from": "Déterminez les dates ou les sujets à choisir", "define-dates-or-subjects-to-choose-from": "Déterminez les dates ou les sujets à choisir",

View File

@ -1,91 +0,0 @@
input,
select,
textarea {
@extend .clickable;
margin-bottom: 0.25rem;
border-bottom: 2px solid $primary_color;
padding: 0.5rem;
&:active,
&:focus,
&:hover {
color: $primary_color;
}
&.ng-invalid {
border: $warning 3px solid !important;
}
}
input[required] {
&:after {
content: '*';
color: red;
}
}
select,
option {
-webkit-appearance: none;
-moz-appearance: none;
border-radius: 0;
background-color: transparent;
//background-image: url('/assets/img/fleche_bas.svg');
padding-right: 2.5rem;
background-repeat: no-repeat;
background-size: 9px 8px;
background-position: right 1rem center;
background-clip: border-box;
min-width: 10rem;
margin-bottom: 0.25rem;
border-bottom: 2px solid $primary_color !important;
}
select {
@extend .select, .input;
}
#multi_hours select {
min-width: 300px !important;
}
input {
@extend .input, .text-ellipsis;
}
label {
margin-top: 0.5rem;
}
textarea {
width: 100%;
max-width: 100%;
min-height: 213px;
padding: 0.5em 1em;
margin-bottom: 1em;
border-left: 3px solid $primary_color;
}
.comment {
border-left: 6px solid $primary_color;
margin-top: 25px;
margin-bottom: 25px;
flex-wrap: wrap;
padding-left: 17px;
}
.cname {
font-weight: bold;
}
.deletable-field-hint {
margin-right: 2.8em;
margin-top: -0.7em;
-moz-border-radius-bottomright: 1em;
}
.mat-error {
display: block;
background: $warning;
padding: 1em;
}

View File

@ -1,34 +1,9 @@
@charset "UTF-8"; @charset "UTF-8";
label { label {
margin-top: 1em; margin-top: 0.25em;
line-height: 2.5em; line-height: 2.5em;
} }
.ui-inputswitch {
margin-left: 1ch;
margin-right: 1ch;
}
.input-lg {
display: block;
width: 100%;
}
input {
margin-bottom: 1em;
+ button,
+ .mat-icon-button {
margin-bottom: 1em;
}
&[type='date']::after {
content: 'au format JJ/MM/AAAA';
display: inline-block;
position: relative;
}
}
input, input,
select, select,
textarea { textarea {
@ -36,26 +11,50 @@ textarea {
padding: 1rem; padding: 1rem;
border: 1px solid $secondary_color; border: 1px solid $secondary_color;
border-bottom: 3px solid $primary_color; border-bottom: 3px solid $primary_color;
width: calc(100% - 45px) !important; width: calc(100% -45px);
max-width: 40em !important; margin-right: 2px;
margin-bottom: 1em;
+ button,
+ .mat-icon-button {
margin-bottom: 1em;
}
} }
.next { input,
max-width: 200px; select,
textarea {
@extend .clickable;
margin-bottom: 0.25rem;
border-bottom: 2px solid $primary_color;
padding: 0.5rem;
&:active,
&:focus,
&:hover {
color: $primary_color;
}
&.ng-invalid {
border: $warning 3px solid !important;
}
&[required] {
&:after {
content: '*';
color: red;
}
}
} }
li { li {
list-style-type: none; list-style-type: none;
} }
.next { .next {
max-width: 200px;
margin-bottom: 50px; margin-bottom: 50px;
} }
.nobold {
font-weight: normal;
}
.btn-block { .btn-block {
display: block; display: block;
width: 100%; width: 100%;
@ -72,3 +71,72 @@ button[type='submit'] {
background: $warning; background: $warning;
color: $white; color: $white;
} }
select,
option {
-webkit-appearance: none;
-moz-appearance: none;
border-radius: 0;
background-color: transparent;
background-image: url('assets/img/fleche_bas.svg') !important;
padding-right: 2.5rem;
background-repeat: no-repeat;
background-size: 9px 8px;
background-position: right 1rem center;
background-clip: border-box;
min-width: 10rem;
margin-bottom: 0.25rem;
border-bottom: 2px solid $primary_color !important;
}
select {
@extend .select, .input;
}
#multi_hours select {
min-width: 300px !important;
}
input {
@extend .input, .text-ellipsis;
}
label {
margin-top: 0.5rem;
}
textarea {
width: 100%;
max-width: 100%;
min-height: 213px;
padding: 0.5em 1em;
margin-bottom: 1em;
border-left: 3px solid $primary_color;
}
.comment {
border-left: 6px solid $primary_color;
margin-top: 25px;
margin-bottom: 25px;
flex-wrap: wrap;
padding-left: 17px;
}
.cname {
font-weight: bold;
}
.deletable-field-hint {
margin-right: 2.8em;
margin-top: -0.7em;
-moz-border-radius-bottomright: 1em;
}
.mat-error {
display: block;
background: $warning;
padding: 1em;
}
mat-checkbox {
display: block;
}

View File

@ -27,3 +27,7 @@ h3 {
h4 { h4 {
@extend .is-4; @extend .is-4;
} }
.nobold {
font-weight: normal;
}