diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 494f7d0199025917e57d0a4743c8a9021c873e89..37bdaf585db16190fc35662a8fb4d9e266c0f665 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -144,7 +144,8 @@ </div> <div formGroupName="hours"> <p>Heures</p> - <div *ngFor="let day of weekDay | keyvalue"> + <app-hour-picker [structureInput]="getStructureControl('hours')" [isEditMode]="!isEditMode"></app-hour-picker> + <!-- <div *ngFor="let day of weekDay | keyvalue"> <div [formGroupName]="day.key"> <p>Ouvert le {{ day.value }} ? :</p> <input type="radio" formControlName="open" (click)="addTime(day.key)" [value]="true" />Oui <br /> @@ -167,7 +168,7 @@ </div> </div> </div> - </div> + </div> --> </div> <p>Fermetures exceptionnelles</p> <input type="text" formControlName="exceptionalClosures" /> diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 93ab67f337a7341b780192c3fc491270853aeb2e..43b46c377d83b950bd2b2a34064de3e551715717 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -13,6 +13,7 @@ import { typeStructureEnum } from '../shared/enum/typeStructure.enum'; import { FonctionContactEnum } from '../shared/enum/fonctionContact.enum'; import { ProfileService } from '../profile/services/profile.service'; import { User } from '../models/user.model'; +import { Week } from '../models/week.model'; @Component({ selector: 'app-structureForm', @@ -21,7 +22,7 @@ import { User } from '../models/user.model'; }) export class FormComponent implements OnInit { @Input() public idStructure?: string; - @Input() public isEditMode: boolean; + @Input() public isEditMode: boolean = true; @Input() public profile?: User; @Output() closeEvent = new EventEmitter<Structure>(); public structureForm: FormGroup; diff --git a/src/app/shared/components/hour-picker/copy-paste/copy-paste.component.html b/src/app/shared/components/hour-picker/copy-paste/copy-paste.component.html new file mode 100644 index 0000000000000000000000000000000000000000..58741d87ae76430319f6666494db22e631e99c89 --- /dev/null +++ b/src/app/shared/components/hour-picker/copy-paste/copy-paste.component.html @@ -0,0 +1,13 @@ +<div> + <div *ngIf="copiedDayName === day.name" class="grey-rounded-border"> + <app-svg-icon [type]="'ico'" [icon]="'cancel'" [iconColor]="'currentColor'" (click)="cancel()"></app-svg-icon> + </div> + + <div *ngIf="copiedDayName !== day.name && copiedDayName.length > 0" class="grey-rounded-border"> + <app-svg-icon [type]="'ico'" [icon]="'paste'" [iconColor]="'currentColor'" (click)="paste(day)"></app-svg-icon> + </div> + + <div *ngIf="!copiedDayName" class="grey-rounded-border"> + <app-svg-icon [type]="'ico'" [icon]="'copy'" [iconColor]="'currentColor'" (click)="copy(day)"></app-svg-icon> + </div> +</div> diff --git a/src/app/shared/components/hour-picker/copy-paste/copy-paste.component.scss b/src/app/shared/components/hour-picker/copy-paste/copy-paste.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..537a699337525284055d070052847399d4f509b9 --- /dev/null +++ b/src/app/shared/components/hour-picker/copy-paste/copy-paste.component.scss @@ -0,0 +1,13 @@ +@import '../../../../../assets/scss/color'; +@import '../../../../../assets/scss/typography'; + +.grey-rounded-border { + border: 1px solid $grey-4; + box-sizing: border-box; + border-radius: 22px; + @include cn-regular-14; + color: $grey-2; + display: flex; + justify-content: center; + width: 40px; +} diff --git a/src/app/shared/components/hour-picker/copy-paste/copy-paste.component.spec.ts b/src/app/shared/components/hour-picker/copy-paste/copy-paste.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c904bb2addc0f90da87b41a2e165a788e1585e09 --- /dev/null +++ b/src/app/shared/components/hour-picker/copy-paste/copy-paste.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CopyPasteComponent } from './copy-paste.component'; + +describe('CopyPasteComponent', () => { + let component: CopyPasteComponent; + let fixture: ComponentFixture<CopyPasteComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ CopyPasteComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(CopyPasteComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/hour-picker/copy-paste/copy-paste.component.ts b/src/app/shared/components/hour-picker/copy-paste/copy-paste.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..6a63223ca4f060e7e066a92441dbc4c85db5eeef --- /dev/null +++ b/src/app/shared/components/hour-picker/copy-paste/copy-paste.component.ts @@ -0,0 +1,30 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +@Component({ + selector: 'app-copy-paste', + templateUrl: './copy-paste.component.html', + styleUrls: ['./copy-paste.component.scss'], +}) +export class CopyPasteComponent implements OnInit { + @Input() copiedDayName = ''; + @Input() day = null; + + @Output() copyEvent = new EventEmitter<any>(); + @Output() cancelEvent = new EventEmitter<any>(); + @Output() pasteEvent = new EventEmitter<any>(); + constructor() {} + + ngOnInit(): void {} + + public copy() { + this.copyEvent.emit(this.day); + } + + public paste() { + this.pasteEvent.emit(this.day); + } + + public cancel() { + this.cancelEvent.emit(); + } +} diff --git a/src/app/shared/components/hour-picker/hour-picker.component.html b/src/app/shared/components/hour-picker/hour-picker.component.html new file mode 100644 index 0000000000000000000000000000000000000000..5b3e7240247eef87597d5212118fc504d68cee92 --- /dev/null +++ b/src/app/shared/components/hour-picker/hour-picker.component.html @@ -0,0 +1,152 @@ +<h1>Quels sont les horaires d'ouverture ?<br /></h1> + +<div class="days"> + <div *ngFor="let day of structure.hours" (click)="activateDay(day)" class="day" [ngClass]="{ active: day.active }"> + <div + class="header-container sub-text" + [ngClass]="modifiedFields && modifiedFields.hours && modifiedFields.hours[day.name] ? 'modified' : ''" + > + <div class="header"> + <div class="grid-center"> + <!-- <input + type="checkbox" + id="{{ day.name }}" + class="toggle-checkbox hidden" + (click)="toggleOpenDay(day, $event.target.checked)" + [checked]="day.open" + /> --> + <label class="switch"> + <input + type="checkbox" + id="{{ day.name }}" + (click)="toggleOpenDay(day, $event.target.checked)" + [checked]="day.open" + [disabled]="isEditMode" + /> + <span class="slider"></span> + </label> + <label for="{{ day.name }}" class="toggle-label"></label> + </div> + + <div> + {{ day.name | titlecase }} + </div> + </div> + </div> + + <!-- <div *ngIf="!day.open"> + <div class="active"> + <div class="grid-center"> + <app-copy-paste + [day]="day" + [copiedDayName]="copiedDayName" + (copyEvent)="copy($event)" + (pasteEvent)="paste($event)" + (cancelEvent)="cancelCopy()" + ></app-copy-paste> + </div> + </div> + </div> --> + + <div *ngIf="day.open" class="row-container"> + <div class="active" *ngIf="day.active"> + <div class="hour" *ngFor="let hour of day.hours; let i = index"> + <div>de</div> + + <div class="input-container"> + <input type="time" [(ngModel)]="hour.start" (change)="checkHoursValid()" [disabled]="isEditMode" /> + </div> + + <div>à</div> + + <div class="input-container"> + <input type="time" [(ngModel)]="hour.end" (change)="checkHoursValid()" [disabled]="isEditMode" /> + </div> + + <div> + <div *ngIf="hour.error === 'wrong' || hour.error === 'incomplete'" class="error-message"> + <app-svg-icon [type]="'ico'" [icon]="'nok'"></app-svg-icon> + </div> + <div *ngIf="hour.error === null" class="error-message"> + <app-svg-icon [type]="'ico'" [icon]="'ok'"></app-svg-icon> + </div> + </div> + </div> + <div class="add" *ngIf="day.hours.length === 1 && !isEditMode"> + <div + (click)="addHours(day)" + fxLayout="row" + fxLayoutAlign="center center" + fxLayoutGap="3px" + class="grey-rounded-border" + > + <app-svg-icon [type]="'ico'" [icon]="'add'" [iconColor]="'currentColor'"></app-svg-icon>Ajouter + </div> + <!-- <div class="grid-center"> + <app-copy-paste + [day]="day" + [copiedDayName]="copiedDayName" + (copyEvent)="copy($event)" + (pasteEvent)="paste($event)" + (cancelEvent)="cancelCopy()" + ></app-copy-paste> + </div> --> + </div> + + <!-- <div *ngIf="day.hours.length === 2" class="grid-center"> + <app-copy-paste + [day]="day" + [copiedDayName]="copiedDayName" + (copyEvent)="copy($event)" + (pasteEvent)="paste($event)" + (cancelEvent)="cancelCopy()" + ></app-copy-paste> + </div> --> + </div> + + <!-- <div class="inactive hour" *ngIf="!day.active"> + <div>De</div> + + <div> + <input type="time" [(ngModel)]="day.hours[0].start" (change)="checkHoursValid()" /> + </div> + + <div>à</div> + + <div> + <input type="time" [(ngModel)]="day.hours[0].end" (change)="checkHoursValid()" /> + </div> + + <div> + <div + *ngIf=" + day.hours[0].error === 'incomplete' || + (day.hours[1] && day.hours[1].error === 'incomplete') || + (day.hours[2] && day.hours[2].error === 'incomplete') || + (day.hours[3] && day.hours[3].error === 'incomplete') || + (day.hours[4] && day.hours[4].error === 'incomplete'); + else wrong + " + class="warning-message" + > + <app-svg-icon [type]="'ico'" [icon]="'nok'"></app-svg-icon> + </div> + <ng-template #wrong> + <div + *ngIf=" + day.hours[0].error === 'wrong' || + (day.hours[1] && day.hours[1].error === 'wrong') || + (day.hours[2] && day.hours[2].error === 'wrong') || + (day.hours[23] && day.hours[3].error === 'wrong') || + (day.hours[4] && day.hours[4].error === 'wrong') + " + class="error-message" + > + <app-svg-icon [type]="'ico'" [icon]="'nok'"></app-svg-icon> + </div> + </ng-template> + </div> + </div> --> + </div> + </div> +</div> diff --git a/src/app/shared/components/hour-picker/hour-picker.component.scss b/src/app/shared/components/hour-picker/hour-picker.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..d685087b08b966041d23cd97e35142cfba76ac89 --- /dev/null +++ b/src/app/shared/components/hour-picker/hour-picker.component.scss @@ -0,0 +1,112 @@ +@import '../../../../assets/scss/color'; +@import '../../../../assets/scss/typography'; +@import '../../../../assets/scss/breakpoint'; + +h1 { + @include cn-bold-22; +} + +.days { + display: grid; + row-gap: 15px; + + .day { + display: grid; + grid-template-columns: 130px 1fr; + column-gap: 10px; + + .header-container { + .header { + display: grid; + grid-template-columns: 35px auto; + column-gap: 20px; + align-items: center; + height: 40px; + } + } + + .row-container { + display: grid; + grid-template-columns: auto 1fr; + } + .active { + display: grid; + grid-template-columns: 1fr 250px 40px; + @media #{$large-phone} { + grid-template-columns: unset; + grid-template-rows: 1fr 1fr; + grid-row-gap: 20px; + } + } + .add { + display: grid; + grid-template-columns: 96px 40px; + column-gap: 10px; + // grid-template-columns: 80px 100px; + align-items: center; + } + + .hour { + height: 40px; + display: grid; + // grid-template-columns: auto 70px auto 70px 30px 80px 1fr; + grid-template-columns: auto 70px auto 70px 30px 30px; + column-gap: 10px; + align-items: center; + justify-items: center; + } + } +} + +.grey-rounded-border { + border: 1px solid $grey-4; + box-sizing: border-box; + border-radius: 22px; + @include cn-regular-14; + color: $grey-2; + display: flex; + justify-content: center; +} + +.grid-center { + display: grid; + align-items: center; +} + +input { + background: $grey-6; + border: 1px solid $grey-4; + box-sizing: border-box; + border-radius: 4px; + height: 40px; + @include cn-regular-14; +} + +p { + margin-top: 0px; +} + +img { + cursor: pointer; + height: 15px; + width: 15px; + &.add { + height: 20px; + width: 20px; + } +} + +.modified { + border-left: 3px solid red; + padding-left: 8px; + margin-left: -11px; + border-radius: 3px; +} + +.warning-message, +.error-message { + font-weight: bold; + font-size: 1em; + display: grid; + align-items: center; +} diff --git a/src/app/shared/components/hour-picker/hour-picker.component.spec.ts b/src/app/shared/components/hour-picker/hour-picker.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..e05b8606443aecf6462165b4464c22dc7b4f2ba9 --- /dev/null +++ b/src/app/shared/components/hour-picker/hour-picker.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HourPickerComponent } from './hour-picker.component'; + +describe('HourPickerComponent', () => { + let component: HourPickerComponent; + let fixture: ComponentFixture<HourPickerComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ HourPickerComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HourPickerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/hour-picker/hour-picker.component.ts b/src/app/shared/components/hour-picker/hour-picker.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..03fefa2346d548479b8d9a9d17c4a2cce4fd9826 --- /dev/null +++ b/src/app/shared/components/hour-picker/hour-picker.component.ts @@ -0,0 +1,311 @@ +import { Component, Input, Output, EventEmitter, OnDestroy, OnChanges } from '@angular/core'; +import { AbstractControl, FormArray, FormControl, FormGroup, Validators } from '@angular/forms'; +import * as _ from 'lodash'; +import { Day } from '../../../models/day.model'; +import { Time } from '../../../models/time.model'; +import { WeekDayEnum } from '../../enum/weekDay.enum'; + +@Component({ + selector: 'app-hour-picker', + templateUrl: './hour-picker.component.html', + styleUrls: ['./hour-picker.component.scss'], +}) +export class HourPickerComponent implements OnChanges, OnDestroy { + @Input() modifiedFields: any; + @Input() structureInput: FormGroup; + @Input() isEditMode: boolean; + + @Output() updateHoursError = new EventEmitter<{ badHoursFormat: boolean }>(); + @Output() updateForm = new EventEmitter<FormGroup>(); + + private copiedDay: any; + public copiedDayName = ''; + public structure = { + hours: this.initHoursDefault(), + }; + public structureHoursDefault: any[] = this.initHoursDefault(); + + ngOnChanges(): void { + this.formatHoursForEdition(); + } + + ngOnDestroy(): void { + this.formatHoursForSave(); + } + + public getStructureControl(nameControl: string): AbstractControl { + return this.structureInput.get(nameControl); + } + + private initHoursDefault(): any { + return [ + { + name: 'Lundi', + hours: [{ start: '', end: '', error: 'incomplete' }], + open: false, + active: false, + }, + { + name: 'Mardi', + hours: [{ start: '', end: '', error: 'incomplete' }], + open: false, + active: false, + }, + { + name: 'Mercredi', + hours: [{ start: '', end: '', error: 'incomplete' }], + open: false, + active: false, + }, + { + name: 'Jeudi', + hours: [{ start: '', end: '', error: 'incomplete' }], + open: false, + active: false, + }, + { + name: 'Vendredi', + hours: [{ start: '', end: '', error: 'incomplete' }], + open: false, + active: false, + }, + { + name: 'Samedi', + hours: [{ start: '', end: '', error: 'incomplete' }], + open: false, + active: false, + }, + { + name: 'Dimanche', + hours: [{ start: '', end: '', error: 'incomplete' }], + open: false, + active: false, + }, + ]; + } + + /** + * Convert data from form to component structure + */ + private parseFormToHours(day: Day, key: string): void { + this.structureHoursDefault.forEach((element) => { + if (element.name.toLowerCase() === key) { + element.open = day.open; + element.active = day.open; + element.hours = day.time + .map((hour: Time) => { + if (hour.openning) { + return { + start: this.formatNumericalHours(hour.openning), + end: this.formatNumericalHours(hour.closing), + error: null, + }; + } + }) + .filter((item) => item); + } + }); + this.structure.hours = this.structureHoursDefault; + } + + private parseToDay(data: { + name: string; + hours: { start: string; end: string }[]; + open: boolean; + active: boolean; + }): Day { + return new Day({ + open: data.open, + time: data.hours.map( + (hour) => + new Time({ + openning: this.formatStringHours(hour.start), + closing: this.formatStringHours(hour.end), + }) + ), + }); + } + + private parseHoursToForm(): FormGroup { + return new FormGroup({ + monday: this.createDay(this.parseToDay(this.structure.hours[0])), + tuesday: this.createDay(this.parseToDay(this.structure.hours[1])), + wednesday: this.createDay(this.parseToDay(this.structure.hours[2])), + thursday: this.createDay(this.parseToDay(this.structure.hours[3])), + friday: this.createDay(this.parseToDay(this.structure.hours[4])), + saturday: this.createDay(this.parseToDay(this.structure.hours[5])), + sunday: this.createDay(this.parseToDay(this.structure.hours[6])), + }); + } + + /** + * convert 1300 to '13:00' + */ + private formatNumericalHours(hour: number): string { + const numberStr = hour.toString(); + if (numberStr.length === 3) { + return `0${numberStr[0]}:${numberStr[1]}${numberStr[2]}`; + } else { + const splitStr = numberStr.match(/.{1,2}/g); + return `${splitStr[0]}:${splitStr[1]}`; + } + } + + /** + * convert '13:00' to 1300 + */ + private formatStringHours(hour: string): number { + const numberStr = hour.split(':')[0] + hour.split(':')[1]; + return parseInt(numberStr); + } + + /** + * Intégrer les horaires dans les horaires par défaut du composant + */ + public formatHoursForEdition(): void { + if (this.structureInput) { + this.parseFormToHours(this.getStructureControl('monday').value, WeekDayEnum.monday); + this.parseFormToHours(this.getStructureControl('tuesday').value, WeekDayEnum.tuesday); + this.parseFormToHours(this.getStructureControl('wednesday').value, WeekDayEnum.wednesday); + this.parseFormToHours(this.getStructureControl('thursday').value, WeekDayEnum.thursday); + this.parseFormToHours(this.getStructureControl('friday').value, WeekDayEnum.friday); + this.parseFormToHours(this.getStructureControl('saturday').value, WeekDayEnum.saturday); + this.parseFormToHours(this.getStructureControl('sunday').value, WeekDayEnum.sunday); + } + // this.structure.hours = this.structureHoursDefault; + } + + /** + * Formater les horaires pour l'enregistrement en base : + * supprimer les données inutiles + */ + public formatHoursForSave(): void { + if (!this.structure.hours) { + return; + } + + this.structure.hours = this.structure.hours.filter((day) => day.open === true); + + for (const day of this.structure.hours) { + delete day.open; + delete day.active; + for (const hour of day.hours) { + delete hour.error; + } + } + } + + public activateDay(day: any): void { + day.active = true; + } + + public toggleOpenDay(day: any, value: any): void { + day.open = value; + if (!value) { + day.hours = [{ start: '', end: '', error: 'incomplete' }]; + } + + this.checkHoursValid(); + } + + /** + * Ajouter une ligne d'horaires à un jour + */ + public addHours(day: any): void { + if (day.hours.length >= 5) { + return; + } + + day.hours.push({ + start: '', + end: '', + error: 'incomplete', + }); + + this.checkHoursValid(); + } + + /** + * Supprimer la dernière ligne d'horaires d'un jour + */ + public removeHours(day: any, index: number): void { + if (index > -1) { + day.hours.splice(index, 1); + } + } + + /** + * Copier les horaires d'un jour pour les coller par dessus les horaires d'un autre jour + */ + public copy(day): void { + this.copiedDayName = day.name; + this.copiedDay = day; + } + + /** + * Remplacer les horaires d'un jour par les horaires copiés précédemment + */ + public paste(day): void { + day.hours = JSON.parse(JSON.stringify(this.copiedDay.hours)); + day.open = this.copiedDay.open; + } + + /** + * Annuler la copie des horaires + */ + public cancelCopy(): void { + this.copiedDayName = ''; + this.copiedDay = null; + } + + /** + * Vérifier que le format des horaires est correct + */ + public checkHoursValid(): void { + let error = false; + for (const day of this.structure.hours) { + if (day.open) { + // Init if no data + if (day.hours.length === 0) { + this.addHours(day); + } + for (const hour of day.hours) { + if (hour.start === '' || hour.end === '') { + hour.error = 'incomplete'; + error = true; + } else if (hour.end <= hour.start) { + hour.error = 'wrong'; + error = true; + } else { + hour.error = null; + } + } + } + } + + // Émettre l'erreur à ajouter au formulaire pour autoriser + // ou empêcher de passer à l'étape suivante + if (error) { + this.updateHoursError.emit({ badHoursFormat: true }); + } else { + this.updateHoursError.emit(null); + // Emit new form value + this.parseHoursToForm(); + this.updateForm.emit(this.parseHoursToForm()); + } + } + + private createDay(day: Day): FormGroup { + return new FormGroup({ + open: new FormControl(day.open, Validators.required), + time: new FormArray(day.time.map((oneTime) => this.createTime(oneTime))) as FormArray, + }); + } + + private createTime(time: Time): FormGroup { + return new FormGroup({ + openning: new FormControl(time.openning), + closing: new FormControl(time.closing), + }); + } +} diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts index e4d55982fd57f00574f4456459841eed11994a92..2916701f9ffa7bb2a9d4944c771955685e030c79 100644 --- a/src/app/shared/components/index.ts +++ b/src/app/shared/components/index.ts @@ -7,6 +7,8 @@ import { SvgIconComponent } from './svg-icon/svg-icon.component'; import { ValidatorFormComponent } from './validator-form/validator-form.component'; import { CreateAccountFormComponent } from './create-account-form/create-account-form.component'; import { AddressAutocompleteComponent } from './address-autocomplete/address-autocomplete.component'; +import { HourPickerComponent } from './hour-picker/hour-picker.component'; +import { CopyPasteComponent } from './hour-picker/copy-paste/copy-paste.component'; // tslint:disable-next-line: max-line-length export { @@ -19,6 +21,8 @@ export { SignInModalComponent, CreateAccountFormComponent, AddressAutocompleteComponent, + HourPickerComponent, + CopyPasteComponent, }; // tslint:disable-next-line:variable-name @@ -32,4 +36,6 @@ export const SharedComponents = [ SignInModalComponent, CreateAccountFormComponent, AddressAutocompleteComponent, + HourPickerComponent, + CopyPasteComponent, ]; diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 2709454bdff75e2077f2c0fc772a1b073fa58c03..d3f315bc55aa005c76082dfd02ad7b2bc9aea050 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -8,9 +8,19 @@ import { SharedPipes } from './pipes'; import { SharedDirectives } from './directives'; import { SvgIconComponent } from './components/svg-icon/svg-icon.component'; import { AddressAutocompleteComponent } from './components/address-autocomplete/address-autocomplete.component'; +import { HourPickerComponent } from './components/hour-picker/hour-picker.component'; +import { CopyPasteComponent } from './components/hour-picker/copy-paste/copy-paste.component'; @NgModule({ - imports: [CommonModule, RouterModule, FlexLayoutModule, ReactiveFormsModule], - declarations: [...SharedPipes, ...SharedComponents, ...SharedDirectives, SvgIconComponent, AddressAutocompleteComponent], + imports: [CommonModule, FormsModule, RouterModule, FlexLayoutModule, ReactiveFormsModule], + declarations: [ + ...SharedPipes, + ...SharedComponents, + ...SharedDirectives, + SvgIconComponent, + AddressAutocompleteComponent, + HourPickerComponent, + CopyPasteComponent, + ], exports: [ ...SharedPipes, ...SharedComponents, diff --git a/src/assets/ico/sprite.svg b/src/assets/ico/sprite.svg index 342b08c58a85dd0e2dd83d7f74aaa45880479583..6fa07015607aac8f7ff5e18259ad6c70c5a3ceed 100644 --- a/src/assets/ico/sprite.svg +++ b/src/assets/ico/sprite.svg @@ -14,6 +14,36 @@ <path fill-rule="evenodd" clip-rule="evenodd" d="M7 4C5.89543 4 5 4.89543 5 6V23C5 24.1046 5.89543 25 7 25L7 26.5C7 27.3284 7.67157 28 8.5 28C9.32843 28 10 27.3284 10 26.5V25H21V26.5C21 27.3284 21.6716 28 22.5 28C23.3284 28 24 27.3284 24 26.5V25C25.1046 25 26 24.1046 26 23V6C26 4.89543 25.1046 4 24 4H7ZM24 9H7V18H24V9ZM12 22H19L18.125 23H12.875L12 22ZM10 6C9.44772 6 9 6.44772 9 7C9 7.55228 9.44772 8 10 8H21C21.5523 8 22 7.55228 22 7C22 6.44772 21.5523 6 21 6H10ZM10.6668 21.8754C10.4609 21.1805 9.89524 20.6514 9.18821 20.4923L7 20V23H11L10.6668 21.8754ZM21.8118 20.4923C21.1048 20.6514 20.5391 21.1805 20.3332 21.8754L20 23H24V20L21.8118 20.4923Z" fill="black"/> </symbol> +<symbol id="paste" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M14 16L5 16V4H14L14 16Z" stroke="#333333" stroke-width="2"/> +<path d="M19 21C19.5523 21 20 20.5523 20 20V7C20 6.44772 19.5523 6 19 6H16V17C16 17.5523 15.5523 18 15 18H9V20C9 20.5523 9.44772 21 10 21H19Z" fill="#32383D"/> +</symbol> + + +<symbol id="copy" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M4 4C4 3.44771 4.44772 3 5 3H14C14.5523 3 15 3.44772 15 4V6H9C8.44772 6 8 6.44772 8 7V18H5C4.44772 18 4 17.5523 4 17V4ZM10 7C9.44772 7 9 7.44772 9 8V20C9 20.5523 9.44771 21 10 21H19C19.5523 21 20 20.5523 20 20V8C20 7.44772 19.5523 7 19 7H10Z" fill="#32383D"/> +</symbol> + +<symbol id="cancel" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M16.9498 5.36385C17.3403 4.97332 17.9734 4.97332 18.364 5.36385C18.7545 5.75437 18.7545 6.38753 18.364 6.77806L7.05026 18.0918C6.65973 18.4823 6.02657 18.4823 5.63605 18.0918C5.24552 17.7012 5.24552 17.0681 5.63605 16.6776L16.9498 5.36385Z" fill="black"/> +<path d="M18.364 16.6777C18.7545 17.0682 18.7545 17.7013 18.364 18.0919C17.9734 18.4824 17.3403 18.4824 16.9498 18.0919L5.63605 6.77816C5.24552 6.38764 5.24552 5.75447 5.63605 5.36395C6.02657 4.97343 6.65974 4.97343 7.05026 5.36395L18.364 16.6777Z" fill="black"/> +</symbol> + +<symbol id="nok" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> +<circle cx="16" cy="16" r="13" fill="#DA6C2E"/> +<path d="M16.25 17.5L16.25 9.00001" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M16.25 23.6065L16.25 22.9999" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> +</symbol> + +<symbol id="ok" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> +<circle cx="16" cy="16" r="13" fill="#47C562"/> +<path d="M11 16.8182L14.8889 20L21 13" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> +</symbol> + +<symbol id="add" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M12 5C11.4477 5 11 5.44772 11 6V11H6C5.44772 11 5 11.4477 5 12C5 12.5523 5.44772 13 6 13H11V18C11 18.5523 11.4477 19 12 19C12.5523 19 13 18.5523 13 18V13H18C18.5523 13 19 12.5523 19 12C19 11.4477 18.5523 11 18 11H13V6C13 5.44772 12.5523 5 12 5Z" fill="#333333"/> +</symbol> + <symbol id="liste" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> <rect x="10" y="9" width="16" height="2" rx="1" /> diff --git a/src/assets/scss/_inputs.scss b/src/assets/scss/_inputs.scss index e672692f6e3e66ced23a2d0175dd1a3bfe61c43d..ce1e0b7abbd7f21f4adcf27aaf2ce6e9aad72501 100644 --- a/src/assets/scss/_inputs.scss +++ b/src/assets/scss/_inputs.scss @@ -23,3 +23,54 @@ border: 1px solid $blue; outline: none !important; } + +/* The switch - the box around the slider */ +.switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} + +/* Hide default HTML checkbox */ +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 10px; + background-color: $white; + border-radius: 7px; + width: 34px; + height: 14px; + border: 1px solid $grey-4; +} + +.slider:before { + position: absolute; + content: ''; + height: 20px; + width: 20px; + left: -6px; + bottom: -3px; + background-color: $grey-4; + -webkit-transition: 0.4s; + transition: 0.4s; + border-radius: 50%; +} + +input:checked + .slider { + border: 1px solid $secondary-color; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); + background-color: $secondary-color; +} diff --git a/src/assets/scss/_typography.scss b/src/assets/scss/_typography.scss index e7469954d6ee56132b5a1e2d67ca42325d6cc787..0d6744ebf22892c8282121192c79e87e6fd499a0 100644 --- a/src/assets/scss/_typography.scss +++ b/src/assets/scss/_typography.scss @@ -4,7 +4,8 @@ $title-font: 'Trebuchet MS', 'Helvetica', sans-serif; $font-size-xsmall: 0.875em; // 14px $font-size-small: 1em; // 16px -$font-size-medium: 1.25em; // 20px +$font-size-smedium: 1.25em; // 20px +$font-size-medium: 1.375em; // 22px $font-size-xmedium: 1.5em; // 24px $font-size-large: 1.75em; // 28px $font-size-xlarge: 2em; // 32px @@ -96,29 +97,35 @@ h6, font-weight: bold; font-size: $font-size-xmedium; } -@mixin cn-bold-20 { +@mixin cn-bold-22 { font-family: $text-font; font-style: normal; font-weight: bold; font-size: $font-size-medium; } +@mixin cn-bold-20 { + font-family: $text-font; + font-style: normal; + font-weight: bold; + font-size: $font-size-smedium; +} @mixin cn-regular-20 { font-family: $text-font; font-style: normal; font-weight: normal; - font-size: $font-size-medium; + font-size: $font-size-smedium; } @mixin cn-bold-20 { font-family: $title-font; font-style: normal; font-weight: bold; - font-size: $font-size-medium; + font-size: $font-size-smedium; } @mixin cn-regular-20 { font-family: $title-font; font-style: normal; font-weight: normal; - font-size: $font-size-medium; + font-size: $font-size-smedium; } @mixin cn-regular-18 { font-family: $title-font;