From 4333076c2212ceb08d73cc6d7205e0e3bc75b986 Mon Sep 17 00:00:00 2001
From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com>
Date: Tue, 19 Jan 2021 17:43:48 +0100
Subject: [PATCH 1/3] feat: add hour-picker first version from boussole project

---
 src/app/form/form.component.html              |   5 +-
 src/app/form/form.component.ts                |   2 +
 .../hour-picker/hour-picker.component.html    | 176 ++++++++++++
 .../hour-picker/hour-picker.component.scss    |  69 +++++
 .../hour-picker/hour-picker.component.spec.ts |  25 ++
 .../hour-picker/hour-picker.component.ts      | 259 ++++++++++++++++++
 src/app/shared/components/index.ts            |   3 +
 src/app/shared/shared.module.ts               |  12 +-
 8 files changed, 547 insertions(+), 4 deletions(-)
 create mode 100644 src/app/shared/components/hour-picker/hour-picker.component.html
 create mode 100644 src/app/shared/components/hour-picker/hour-picker.component.scss
 create mode 100644 src/app/shared/components/hour-picker/hour-picker.component.spec.ts
 create mode 100644 src/app/shared/components/hour-picker/hour-picker.component.ts

diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html
index 494f7d019..2c73700d2 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></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 93ab67f33..96750559d 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',
@@ -25,6 +26,7 @@ export class FormComponent implements OnInit {
   @Input() public profile?: User;
   @Output() closeEvent = new EventEmitter<Structure>();
   public structureForm: FormGroup;
+  public tmp = new Week();
 
   public userAlreadyExist = false;
 
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 000000000..206db6250
--- /dev/null
+++ b/src/app/shared/components/hour-picker/hour-picker.component.html
@@ -0,0 +1,176 @@
+<p>
+  Horaires d'ouverture :<br />
+
+  <span class="sub-text" #test>
+    Entrez les horaires généraux d'ouverture. Les horaires propres à chaque services proposés seront à remplir plus
+    loin.
+  </span>
+</p>
+
+<div class="days">
+  <p>la ?</p>
+  <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>
+          {{ day.name | titlecase }}
+        </div>
+
+        <div>
+          <img
+            *ngIf="copiedDayName === day.name"
+            (click)="cancelCopy()"
+            src="../../../../../assets/img/black/cross.svg"
+            matTooltip="Annuler la copie"
+            matTooltipClass="tooltip"
+          />
+          <img
+            *ngIf="copiedDayName !== day.name && copiedDayName.length > 0"
+            (click)="paste(day)"
+            src="../../../../../assets/img/black/paste.svg"
+            matTooltip="Coller les heures copiées"
+            matTooltipClass="tooltip"
+          />
+          <img
+            *ngIf="!copiedDayName"
+            (click)="copy(day)"
+            src="../../../../../assets/img/black/copy.svg"
+            matTooltip="Copier les heures"
+            matTooltipClass="tooltip"
+          />
+        </div>
+
+        <div>
+          <input
+            type="checkbox"
+            id="{{ day.name }}"
+            class="toggle-checkbox hidden"
+            (click)="toggleOpenDay(day, $event.target.checked)"
+            [checked]="day.open"
+          />
+          <label for="{{ day.name }}" class="toggle-label"></label>
+        </div>
+      </div>
+    </div>
+
+    <div *ngIf="!day.open"></div>
+
+    <div *ngIf="day.open">
+      <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()" />
+          </div>
+
+          <div>à</div>
+
+          <div class="input-container">
+            <input type="time" [(ngModel)]="hour.end" (change)="checkHoursValid()" />
+          </div>
+
+          <div class="input-container">
+            <select [(ngModel)]="hour.type">
+              <option value="withoutAppointment">Présentiel (Sans rendez-vous)</option>
+              <option value="appointment">Présentiel (Avec rendez-vous)</option>
+              <option value="phone">Téléphonique</option>
+            </select>
+          </div>
+
+          <img
+            src="../../../../../assets/img/black/add-full.svg"
+            *ngIf="day.hours.length === 1"
+            (click)="addHours(day)"
+            class="add"
+          />
+          <img
+            src="../../../../../assets/img/black/cross.svg"
+            *ngIf="day.hours.length > 1"
+            (click)="removeHours(day, i)"
+          />
+
+          <div>
+            <div *ngIf="hour.error === 'incomplete'" class="warning-message">!</div>
+            <div *ngIf="hour.error === 'wrong'" class="error-message">?</div>
+          </div>
+        </div>
+
+        <img
+          src="../../../../../assets/img/black/add-full.svg"
+          *ngIf="day.hours.length > 1 && day.hours.length < 5"
+          (click)="addHours(day)"
+          class="add"
+        />
+      </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 class="input-container">
+          <select [(ngModel)]="day.hours[0].type">
+            <option value="withoutAppointment">Présentiel (Sans rendez-vous)</option>
+            <option value="appointment">Présentiel (Avec rendez-vous)</option>
+            <option value="phone">Téléphonique</option>
+          </select>
+        </div>
+
+        <img
+          src="../../../../../assets/img/black/add-full.svg"
+          *ngIf="day.hours.length === 1"
+          (click)="addHours(day)"
+          class="add"
+        />
+        <div *ngIf="day.hours.length > 1">...</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"
+          >
+            !
+          </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"
+            >
+              ?
+            </div>
+          </ng-template>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<p class="legend">
+  ! : Horaire incomplet<br />
+  ? : Horaire incohérent
+</p>
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 000000000..1179c9cd8
--- /dev/null
+++ b/src/app/shared/components/hour-picker/hour-picker.component.scss
@@ -0,0 +1,69 @@
+.days {
+  display: grid;
+  row-gap: 15px;
+
+  .day {
+    display: grid;
+    grid-template-columns: 175px 1fr;
+    column-gap: 20px;
+
+    .header-container {
+      .header {
+        display: grid;
+        grid-template-columns: 1fr auto auto;
+        column-gap: 20px;
+        align-items: center;
+        height: 40px;
+      }
+    }
+
+    .active {
+      display: grid;
+      row-gap: 10px;
+    }
+
+    .hour {
+      height: 40px;
+      display: grid;
+      grid-template-columns: auto 100px auto 100px 1fr 30px 20px;
+      column-gap: 10px;
+      align-items: center;
+      justify-items: center;
+    }
+  }
+}
+
+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;
+}
+
+.legend {
+  font-weight: normal;
+  font-style: italic;
+  text-align: right;
+}
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 000000000..e05b86064
--- /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 000000000..eb50fd442
--- /dev/null
+++ b/src/app/shared/components/hour-picker/hour-picker.component.ts
@@ -0,0 +1,259 @@
+import { Component, Input, Output, EventEmitter, OnDestroy, ViewChild, OnChanges, OnInit } from '@angular/core';
+import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
+import * as _ from 'lodash';
+import { Structure } from '../../../models/structure.model';
+import { Time } from '../../../models/time.model';
+import { Week } from '../../../models/week.model';
+
+@Component({
+  selector: 'app-hour-picker',
+  templateUrl: './hour-picker.component.html',
+  styleUrls: ['./hour-picker.component.scss'],
+})
+export class HourPickerComponent implements OnChanges, OnDestroy {
+  @ViewChild('test', { static: true }) test;
+  @Input() modifiedFields: any;
+  // @Input() structure: any;
+
+  @Output() updateHoursError = new EventEmitter<{ badHoursFormat: boolean }>();
+
+  private copiedDay: any;
+  public copiedDayName = '';
+  public structure = {
+    hours: [
+      {
+        name: 'Lundi',
+        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        open: false,
+        active: false,
+      },
+      {
+        name: 'Mardi',
+        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        open: false,
+        active: false,
+      },
+      {
+        name: 'Mercredi',
+        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        open: false,
+        active: false,
+      },
+      {
+        name: 'Jeudi',
+        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        open: false,
+        active: false,
+      },
+      {
+        name: 'Vendredi',
+        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        open: false,
+        active: false,
+      },
+      {
+        name: 'Samedi',
+        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        open: false,
+        active: false,
+      },
+      {
+        name: 'Dimanche',
+        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        open: false,
+        active: false,
+      },
+    ],
+  };
+  public structureHoursDefault: any[] = [
+    {
+      name: 'Lundi',
+      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      open: false,
+      active: false,
+    },
+    {
+      name: 'Mardi',
+      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      open: false,
+      active: false,
+    },
+    {
+      name: 'Mercredi',
+      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      open: false,
+      active: false,
+    },
+    {
+      name: 'Jeudi',
+      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      open: false,
+      active: false,
+    },
+    {
+      name: 'Vendredi',
+      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      open: false,
+      active: false,
+    },
+    {
+      name: 'Samedi',
+      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      open: false,
+      active: false,
+    },
+    {
+      name: 'Dimanche',
+      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      open: false,
+      active: false,
+    },
+  ];
+
+  ngOnChanges(): void {
+    this.formatHoursForEdition();
+  }
+
+  ngOnDestroy(): void {
+    this.formatHoursForSave();
+  }
+
+  /**
+   * Intégrer les horaires dans les horaires par défaut du composant
+   */
+  formatHoursForEdition() {
+    console.log('formatHoursForEdition');
+    if (this.structure.hours) {
+      for (const dayDefault of this.structureHoursDefault) {
+        const foundDay = this.structure.hours.filter((day) => day.name === dayDefault.name);
+
+        // if (foundDay.length && !foundDay[0].error) {
+        if (foundDay.length) {
+          foundDay[0].open = true;
+        } else if (!foundDay.length) {
+          this.structure.hours.push(dayDefault);
+        }
+      }
+    } else {
+      this.structure.hours = this.structureHoursDefault;
+    }
+  }
+
+  /**
+   * Formater les horaires pour l'enregistrement en base :
+   * supprimer les données inutiles
+   */
+  formatHoursForSave(): any {
+    console.log('formatHoursForSave');
+    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;
+      }
+    }
+  }
+
+  activateDay(day: any): void {
+    console.log('activateDay');
+    this.structure.hours.forEach((dayHours) => {
+      dayHours.active = false;
+    });
+    day.active = true;
+  }
+
+  toggleOpenDay(day: any, value: any): void {
+    day.open = value;
+
+    this.checkHoursValid();
+  }
+
+  /**
+   * Ajouter une ligne d'horaires à un jour
+   */
+  addHours(day: any): void {
+    if (day.hours.length >= 5) {
+      return;
+    }
+
+    day.hours.push({
+      start: '',
+      end: '',
+      type: 'withoutAppointment',
+      error: 'incomplete',
+    });
+
+    this.checkHoursValid();
+  }
+
+  /**
+   * Supprimer la dernière ligne d'horaires d'un jour
+   */
+  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
+   */
+  copy(day): void {
+    this.copiedDayName = day.name;
+    this.copiedDay = day;
+  }
+
+  /**
+   * Remplacer les horaires d'un jour par les horaires copiés précédemment
+   */
+  paste(day): void {
+    day.hours = JSON.parse(JSON.stringify(this.copiedDay.hours));
+    day.open = this.copiedDay.open;
+  }
+
+  /**
+   * Annuler la copie des horaires
+   */
+  cancelCopy(): void {
+    this.copiedDayName = '';
+    this.copiedDay = null;
+  }
+
+  /**
+   * Vérifier que le format des horaires est correct
+   */
+  checkHoursValid() {
+    let error = false;
+
+    console.log('checkHoursValid');
+    for (const day of this.structure.hours) {
+      if (day.open) {
+        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);
+    }
+  }
+}
diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts
index e4d55982f..2b6cc7e46 100644
--- a/src/app/shared/components/index.ts
+++ b/src/app/shared/components/index.ts
@@ -7,6 +7,7 @@ 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';
 
 // tslint:disable-next-line: max-line-length
 export {
@@ -19,6 +20,7 @@ export {
   SignInModalComponent,
   CreateAccountFormComponent,
   AddressAutocompleteComponent,
+  HourPickerComponent,
 };
 
 // tslint:disable-next-line:variable-name
@@ -32,4 +34,5 @@ export const SharedComponents = [
   SignInModalComponent,
   CreateAccountFormComponent,
   AddressAutocompleteComponent,
+  HourPickerComponent,
 ];
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index 2709454bd..c0829cf6b 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -8,9 +8,17 @@ 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';
 @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,
+  ],
   exports: [
     ...SharedPipes,
     ...SharedComponents,
-- 
GitLab


From 36904c785c075d64c68fffe669e3e9b33c9d119f Mon Sep 17 00:00:00 2001
From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com>
Date: Wed, 20 Jan 2021 14:25:08 +0100
Subject: [PATCH 2/3] feat: update date-picker style

---
 .../copy-paste/copy-paste.component.html      |  13 ++
 .../copy-paste/copy-paste.component.scss      |  13 ++
 .../copy-paste/copy-paste.component.spec.ts   |  25 +++
 .../copy-paste/copy-paste.component.ts        |  30 ++++
 .../hour-picker/hour-picker.component.html    | 162 +++++++++---------
 .../hour-picker/hour-picker.component.scss    |  57 ++++--
 .../hour-picker/hour-picker.component.ts      |  59 ++++---
 src/app/shared/components/index.ts            |   3 +
 src/app/shared/shared.module.ts               |   2 +
 src/assets/ico/sprite.svg                     |  30 ++++
 src/assets/scss/_inputs.scss                  |  51 ++++++
 src/assets/scss/_typography.scss              |  17 +-
 12 files changed, 337 insertions(+), 125 deletions(-)
 create mode 100644 src/app/shared/components/hour-picker/copy-paste/copy-paste.component.html
 create mode 100644 src/app/shared/components/hour-picker/copy-paste/copy-paste.component.scss
 create mode 100644 src/app/shared/components/hour-picker/copy-paste/copy-paste.component.spec.ts
 create mode 100644 src/app/shared/components/hour-picker/copy-paste/copy-paste.component.ts

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 000000000..58741d87a
--- /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 000000000..537a69933
--- /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 000000000..c904bb2ad
--- /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 000000000..6a63223ca
--- /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
index 206db6250..7d8082936 100644
--- a/src/app/shared/components/hour-picker/hour-picker.component.html
+++ b/src/app/shared/components/hour-picker/hour-picker.component.html
@@ -1,67 +1,56 @@
-<p>
-  Horaires d'ouverture :<br />
-
-  <span class="sub-text" #test>
-    Entrez les horaires généraux d'ouverture. Les horaires propres à chaque services proposés seront à remplir plus
-    loin.
-  </span>
-</p>
+<h1>Quels sont les horaires d'ouverture ?<br /></h1>
 
 <div class="days">
-  <p>la ?</p>
   <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>
-          {{ day.name | titlecase }}
-        </div>
-
-        <div>
-          <img
-            *ngIf="copiedDayName === day.name"
-            (click)="cancelCopy()"
-            src="../../../../../assets/img/black/cross.svg"
-            matTooltip="Annuler la copie"
-            matTooltipClass="tooltip"
-          />
-          <img
-            *ngIf="copiedDayName !== day.name && copiedDayName.length > 0"
-            (click)="paste(day)"
-            src="../../../../../assets/img/black/paste.svg"
-            matTooltip="Coller les heures copiées"
-            matTooltipClass="tooltip"
-          />
-          <img
-            *ngIf="!copiedDayName"
-            (click)="copy(day)"
-            src="../../../../../assets/img/black/copy.svg"
-            matTooltip="Copier les heures"
-            matTooltipClass="tooltip"
-          />
-        </div>
-
-        <div>
-          <input
+        <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"
+            />
+            <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>
+    <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">
+    <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>de</div>
 
           <div class="input-container">
             <input type="time" [(ngModel)]="hour.start" (change)="checkHoursValid()" />
@@ -73,38 +62,58 @@
             <input type="time" [(ngModel)]="hour.end" (change)="checkHoursValid()" />
           </div>
 
-          <div class="input-container">
-            <select [(ngModel)]="hour.type">
-              <option value="withoutAppointment">Présentiel (Sans rendez-vous)</option>
-              <option value="appointment">Présentiel (Avec rendez-vous)</option>
-              <option value="phone">Téléphonique</option>
-            </select>
+          <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>
-
-          <img
-            src="../../../../../assets/img/black/add-full.svg"
-            *ngIf="day.hours.length === 1"
+        </div>
+        <div class="add" *ngIf="day.hours.length === 1">
+          <div
             (click)="addHours(day)"
-            class="add"
-          />
-          <img
-            src="../../../../../assets/img/black/cross.svg"
-            *ngIf="day.hours.length > 1"
-            (click)="removeHours(day, i)"
-          />
-
-          <div>
-            <div *ngIf="hour.error === 'incomplete'" class="warning-message">!</div>
-            <div *ngIf="hour.error === 'wrong'" class="error-message">?</div>
+            fxLayout="row"
+            fxLayoutAlign="center center"
+            fxLayoutGap="3px"
+            class="grey-rounded-border"
+          >
+            <app-svg-icon
+              [type]="'ico'"
+              [icon]="'add'"
+              [iconColor]="'currentColor'"
+              (click)="cancelCopy()"
+            ></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>
 
-        <img
+        <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>
+
+        <!-- <img
           src="../../../../../assets/img/black/add-full.svg"
           *ngIf="day.hours.length > 1 && day.hours.length < 5"
           (click)="addHours(day)"
           class="add"
-        />
+        /> -->
       </div>
 
       <div class="inactive hour" *ngIf="!day.active">
@@ -120,20 +129,14 @@
           <input type="time" [(ngModel)]="day.hours[0].end" (change)="checkHoursValid()" />
         </div>
 
-        <div class="input-container">
-          <select [(ngModel)]="day.hours[0].type">
-            <option value="withoutAppointment">Présentiel (Sans rendez-vous)</option>
-            <option value="appointment">Présentiel (Avec rendez-vous)</option>
-            <option value="phone">Téléphonique</option>
-          </select>
-        </div>
+        <div *ngIf="day.hours.length > 1 && day.hours.length < 2" (click)="addHours(day)">+ Ajouter 3</div>
 
-        <img
+        <!-- <img
           src="../../../../../assets/img/black/add-full.svg"
           *ngIf="day.hours.length === 1"
           (click)="addHours(day)"
           class="add"
-        />
+        /> -->
         <div *ngIf="day.hours.length > 1">...</div>
 
         <div>
@@ -148,7 +151,7 @@
             "
             class="warning-message"
           >
-            !
+            <app-svg-icon [type]="'ico'" [icon]="'nok'"></app-svg-icon>
           </div>
           <ng-template #wrong>
             <div
@@ -161,7 +164,7 @@
               "
               class="error-message"
             >
-              ?
+              <app-svg-icon [type]="'ico'" [icon]="'nok'"></app-svg-icon>
             </div>
           </ng-template>
         </div>
@@ -169,8 +172,3 @@
     </div>
   </div>
 </div>
-
-<p class="legend">
-  ! : Horaire incomplet<br />
-  ? : Horaire incohérent
-</p>
diff --git a/src/app/shared/components/hour-picker/hour-picker.component.scss b/src/app/shared/components/hour-picker/hour-picker.component.scss
index 1179c9cd8..01f1ff6e5 100644
--- a/src/app/shared/components/hour-picker/hour-picker.component.scss
+++ b/src/app/shared/components/hour-picker/hour-picker.component.scss
@@ -1,31 +1,50 @@
+@import '../../../../assets/scss/color';
+@import '../../../../assets/scss/typography';
+
+h1 {
+  @include cn-bold-22;
+}
+
 .days {
   display: grid;
   row-gap: 15px;
 
   .day {
     display: grid;
-    grid-template-columns: 175px 1fr;
+    grid-template-columns: 130px 1fr;
     column-gap: 20px;
 
     .header-container {
       .header {
         display: grid;
-        grid-template-columns: 1fr auto auto;
+        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;
-      row-gap: 10px;
+      grid-template-columns: 1fr 250px 40px;
+    }
+    .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 100px auto 100px 1fr 30px 20px;
+      // 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;
@@ -33,6 +52,30 @@
   }
 }
 
+.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;
 }
@@ -61,9 +104,3 @@ img {
   display: grid;
   align-items: center;
 }
-
-.legend {
-  font-weight: normal;
-  font-style: italic;
-  text-align: right;
-}
diff --git a/src/app/shared/components/hour-picker/hour-picker.component.ts b/src/app/shared/components/hour-picker/hour-picker.component.ts
index eb50fd442..3f3a43d7b 100644
--- a/src/app/shared/components/hour-picker/hour-picker.component.ts
+++ b/src/app/shared/components/hour-picker/hour-picker.component.ts
@@ -11,7 +11,6 @@ import { Week } from '../../../models/week.model';
   styleUrls: ['./hour-picker.component.scss'],
 })
 export class HourPickerComponent implements OnChanges, OnDestroy {
-  @ViewChild('test', { static: true }) test;
   @Input() modifiedFields: any;
   // @Input() structure: any;
 
@@ -23,43 +22,43 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
     hours: [
       {
         name: 'Lundi',
-        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        hours: [{ start: '', end: '', error: 'incomplete' }],
         open: false,
         active: false,
       },
       {
         name: 'Mardi',
-        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        hours: [{ start: '', end: '', error: 'incomplete' }],
         open: false,
         active: false,
       },
       {
         name: 'Mercredi',
-        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        hours: [{ start: '', end: '', error: 'incomplete' }],
         open: false,
         active: false,
       },
       {
         name: 'Jeudi',
-        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        hours: [{ start: '', end: '', error: 'incomplete' }],
         open: false,
         active: false,
       },
       {
         name: 'Vendredi',
-        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        hours: [{ start: '', end: '', error: 'incomplete' }],
         open: false,
         active: false,
       },
       {
         name: 'Samedi',
-        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        hours: [{ start: '', end: '', error: 'incomplete' }],
         open: false,
         active: false,
       },
       {
         name: 'Dimanche',
-        hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+        hours: [{ start: '', end: '', error: 'incomplete' }],
         open: false,
         active: false,
       },
@@ -68,43 +67,43 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
   public structureHoursDefault: any[] = [
     {
       name: 'Lundi',
-      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      hours: [{ start: '', end: '', error: 'incomplete' }],
       open: false,
       active: false,
     },
     {
       name: 'Mardi',
-      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      hours: [{ start: '', end: '', error: 'incomplete' }],
       open: false,
       active: false,
     },
     {
       name: 'Mercredi',
-      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      hours: [{ start: '', end: '', error: 'incomplete' }],
       open: false,
       active: false,
     },
     {
       name: 'Jeudi',
-      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      hours: [{ start: '', end: '', error: 'incomplete' }],
       open: false,
       active: false,
     },
     {
       name: 'Vendredi',
-      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      hours: [{ start: '', end: '', error: 'incomplete' }],
       open: false,
       active: false,
     },
     {
       name: 'Samedi',
-      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      hours: [{ start: '', end: '', error: 'incomplete' }],
       open: false,
       active: false,
     },
     {
       name: 'Dimanche',
-      hours: [{ start: '', end: '', type: 'withoutAppointment', error: 'incomplete' }],
+      hours: [{ start: '', end: '', error: 'incomplete' }],
       open: false,
       active: false,
     },
@@ -121,7 +120,7 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
   /**
    * Intégrer les horaires dans les horaires par défaut du composant
    */
-  formatHoursForEdition() {
+  public formatHoursForEdition(): void {
     console.log('formatHoursForEdition');
     if (this.structure.hours) {
       for (const dayDefault of this.structureHoursDefault) {
@@ -143,7 +142,7 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
    * Formater les horaires pour l'enregistrement en base :
    * supprimer les données inutiles
    */
-  formatHoursForSave(): any {
+  public formatHoursForSave(): void {
     console.log('formatHoursForSave');
     if (!this.structure.hours) {
       return;
@@ -160,16 +159,19 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
     }
   }
 
-  activateDay(day: any): void {
+  public activateDay(day: any): void {
     console.log('activateDay');
-    this.structure.hours.forEach((dayHours) => {
-      dayHours.active = false;
-    });
+    // this.structure.hours.forEach((dayHours) => {
+    //   dayHours.active = false;
+    // });
     day.active = true;
   }
 
-  toggleOpenDay(day: any, value: any): void {
+  public toggleOpenDay(day: any, value: any): void {
     day.open = value;
+    if (!value) {
+      day.hours = [{ start: '', end: '', error: 'incomplete' }];
+    }
 
     this.checkHoursValid();
   }
@@ -177,7 +179,7 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
   /**
    * Ajouter une ligne d'horaires à un jour
    */
-  addHours(day: any): void {
+  public addHours(day: any): void {
     if (day.hours.length >= 5) {
       return;
     }
@@ -195,7 +197,7 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
   /**
    * Supprimer la dernière ligne d'horaires d'un jour
    */
-  removeHours(day: any, index: number): void {
+  public removeHours(day: any, index: number): void {
     if (index > -1) {
       day.hours.splice(index, 1);
     }
@@ -204,7 +206,8 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
   /**
    * Copier les horaires d'un jour pour les coller par dessus les horaires d'un autre jour
    */
-  copy(day): void {
+  public copy(day): void {
+    console.log('copy', day);
     this.copiedDayName = day.name;
     this.copiedDay = day;
   }
@@ -212,7 +215,7 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
   /**
    * Remplacer les horaires d'un jour par les horaires copiés précédemment
    */
-  paste(day): void {
+  public paste(day): void {
     day.hours = JSON.parse(JSON.stringify(this.copiedDay.hours));
     day.open = this.copiedDay.open;
   }
@@ -220,7 +223,7 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
   /**
    * Annuler la copie des horaires
    */
-  cancelCopy(): void {
+  public cancelCopy(): void {
     this.copiedDayName = '';
     this.copiedDay = null;
   }
@@ -228,7 +231,7 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
   /**
    * Vérifier que le format des horaires est correct
    */
-  checkHoursValid() {
+  public checkHoursValid(): void {
     let error = false;
 
     console.log('checkHoursValid');
diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts
index 2b6cc7e46..2916701f9 100644
--- a/src/app/shared/components/index.ts
+++ b/src/app/shared/components/index.ts
@@ -8,6 +8,7 @@ import { ValidatorFormComponent } from './validator-form/validator-form.componen
 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 {
@@ -21,6 +22,7 @@ export {
   CreateAccountFormComponent,
   AddressAutocompleteComponent,
   HourPickerComponent,
+  CopyPasteComponent,
 };
 
 // tslint:disable-next-line:variable-name
@@ -35,4 +37,5 @@ export const SharedComponents = [
   CreateAccountFormComponent,
   AddressAutocompleteComponent,
   HourPickerComponent,
+  CopyPasteComponent,
 ];
diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts
index c0829cf6b..d3f315bc5 100644
--- a/src/app/shared/shared.module.ts
+++ b/src/app/shared/shared.module.ts
@@ -9,6 +9,7 @@ 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, FormsModule, RouterModule, FlexLayoutModule, ReactiveFormsModule],
   declarations: [
@@ -18,6 +19,7 @@ import { HourPickerComponent } from './components/hour-picker/hour-picker.compon
     SvgIconComponent,
     AddressAutocompleteComponent,
     HourPickerComponent,
+    CopyPasteComponent,
   ],
   exports: [
     ...SharedPipes,
diff --git a/src/assets/ico/sprite.svg b/src/assets/ico/sprite.svg
index 342b08c58..54b2b4e7e 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="#ED3939"/>
+<path d="M12.5 20L20 12.5" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M20 20L12.5 12.5" 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 e672692f6..ce1e0b7ab 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 e7469954d..0d6744ebf 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;
-- 
GitLab


From c61a63e391636645af45b26c174dd57d86d17e80 Mon Sep 17 00:00:00 2001
From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com>
Date: Wed, 20 Jan 2021 19:24:24 +0100
Subject: [PATCH 3/3] feat: add logic to hout-picker + remove copy/paste
 feature

---
 src/app/form/form.component.html              |   2 +-
 src/app/form/form.component.ts                |   3 +-
 .../hour-picker/hour-picker.component.html    |  48 ++--
 .../hour-picker/hour-picker.component.scss    |   8 +-
 .../hour-picker/hour-picker.component.ts      | 207 +++++++++++-------
 src/assets/ico/sprite.svg                     |   6 +-
 6 files changed, 153 insertions(+), 121 deletions(-)

diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html
index 2c73700d2..37bdaf585 100644
--- a/src/app/form/form.component.html
+++ b/src/app/form/form.component.html
@@ -144,7 +144,7 @@
     </div>
     <div formGroupName="hours">
       <p>Heures</p>
-      <app-hour-picker></app-hour-picker>
+      <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>
diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts
index 96750559d..43b46c377 100644
--- a/src/app/form/form.component.ts
+++ b/src/app/form/form.component.ts
@@ -22,11 +22,10 @@ import { Week } from '../models/week.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;
-  public tmp = new Week();
 
   public userAlreadyExist = false;
 
diff --git a/src/app/shared/components/hour-picker/hour-picker.component.html b/src/app/shared/components/hour-picker/hour-picker.component.html
index 7d8082936..5b3e72402 100644
--- a/src/app/shared/components/hour-picker/hour-picker.component.html
+++ b/src/app/shared/components/hour-picker/hour-picker.component.html
@@ -21,6 +21,7 @@
               id="{{ day.name }}"
               (click)="toggleOpenDay(day, $event.target.checked)"
               [checked]="day.open"
+              [disabled]="isEditMode"
             />
             <span class="slider"></span>
           </label>
@@ -33,7 +34,7 @@
       </div>
     </div>
 
-    <div *ngIf="!day.open">
+    <!-- <div *ngIf="!day.open">
       <div class="active">
         <div class="grid-center">
           <app-copy-paste
@@ -45,7 +46,7 @@
           ></app-copy-paste>
         </div>
       </div>
-    </div>
+    </div> -->
 
     <div *ngIf="day.open" class="row-container">
       <div class="active" *ngIf="day.active">
@@ -53,13 +54,13 @@
           <div>de</div>
 
           <div class="input-container">
-            <input type="time" [(ngModel)]="hour.start" (change)="checkHoursValid()" />
+            <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()" />
+            <input type="time" [(ngModel)]="hour.end" (change)="checkHoursValid()" [disabled]="isEditMode" />
           </div>
 
           <div>
@@ -71,7 +72,7 @@
             </div>
           </div>
         </div>
-        <div class="add" *ngIf="day.hours.length === 1">
+        <div class="add" *ngIf="day.hours.length === 1 && !isEditMode">
           <div
             (click)="addHours(day)"
             fxLayout="row"
@@ -79,15 +80,9 @@
             fxLayoutGap="3px"
             class="grey-rounded-border"
           >
-            <app-svg-icon
-              [type]="'ico'"
-              [icon]="'add'"
-              [iconColor]="'currentColor'"
-              (click)="cancelCopy()"
-            ></app-svg-icon
-            >Ajouter
+            <app-svg-icon [type]="'ico'" [icon]="'add'" [iconColor]="'currentColor'"></app-svg-icon>Ajouter
           </div>
-          <div class="grid-center">
+          <!-- <div class="grid-center">
             <app-copy-paste
               [day]="day"
               [copiedDayName]="copiedDayName"
@@ -95,10 +90,10 @@
               (pasteEvent)="paste($event)"
               (cancelEvent)="cancelCopy()"
             ></app-copy-paste>
-          </div>
+          </div> -->
         </div>
 
-        <div *ngIf="day.hours.length === 2" class="grid-center">
+        <!-- <div *ngIf="day.hours.length === 2" class="grid-center">
           <app-copy-paste
             [day]="day"
             [copiedDayName]="copiedDayName"
@@ -106,17 +101,10 @@
             (pasteEvent)="paste($event)"
             (cancelEvent)="cancelCopy()"
           ></app-copy-paste>
-        </div>
-
-        <!-- <img
-          src="../../../../../assets/img/black/add-full.svg"
-          *ngIf="day.hours.length > 1 && day.hours.length < 5"
-          (click)="addHours(day)"
-          class="add"
-        /> -->
+        </div> -->
       </div>
 
-      <div class="inactive hour" *ngIf="!day.active">
+      <!-- <div class="inactive hour" *ngIf="!day.active">
         <div>De</div>
 
         <div>
@@ -129,16 +117,6 @@
           <input type="time" [(ngModel)]="day.hours[0].end" (change)="checkHoursValid()" />
         </div>
 
-        <div *ngIf="day.hours.length > 1 && day.hours.length < 2" (click)="addHours(day)">+ Ajouter 3</div>
-
-        <!-- <img
-          src="../../../../../assets/img/black/add-full.svg"
-          *ngIf="day.hours.length === 1"
-          (click)="addHours(day)"
-          class="add"
-        /> -->
-        <div *ngIf="day.hours.length > 1">...</div>
-
         <div>
           <div
             *ngIf="
@@ -168,7 +146,7 @@
             </div>
           </ng-template>
         </div>
-      </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
index 01f1ff6e5..d685087b0 100644
--- a/src/app/shared/components/hour-picker/hour-picker.component.scss
+++ b/src/app/shared/components/hour-picker/hour-picker.component.scss
@@ -1,5 +1,6 @@
 @import '../../../../assets/scss/color';
 @import '../../../../assets/scss/typography';
+@import '../../../../assets/scss/breakpoint';
 
 h1 {
   @include cn-bold-22;
@@ -12,7 +13,7 @@ h1 {
   .day {
     display: grid;
     grid-template-columns: 130px 1fr;
-    column-gap: 20px;
+    column-gap: 10px;
 
     .header-container {
       .header {
@@ -31,6 +32,11 @@ h1 {
     .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;
diff --git a/src/app/shared/components/hour-picker/hour-picker.component.ts b/src/app/shared/components/hour-picker/hour-picker.component.ts
index 3f3a43d7b..03fefa234 100644
--- a/src/app/shared/components/hour-picker/hour-picker.component.ts
+++ b/src/app/shared/components/hour-picker/hour-picker.component.ts
@@ -1,9 +1,9 @@
-import { Component, Input, Output, EventEmitter, OnDestroy, ViewChild, OnChanges, OnInit } from '@angular/core';
-import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
+import { Component, Input, Output, EventEmitter, OnDestroy, OnChanges } from '@angular/core';
+import { AbstractControl, FormArray, FormControl, FormGroup, Validators } from '@angular/forms';
 import * as _ from 'lodash';
-import { Structure } from '../../../models/structure.model';
+import { Day } from '../../../models/day.model';
 import { Time } from '../../../models/time.model';
-import { Week } from '../../../models/week.model';
+import { WeekDayEnum } from '../../enum/weekDay.enum';
 
 @Component({
   selector: 'app-hour-picker',
@@ -12,14 +12,33 @@ import { Week } from '../../../models/week.model';
 })
 export class HourPickerComponent implements OnChanges, OnDestroy {
   @Input() modifiedFields: any;
-  // @Input() structure: 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: [
+    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' }],
@@ -62,80 +81,98 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
         open: false,
         active: false,
       },
-    ],
-  };
-  public structureHoursDefault: any[] = [
-    {
-      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,
-    },
-  ];
+    ];
+  }
 
-  ngOnChanges(): void {
-    this.formatHoursForEdition();
+  /**
+   * 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;
   }
 
-  ngOnDestroy(): void {
-    this.formatHoursForSave();
+  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 {
-    console.log('formatHoursForEdition');
-    if (this.structure.hours) {
-      for (const dayDefault of this.structureHoursDefault) {
-        const foundDay = this.structure.hours.filter((day) => day.name === dayDefault.name);
-
-        // if (foundDay.length && !foundDay[0].error) {
-        if (foundDay.length) {
-          foundDay[0].open = true;
-        } else if (!foundDay.length) {
-          this.structure.hours.push(dayDefault);
-        }
-      }
-    } else {
-      this.structure.hours = this.structureHoursDefault;
+    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;
   }
 
   /**
@@ -143,7 +180,6 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
    * supprimer les données inutiles
    */
   public formatHoursForSave(): void {
-    console.log('formatHoursForSave');
     if (!this.structure.hours) {
       return;
     }
@@ -160,10 +196,6 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
   }
 
   public activateDay(day: any): void {
-    console.log('activateDay');
-    // this.structure.hours.forEach((dayHours) => {
-    //   dayHours.active = false;
-    // });
     day.active = true;
   }
 
@@ -187,7 +219,6 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
     day.hours.push({
       start: '',
       end: '',
-      type: 'withoutAppointment',
       error: 'incomplete',
     });
 
@@ -207,7 +238,6 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
    * Copier les horaires d'un jour pour les coller par dessus les horaires d'un autre jour
    */
   public copy(day): void {
-    console.log('copy', day);
     this.copiedDayName = day.name;
     this.copiedDay = day;
   }
@@ -233,10 +263,12 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
    */
   public checkHoursValid(): void {
     let error = false;
-
-    console.log('checkHoursValid');
     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';
@@ -257,6 +289,23 @@ export class HourPickerComponent implements OnChanges, OnDestroy {
       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/assets/ico/sprite.svg b/src/assets/ico/sprite.svg
index 54b2b4e7e..6fa070156 100644
--- a/src/assets/ico/sprite.svg
+++ b/src/assets/ico/sprite.svg
@@ -30,9 +30,9 @@
 </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="#ED3939"/>
-<path d="M12.5 20L20 12.5" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
-<path d="M20 20L12.5 12.5" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
+<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">
-- 
GitLab