diff --git a/angular.json b/angular.json index c86c8402..4848a4f1 100644 --- a/angular.json +++ b/angular.json @@ -22,10 +22,16 @@ "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", - "assets": ["src/favicon.ico", "src/assets"], + "assets": [ + "src/favicon.ico", + "src/assets" + ], "styles": [ "node_modules/fork-awesome/css/fork-awesome.min.css", "node_modules/bulma-switch/dist/css/bulma-switch.min.css", + "node_modules/primeicons/primeicons.css", + "node_modules/primeng/resources/themes/saga-blue/theme.css", + "node_modules/primeng/resources/primeng.min.css", "src/styles.scss" ], "scripts": [ @@ -93,8 +99,14 @@ "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": ["tsconfig.app.json", "tsconfig.spec.json", "e2e/tsconfig.json"], - "exclude": ["**/node_modules/**"] + "tsConfig": [ + "tsconfig.app.json", + "tsconfig.spec.json", + "e2e/tsconfig.json" + ], + "exclude": [ + "**/node_modules/**" + ] } }, "e2e": { diff --git a/mocks/old-stuff/config/defaultConfigs.ts b/mocks/old-stuff/config/defaultConfigs.ts index 80737489..7e18eae6 100644 --- a/mocks/old-stuff/config/defaultConfigs.ts +++ b/mocks/old-stuff/config/defaultConfigs.ts @@ -1,5 +1,5 @@ export interface DateChoice { - literal: string; + literal: String; timeSlices: TimeSlices[]; date_object: Date; } diff --git a/package.json b/package.json index d5c23125..33e64a2c 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "ngx-markdown": "^9.0.0", "ngx-webstorage": "^5.0.0", "node-forge": "^0.10.0", + "primeicons": "^5.0.0", "primeng": "^11.0.0", "quill": "^1.3.7", "rxjs": "^6.5.5", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index dde91e65..834fb389 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -101,7 +101,7 @@ export class AppComponent implements OnInit, OnDestroy, AfterViewInit { }); // debug cors - this.apiService.getAllAvailablePolls(); + // this.apiService.getAllAvailablePolls(); } ngAfterViewInit(): void { diff --git a/src/app/core/models/dateChoice.model.ts b/src/app/core/models/dateChoice.model.ts index 32ba99f9..8db7924c 100644 --- a/src/app/core/models/dateChoice.model.ts +++ b/src/app/core/models/dateChoice.model.ts @@ -2,6 +2,7 @@ export interface DateChoice { literal: string; timeSlices: TimeSlices[]; date_object: Date; + date_input: string; } export interface TimeSlices { diff --git a/src/app/core/services/date.utilities.service.ts b/src/app/core/services/date.utilities.service.ts index e6519eb2..35866766 100644 --- a/src/app/core/services/date.utilities.service.ts +++ b/src/app/core/services/date.utilities.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; -import { DateChoice, defaultTimeOfDay } from '../../../../mocks/old-stuff/config/defaultConfigs'; +import { defaultTimeOfDay } from '../../../../mocks/old-stuff/config/defaultConfigs'; +import { DateChoice } from '../models/dateChoice.model'; @Injectable({ providedIn: 'root', @@ -91,37 +92,43 @@ export class DateUtilitiesService { return 0; } + makeDefaultCalendarDateChoices(): Date[] { + return [ + this.addDaysToDate(1, new Date()), + this.addDaysToDate(2, new Date()), + this.addDaysToDate(3, new Date()), + ]; + } /** * fill default dates for today + the next 3 dateChoices */ makeDefaultDateChoices(): DateChoice[] { const today = new Date(); - const ladate = this.addDaysToDate(0, today); const ladate2 = this.addDaysToDate(1, today); const ladate3 = this.addDaysToDate(2, today); const ladate4 = this.addDaysToDate(3, today); return [ - { - literal: this.formateDateToInputStringNg(ladate), - timeSlices: Object.create(defaultTimeOfDay), - date_object: ladate, - }, - { - literal: this.formateDateToInputStringNg(ladate2), - timeSlices: Object.create(defaultTimeOfDay), - date_object: ladate2, - }, - { - literal: this.formateDateToInputStringNg(ladate3), - timeSlices: Object.create(defaultTimeOfDay), - date_object: ladate3, - }, - { - literal: this.formateDateToInputStringNg(ladate4), - timeSlices: Object.create(defaultTimeOfDay), - date_object: ladate4, - }, + this.convertDateToDateChoiceObject(ladate2), + this.convertDateToDateChoiceObject(ladate3), + this.convertDateToDateChoiceObject(ladate4), ]; } + + /** + * convert a date to a DateChoice + * @param date + */ + convertDateToDateChoiceObject(date: Date): DateChoice { + let isUnder10 = date.getDate() < 10; + let day = isUnder10 ? `0${date.getDate()}` : date.getDate(); + // get month is based on 0, so yeah + let input = `${date.getFullYear()}-${date.getMonth() + 1}-${day}`; + return { + literal: this.formateDateToInputStringNg(date), + timeSlices: Object.create(defaultTimeOfDay), + date_object: date, + date_input: input, + }; + } } diff --git a/src/app/core/services/poll.service.ts b/src/app/core/services/poll.service.ts index a1e7d8b0..68449c75 100644 --- a/src/app/core/services/poll.service.ts +++ b/src/app/core/services/poll.service.ts @@ -33,7 +33,7 @@ export class PollService implements Resolve { public endDateInterval: string; public intervalDays: number = 1; public intervalDaysDefault = 7; - public dateList: DateChoice[] = []; // sets of days as strings, config to set identical time for days in a special days poll + public dateChoiceList: DateChoice[] = []; // sets of days as strings, config to set identical time for days in a special days poll public timeList: TimeSlices[] = []; // ranges of time expressed as strings public previousRouteName: string = '/administration'; public nextRouteName: string = '/administration/step/2'; @@ -67,11 +67,9 @@ export class PollService implements Resolve { this.createFormGroup(); // fill in the next 3 days of the calendar date picker - this.calendar = [ - this.DateUtilitiesService.addDaysToDate(1, new Date()), - this.DateUtilitiesService.addDaysToDate(2, new Date()), - this.DateUtilitiesService.addDaysToDate(3, new Date()), - ]; + this.calendar = this.DateUtilitiesService.makeDefaultCalendarDateChoices(); + this.dateChoiceList = this.DateUtilitiesService.makeDefaultDateChoices(); + // disable days before today for (let i = 1; i < 31; i++) { this.disabled_dates.push(this.DateUtilitiesService.addDaysToDate(-i, new Date())); @@ -142,6 +140,7 @@ export class PollService implements Resolve { hasMaxCountOfAnswers: [300, [Validators.required]], useVoterUniqueLink: [false, [Validators.required]], voterEmailList: ['', []], + hasSeveralHours: [false, []], allowNewDateTime: [true, [Validators.required]], }); this.form = form; @@ -306,12 +305,12 @@ export class PollService implements Resolve { timeList: [], }); }); - this.dateList = [...new Set(converted)]; + this.dateChoiceList = [...new Set(converted)]; // add only dates that are not already present with a Set of unique items - console.log('this.dateList', this.dateList); + console.log('this.dateChoiceList', this.dateChoiceList); this.showDateInterval = false; - this.form.patchValue({ choices: this.dateList }); + this.form.patchValue({ choices: this.dateChoiceList }); this.toastService.display(`les dates ont été ajoutées aux réponses possibles.`); } @@ -662,8 +661,39 @@ export class PollService implements Resolve { }); } - convertCalendarDatesToChoices(array_dates) { - return array_dates; + /** + * convertir les dates de la propriété Calendar en objets de saisie de texte + */ + convertCalendarToText() { + let converted = []; + for (let someDate of this.calendar) { + converted.push(this.DateUtilitiesService.convertDateToDateChoiceObject(someDate)); + } + this.dateChoiceList = converted.sort((first: any, second: any) => { + return first.date_object - second.date_object; + }); + + return converted; + } + + /** + * convert the DateChoices to an arrray of Dates for calendar picker + */ + convertTextToCalendar() { + console.log('convert text to calendar', this.dateChoiceList); + let converted = []; + for (let someDateChoice of this.dateChoiceList) { + let dateObj = new Date(someDateChoice.date_input); + console.log('dateObj', dateObj); + // check that date is not part of the disabled dates + if (this.disabled_dates.indexOf(dateObj) === -1) { + converted.push(dateObj); + } + } + + console.log('converted', converted); + this.calendar = converted; + return; } patchFormWithPoll(poll: Poll) { @@ -693,7 +723,7 @@ export class PollService implements Resolve { const field = form.value[pk]; newpoll[pk] = field; } else { - // console.log('manque pollKey', pk); + console.log('newPollFromForm : manque pollKey', pk); } } @@ -720,11 +750,7 @@ export class PollService implements Resolve { for (let elem of this.calendar) { console.log('elem', elem); - let converted_day = { - literal: this.DateUtilitiesService.formateDateToInputStringNg(elem), - timeSlices: [], - date_object: elem, - }; + let converted_day = this.DateUtilitiesService.convertDateToDateChoiceObject(elem); newpoll.dateChoices.push(converted_day); } console.log('newpoll.dateChoices', newpoll.dateChoices); diff --git a/src/app/features/administration/administration.module.ts b/src/app/features/administration/administration.module.ts index 70e204ed..c310d712 100644 --- a/src/app/features/administration/administration.module.ts +++ b/src/app/features/administration/administration.module.ts @@ -28,6 +28,7 @@ import { DayListComponent } from './form/date/list/day/day-list.component'; import { PickerComponent } from './form/date/picker/picker.component'; import { TimeListComponent } from './form/date/list/time/time-list.component'; import { AdminConsultationComponent } from './consultation/consultation.component'; +import { ConfirmDialogModule } from 'primeng/confirmdialog'; @NgModule({ declarations: [ @@ -62,6 +63,7 @@ import { AdminConsultationComponent } from './consultation/consultation.componen FormsModule, TranslateModule.forChild({ extend: true }), DragDropModule, + ConfirmDialogModule, ], }) export class AdministrationModule {} diff --git a/src/app/features/administration/form/date-select/date-select.component.html b/src/app/features/administration/form/date-select/date-select.component.html index 320263c8..00b176a2 100644 --- a/src/app/features/administration/form/date-select/date-select.component.html +++ b/src/app/features/administration/form/date-select/date-select.component.html @@ -98,11 +98,7 @@

- + diff --git a/src/app/features/administration/form/date/list/day/day-list.component.html b/src/app/features/administration/form/date/list/day/day-list.component.html index 0907642e..ad4a1229 100644 --- a/src/app/features/administration/form/date/list/day/day-list.component.html +++ b/src/app/features/administration/form/date/list/day/day-list.component.html @@ -6,14 +6,6 @@ {{ 'dates.add' | translate }} -
- - {{ dateChoices.length }} - - - {{ 'dates.count_dates' | translate }} - -
diff --git a/src/app/features/administration/form/date/list/day/day-list.component.ts b/src/app/features/administration/form/date/list/day/day-list.component.ts index ad569ac6..9f0aa02b 100644 --- a/src/app/features/administration/form/date/list/day/day-list.component.ts +++ b/src/app/features/administration/form/date/list/day/day-list.component.ts @@ -7,7 +7,8 @@ import { StorageService } from '../../../../../../core/services/storage.service' import { MatDialog } from '@angular/material/dialog'; import { ShortcutsHelpComponent } from '../../../../../shared/components/ui/shortcuts-help/shortcuts-help.component'; import { DateChoice } from '../../../../../../core/models/dateChoice.model'; - +import { PollService } from '../../../../../../core/services/poll.service'; +import { DateUtilitiesService } from '../../../../../../core/services/date.utilities.service'; @Component({ selector: 'app-day-list', templateUrl: './day-list.component.html', @@ -17,8 +18,7 @@ import { DateChoice } from '../../../../../../core/models/dateChoice.model'; export class DayListComponent { @Input() form: FormGroup; - @Input() - public dateChoices: Array = []; + public dateChoices: DateChoice[] = []; @Input() public hasSeveralHours: boolean; timeList: any; @@ -27,11 +27,13 @@ export class DayListComponent { constructor( public dialog: MatDialog, private toastService: ToastService, + private pollService: PollService, + private dateUtilitiesService: DateUtilitiesService, private cd: ChangeDetectorRef, @Inject(DOCUMENT) private document: any, private storageService: StorageService ) { - // this.setDemoTextChoices(); + this.dateChoices = this.pollService.dateChoiceList; } reinitChoices(): void { @@ -127,12 +129,24 @@ export class DayListComponent { } addChoice(optionalLabel = ''): void { - this.storageService.dateChoices.push({ - literal: '', - timeSlices: [], - date_object: new Date(), - }); + let lastDateChoice = this.pollService.dateChoiceList[this.pollService.dateChoiceList.length]; + console.log('lastDateChoice', lastDateChoice); + let lastDateChoiceObject = this.dateUtilitiesService.addDaysToDate( + this.pollService.dateChoiceList.length, + new Date() + ); + if (lastDateChoice && lastDateChoice.date_object) { + lastDateChoiceObject = lastDateChoice.date_object; + } + this.pollService.dateChoiceList.push( + this.dateUtilitiesService.convertDateToDateChoiceObject( + this.dateUtilitiesService.addDaysToDate(1, lastDateChoiceObject) + ) + ); + this.pollService.dateChoiceList.sort((a: any, b: any) => { + return a.date_object - b.date_object; + }); this.focusOnChoice(this.storageService.dateChoices.length - 1); } @@ -150,10 +164,6 @@ export class DayListComponent { } } - openKeyboardShortcutsModal() { - this.display = true; - } - isWeekendDay(date_object: Date) { if (date_object) { const day = date_object.getDay(); diff --git a/src/app/features/administration/form/steps/step-one/step-one.component.html b/src/app/features/administration/form/steps/step-one/step-one.component.html index f133ba0b..3a227930 100644 --- a/src/app/features/administration/form/steps/step-one/step-one.component.html +++ b/src/app/features/administration/form/steps/step-one/step-one.component.html @@ -58,10 +58,31 @@ + + +

{{ 'creation.dialog' | translate }}

+
+ + + + +
-
diff --git a/src/app/features/administration/form/steps/step-one/step-one.component.ts b/src/app/features/administration/form/steps/step-one/step-one.component.ts index 24414959..d39ec912 100644 --- a/src/app/features/administration/form/steps/step-one/step-one.component.ts +++ b/src/app/features/administration/form/steps/step-one/step-one.component.ts @@ -2,6 +2,8 @@ import { Component, Inject, Input, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { PollService } from '../../../../../core/services/poll.service'; import { DOCUMENT } from '@angular/common'; +import { ConfirmationService } from 'primeng/api'; +import { Router } from '@angular/router'; @Component({ selector: 'app-step-one', @@ -9,13 +11,18 @@ import { DOCUMENT } from '@angular/common'; styleUrls: ['./step-one.component.scss'], }) export class StepOneComponent implements OnInit { - constructor(public pollService: PollService, @Inject(DOCUMENT) private document: any) {} - @Input() step_max: any; @Input() form: FormGroup; + constructor( + public pollService: PollService, + @Inject(DOCUMENT) private document: any, + private router: Router, + private confirmationService: ConfirmationService + ) {} + ngOnInit(): void { this.pollService.step_current = 1; const selector = '#title'; @@ -24,4 +31,13 @@ export class StepOneComponent implements OnInit { firstField.focus(); } } + + cancelCreationDialog() { + this.confirmationService.confirm({ + message: 'Quitter la création de sondage?', + accept: () => { + this.router.navigate(['/']); + }, + }); + } } diff --git a/src/app/features/administration/form/steps/step-three/step-three.component.html b/src/app/features/administration/form/steps/step-three/step-three.component.html index fa8f3aec..769e06d4 100644 --- a/src/app/features/administration/form/steps/step-three/step-three.component.html +++ b/src/app/features/administration/form/steps/step-three/step-three.component.html @@ -2,11 +2,6 @@ - - {{ pollService.calendar.length }} - - - {{ 'dates.count_dates' | translate }} -
-
@@ -38,8 +42,9 @@ {{ 'dates.add_time' | translate }} + + + + + diff --git a/src/app/features/administration/stepper/stepper.component.scss b/src/app/features/administration/stepper/stepper.component.scss index 0ced13f4..b7ca6e9c 100644 --- a/src/app/features/administration/stepper/stepper.component.scss +++ b/src/app/features/administration/stepper/stepper.component.scss @@ -29,6 +29,16 @@ &.is-active { background: $font_color; } + &:hover { + background: $clicked-color; + } + &.cancel-button { + background: $border-color; + margin-top: -0.5em; + &:hover { + background: $font_color; + } + } } .poll-title { color: $d-neutral; diff --git a/src/app/features/administration/stepper/stepper.component.ts b/src/app/features/administration/stepper/stepper.component.ts index 9f4fc3e7..646f0f9e 100644 --- a/src/app/features/administration/stepper/stepper.component.ts +++ b/src/app/features/administration/stepper/stepper.component.ts @@ -1,6 +1,8 @@ import { Component, Input, OnInit } from '@angular/core'; import { PollService } from '../../../core/services/poll.service'; import { environment } from '../../../../environments/environment'; +import { ConfirmationService } from 'primeng/api'; +import { Router } from '@angular/router'; @Component({ selector: 'app-stepper', @@ -13,5 +15,18 @@ export class StepperComponent { @Input() public step_max: number = 5; public show_shortcuts = environment.showStepperShortcuts; - constructor(public pollService: PollService) {} + constructor( + public pollService: PollService, + private confirmationService: ConfirmationService, + private router: Router + ) {} + + cancelDialog() { + this.confirmationService.confirm({ + message: 'Quitter la création de sondage?', + accept: () => { + this.router.navigate(['/']); + }, + }); + } } diff --git a/src/app/features/administration/success/success.component.scss b/src/app/features/administration/success/success.component.scss index f354ac03..be5620d6 100644 --- a/src/app/features/administration/success/success.component.scss +++ b/src/app/features/administration/success/success.component.scss @@ -11,6 +11,7 @@ a { max-width: 20em; @extend .truncate; } +.admin-ok pre, .truncate { white-space: nowrap; overflow: hidden; diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 940a564d..b9e6e287 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -28,6 +28,8 @@ import { ErasableInputComponent } from './components/ui/erasable-input/erasable- import { WipTodoComponent } from './components/ui/wip-todo/wip-todo.component'; import { ErrorsListComponent } from '../features/shared/components/ui/form/errors-list/errors-list.component'; import { ShortcutsHelpComponent } from '../features/shared/components/ui/shortcuts-help/shortcuts-help.component'; +import { ConfirmDialogModule } from 'primeng/confirmdialog'; +import { ConfirmationService } from 'primeng/api'; const COMPONENTS = [ ChoiceDetailsComponent, @@ -63,7 +65,8 @@ const MATERIAL_MODULES = [ @NgModule({ declarations: COMPONENTS, - imports: [...ANGULAR_MODULES, ...MATERIAL_MODULES], + imports: [...ANGULAR_MODULES, ...MATERIAL_MODULES, ConfirmDialogModule], exports: [...ANGULAR_MODULES, ...MATERIAL_MODULES, ...COMPONENTS], + providers: [ConfirmationService], }) export class SharedModule {} diff --git a/src/assets/i18n/FR.json b/src/assets/i18n/FR.json index 95cb5e88..b2e61b61 100644 --- a/src/assets/i18n/FR.json +++ b/src/assets/i18n/FR.json @@ -29,6 +29,7 @@ "creation": { "title": "Créer un sondage", "want": "Choisissez le type de sondage", + "dialog": "Quitter la création de sondage?", "advanced": "Options avancées", "kind": { "classic": "Propositions", @@ -143,6 +144,10 @@ "choiceNotColorblind": "Je ne suis pas", "colorblindText": "daltonien." }, + "dialogs" : { + "no": "non", + "yes": "oui" + }, "selectors": { "lang": "Sélectionner la langue" }, diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 8b3a6a3c..84964537 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -12,16 +12,14 @@ export const environment = { production: false, display_routes: true, // demo paths to test polls autofill_creation: true, - advanced_options_display: true, - autofill_participation: true, - // autofill: false, + advanced_options_display: false, + autofill_participation: false, showDemoWarning: false, - // autoSendNewPoll: true, autoSendNewPoll: false, showStepperShortcuts: true, interval_days_default: 7, expiresDaysDelay: 60, - maxCountOfAnswers: 150, + maxCountOfAnswers: 300, appTitle: 'Framadate', appVersion: '0.6.0', appLogo: 'assets/img/logo.png', diff --git a/src/styles/partials/_forms.scss b/src/styles/partials/_forms.scss index 0f25d68f..4f67a6da 100644 --- a/src/styles/partials/_forms.scss +++ b/src/styles/partials/_forms.scss @@ -190,6 +190,7 @@ mat-checkbox { background: white; border-radius: 4px; overflow: hidden; + padding: 1em 2em; } .example-box { @@ -270,49 +271,49 @@ mat-checkbox { padding: 0.5em; margin: 1em auto; + p-button, button { - border: solid 1px $primary_color; + border: solid 1px $secondary_color !important; + color: $secondary_color !important; } .p-datepicker-buttonbar { margin-top: 0.5em; } - .pi-chevron-left:after { - content: '<'; - } - - .pi-chevron-right:after { - content: '>'; - } - .p-datepicker-month { margin-right: 1em; } .p-datepicker-weeknumber span { - border-right: 1px solid $legend_color; + border-right: 1px solid $secondary_color; } - .p-datepicker-today span { + .p-datepicker-today td span { font-weight: bold; - border: solid 1px $legend_color; + border: solid 3px $secondary_color; + background: $white; } .p-datepicker-calendar td span { - padding: 1em; + padding: 1.5em 0.5em; width: 3.5em; transition: all ease 0.5s; + background: $white; + border: solid 1px $secondary_color; + color: $secondary_color; &:hover { - background: mix($white, $legend_color); + background: mix($white, $secondary_color); color: $white; transition: all ease 0.2s; } } + table td > span { + border-radius: 0.25em; + } .p-highlight { - background: $legend_color; - color: $white; - border-radius: 100%; + background: $secondary_color !important; + color: $white !important; } .p-disabled { background: $d-grey; @@ -326,7 +327,7 @@ mat-checkbox { &:nth-of-type(6), &:nth-of-type(7) { //border-left: 1px solid $border-color; - background: $grey; + background: $grey-lighter; } } } diff --git a/src/styles/partials/_responsive.scss b/src/styles/partials/_responsive.scss index b83082b7..842a1d47 100644 --- a/src/styles/partials/_responsive.scss +++ b/src/styles/partials/_responsive.scss @@ -14,6 +14,7 @@ display: inline-block; } .time-choice { + padding: 1em; input { width: 80%; } diff --git a/yarn.lock b/yarn.lock index a1ff46b2..d27cddcb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9463,6 +9463,11 @@ pretty-format@^26.0.0, pretty-format@^26.1.0: ansi-styles "^4.0.0" react-is "^16.12.0" +primeicons@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/primeicons/-/primeicons-5.0.0.tgz#73a0b6028a77c58a9eeb331ad13aaf085e8451ee" + integrity sha512-heygWF0X5HFI1otlZE62pp6ye7sZ8om78J9au2BRkg8O7Y8AHTZ9qKMRzchZUHLe8zUAvdi6hZzzm9XxgwIExw== + primeng@^11.0.0: version "11.4.5" resolved "https://registry.yarnpkg.com/primeng/-/primeng-11.4.5.tgz#128137d727d555f68c212a1dcb1f2af3b0f4afd4"