From 62947b4532bb773811e72bdd405991fb55a78b99 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Pailharey?= <rpailharey@grandlyon.com>
Date: Mon, 7 Nov 2022 17:15:58 +0100
Subject: [PATCH 01/12] feat: route guard & resolver

---
 src/app/app.module.ts                         |  2 +
 src/app/guards/isPersonalOfferOwner.guard.ts  | 19 +++++++
 .../personal-offer-edition.component.html     |  5 ++
 .../personal-offer-edition.component.scss     |  0
 .../personal-offer-edition.component.spec.ts  | 23 ++++++++
 .../personal-offer-edition.component.ts       | 23 ++++++++
 .../personal-offer.component.html             |  4 +-
 src/app/profile/profile.module.ts             |  2 +
 src/app/profile/services/profile.service.ts   |  5 ++
 src/app/resolvers/personal-offer.resolver.ts  | 52 +++++++++++++++++++
 src/app/services/personal-offer.service.ts    |  7 ++-
 11 files changed, 139 insertions(+), 3 deletions(-)
 create mode 100644 src/app/guards/isPersonalOfferOwner.guard.ts
 create mode 100644 src/app/profile/personal-offer-edition/personal-offer-edition.component.html
 create mode 100644 src/app/profile/personal-offer-edition/personal-offer-edition.component.scss
 create mode 100644 src/app/profile/personal-offer-edition/personal-offer-edition.component.spec.ts
 create mode 100644 src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
 create mode 100644 src/app/resolvers/personal-offer.resolver.ts

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 5ea46de31..8146ce183 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -51,6 +51,7 @@ import { StructureListSearchComponent } from './structure-list/components/struct
 import { StructureListComponent } from './structure-list/structure-list.component';
 import { StructureExcludeComponent } from './structure/structure-exclude/structure-exclude.component';
 import { StructureJoinComponent } from './structure/structure-join/structure-join.component';
+import { PersonalOfferResolver } from './resolvers/personal-offer.resolver';
 
 @NgModule({
   declarations: [
@@ -108,6 +109,7 @@ import { StructureJoinComponent } from './structure/structure-join/structure-joi
     DeactivateGuard,
     TempUserResolver,
     StructureResolver,
+    PersonalOfferResolver,
     RouterListenerService,
     UpdateService,
   ],
diff --git a/src/app/guards/isPersonalOfferOwner.guard.ts b/src/app/guards/isPersonalOfferOwner.guard.ts
new file mode 100644
index 000000000..e9734ffd2
--- /dev/null
+++ b/src/app/guards/isPersonalOfferOwner.guard.ts
@@ -0,0 +1,19 @@
+import { Injectable } from '@angular/core';
+import { ActivatedRouteSnapshot, CanActivate, Router, UrlTree } from '@angular/router';
+import { ProfileService } from '../profile/services/profile.service';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class IsPersonalOfferOwnerGuard implements CanActivate {
+  constructor(private router: Router, private profileService: ProfileService) {}
+
+  async canActivate(route: ActivatedRouteSnapshot): Promise<boolean | UrlTree> {
+    const personalOffer = route.params.id;
+    const isPersonalOfferOwner = await this.profileService.isPersonalOfferOwner(personalOffer);
+    if (isPersonalOfferOwner) {
+      return true;
+    }
+    return this.router.parseUrl('/home');
+  }
+}
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.html b/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
new file mode 100644
index 000000000..3ac215dbb
--- /dev/null
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
@@ -0,0 +1,5 @@
+<p>personal-offer-edition works!</p>
+<p>{{ this.personalOffer._id }}</p>
+<p>{{ this.personalOffer.categoriesDisplay.onlineProcedures }}</p>
+<p>{{ this.personalOffer.categoriesDisplay.baseSkills }}</p>
+<p>{{ this.personalOffer.categoriesDisplay.advancedSkills }}</p>
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.scss b/src/app/profile/personal-offer-edition/personal-offer-edition.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.spec.ts b/src/app/profile/personal-offer-edition/personal-offer-edition.component.spec.ts
new file mode 100644
index 000000000..c603900ef
--- /dev/null
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PersonalOfferEditionComponent } from './personal-offer-edition.component';
+
+describe('PersonalOfferEditionComponent', () => {
+  let component: PersonalOfferEditionComponent;
+  let fixture: ComponentFixture<PersonalOfferEditionComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ PersonalOfferEditionComponent ]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(PersonalOfferEditionComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts b/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
new file mode 100644
index 000000000..604988c76
--- /dev/null
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
@@ -0,0 +1,23 @@
+import { PersonalOffer } from './../../models/personalOffer.model';
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+
+@Component({
+  selector: 'app-personal-offer-edition',
+  templateUrl: './personal-offer-edition.component.html',
+  styleUrls: ['./personal-offer-edition.component.scss'],
+})
+export class PersonalOfferEditionComponent implements OnInit {
+  public personalOffer: PersonalOffer;
+
+  constructor(private route: ActivatedRoute) {}
+
+  ngOnInit(): void {
+    this.route.data.subscribe((data) => {
+      if (data.personalOffer) {
+        this.personalOffer = data.personalOffer;
+        console.log(this.personalOffer);
+      }
+    });
+  }
+}
diff --git a/src/app/profile/profile-structure/personal-offer/personal-offer.component.html b/src/app/profile/profile-structure/personal-offer/personal-offer.component.html
index d52fd61b2..7b084c929 100644
--- a/src/app/profile/profile-structure/personal-offer/personal-offer.component.html
+++ b/src/app/profile/profile-structure/personal-offer/personal-offer.component.html
@@ -8,7 +8,7 @@
       [iconBtn]="'edit'"
       [text]="'Modifier mon offre'"
       [style]="buttonTypeEnum.SecondaryWide"
-      [disabled]="true"
+      routerLink="./edit-personal-offer/{{ this.personalOffer._id }}"
     ></app-button>
     <app-button
       *ngIf="!isPublic"
@@ -16,7 +16,7 @@
       [type]="'button'"
       [iconBtn]="'edit'"
       [style]="buttonTypeEnum.SecondaryOnlyIcon"
-      [disabled]="true"
+      routerLink="./edit-personal-offer/{{ this.personalOffer._id }}"
     ></app-button>
   </div>
   <div class="content">
diff --git a/src/app/profile/profile.module.ts b/src/app/profile/profile.module.ts
index 2ed630b3a..0890b9a58 100644
--- a/src/app/profile/profile.module.ts
+++ b/src/app/profile/profile.module.ts
@@ -13,6 +13,7 @@ import { StructuresManagementComponent } from './structures-management/structure
 import { MissingInformationComponent } from './structure-edition-summary/missing-information/missing-information.component';
 import { NoInformationComponent } from './structure-edition-summary/no-information/no-information.component';
 import { PersonalOfferComponent } from './profile-structure/personal-offer/personal-offer.component';
+import { PersonalOfferEditionComponent } from './personal-offer-edition/personal-offer-edition.component';
 
 @NgModule({
   declarations: [
@@ -27,6 +28,7 @@ import { PersonalOfferComponent } from './profile-structure/personal-offer/perso
     StructureMembersManagementComponent,
     StructuresManagementComponent,
     PersonalOfferComponent,
+    PersonalOfferEditionComponent,
   ],
   imports: [CommonModule, ProfileRoutingModule, SharedModule],
 })
diff --git a/src/app/profile/services/profile.service.ts b/src/app/profile/services/profile.service.ts
index 2ba4e114e..958238599 100644
--- a/src/app/profile/services/profile.service.ts
+++ b/src/app/profile/services/profile.service.ts
@@ -136,4 +136,9 @@ export class ProfileService {
         })
       );
   }
+
+  public async isPersonalOfferOwner(personalOfferId: string): Promise<boolean> {
+    await this.getProfile();
+    return this.currentProfile.personalOffers.includes(personalOfferId);
+  }
 }
diff --git a/src/app/resolvers/personal-offer.resolver.ts b/src/app/resolvers/personal-offer.resolver.ts
new file mode 100644
index 000000000..4ae34f051
--- /dev/null
+++ b/src/app/resolvers/personal-offer.resolver.ts
@@ -0,0 +1,52 @@
+import { PersonalOffer } from './../models/personalOffer.model';
+import { Injectable } from '@angular/core';
+import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router';
+import { forkJoin, Observable, Subject } from 'rxjs';
+import { catchError, map } from 'rxjs/operators';
+import { PersonalOfferService } from '../services/personal-offer.service';
+
+import { SearchService } from '../structure-list/services/search.service';
+import { Utils } from '../utils/utils';
+
+@Injectable()
+export class PersonalOfferResolver implements Resolve<PersonalOffer> {
+  public subject: Subject<PersonalOffer> = new Subject();
+
+  constructor(
+    private personalOfferService: PersonalOfferService,
+    private searchService: SearchService,
+    private router: Router,
+    public utils: Utils
+  ) {}
+
+  resolve(route: ActivatedRouteSnapshot): Observable<PersonalOffer> {
+    const personalOfferId = route.params.id;
+
+    if (personalOfferId) {
+      forkJoin({
+        personalOffer: this.personalOfferService.getPersonalOffer(personalOfferId).pipe(
+          map((res) => res),
+          catchError(() => {
+            this.router.navigate(['/home']);
+            return new Observable<PersonalOffer>();
+          })
+        ),
+        categories: this.searchService.getCategories(),
+      }).subscribe((res) => {
+        const personalOffer = this.utils.setServiceCategories(res.categories, res.personalOffer) as PersonalOffer;
+        // return res;
+        this.setSubject(personalOffer);
+      });
+
+      return this.getSubject();
+    }
+  }
+
+  setSubject(personalOffer: PersonalOffer): void {
+    this.subject.next(personalOffer);
+  }
+
+  getSubject(): Observable<PersonalOffer> {
+    return this.subject.asObservable();
+  }
+}
diff --git a/src/app/services/personal-offer.service.ts b/src/app/services/personal-offer.service.ts
index aa57be29b..19a7424f5 100644
--- a/src/app/services/personal-offer.service.ts
+++ b/src/app/services/personal-offer.service.ts
@@ -7,9 +7,14 @@ import { PersonalOffer } from '../models/personalOffer.model';
   providedIn: 'root',
 })
 export class PersonalOfferService {
+  private readonly baseUrl = 'api/personal-offers';
   constructor(private http: HttpClient) {}
 
   public createPersonalOffer(structureId: string, personalOffer: PersonalOffer): Observable<any> {
-    return this.http.post<any>(`api/personal-offers/`, { structureId: structureId, personalOffer: personalOffer });
+    return this.http.post<any>(this.baseUrl, { structureId: structureId, personalOffer: personalOffer });
+  }
+
+  public getPersonalOffer(id: string): Observable<PersonalOffer> {
+    return this.http.get<PersonalOffer>(`${this.baseUrl}/${id}`);
   }
 }
-- 
GitLab


From d8c89100999bba77bc4bd19efbe63d3e865e439c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Pailharey?= <rpailharey@grandlyon.com>
Date: Mon, 7 Nov 2022 17:16:33 +0100
Subject: [PATCH 02/12] feat: routing

---
 src/app/profile/profile-routing.module.ts | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/src/app/profile/profile-routing.module.ts b/src/app/profile/profile-routing.module.ts
index 2ddd5a0c6..0037e2709 100644
--- a/src/app/profile/profile-routing.module.ts
+++ b/src/app/profile/profile-routing.module.ts
@@ -1,15 +1,18 @@
 import { NgModule } from '@angular/core';
-import { Routes, RouterModule, Route } from '@angular/router';
+import { Route, RouterModule, Routes } from '@angular/router';
+import { FooterComponent } from '../footer/footer.component';
+import { AuthGuard } from '../guards/auth.guard';
+import { IsPersonalOfferOwnerGuard } from '../guards/isPersonalOfferOwner.guard';
 import { RoleGuard } from '../guards/role.guard';
+import { PersonalOfferResolver } from '../resolvers/personal-offer.resolver';
 import { StructureResolver } from '../resolvers/structure.resolver';
 import { RouteRole } from '../shared/enum/routeRole.enum';
-import { StructureMembersManagementComponent } from './structure-members-management/structure-members-management.component';
-import { StructuresManagementComponent } from './structures-management/structures-management.component';
+import { EditComponent } from './edit/edit.component';
+import { PersonalOfferEditionComponent } from './personal-offer-edition/personal-offer-edition.component';
 import { ProfileComponent } from './profile.component';
 import { StructureEditionSummaryComponent } from './structure-edition-summary/structure-edition-summary.component';
-import { AuthGuard } from '../guards/auth.guard';
-import { EditComponent } from './edit/edit.component';
-import { FooterComponent } from '../footer/footer.component';
+import { StructureMembersManagementComponent } from './structure-members-management/structure-members-management.component';
+import { StructuresManagementComponent } from './structures-management/structures-management.component';
 
 const footerOutletRoute: Route = {
   path: '',
@@ -50,6 +53,14 @@ const routes: Routes = [
       structure: StructureResolver,
     },
   },
+  {
+    path: 'edit-personal-offer/:id',
+    component: PersonalOfferEditionComponent,
+    canActivate: [IsPersonalOfferOwnerGuard],
+    resolve: {
+      personalOffer: PersonalOfferResolver,
+    },
+  },
   {
     path: 'structure-members-management/:id',
     component: StructureMembersManagementComponent,
-- 
GitLab


From 95e534944fc20de5a4173dcf1af1bbdd91b71fd1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Pailharey?= <rpailharey@grandlyon.com>
Date: Tue, 8 Nov 2022 10:20:23 +0100
Subject: [PATCH 03/12] refactor: accompanimentPicker

---
 ...ersonal-offer-accompaniment.component.html | 15 +------
 .../personal-offer-accompaniment.component.ts | 24 -----------
 src/app/profile/profile.module.ts             | 14 +++----
 .../accompaniment-picker.component.html       | 13 ++++++
 .../accompaniment-picker.component.scss       |  1 +
 .../accompaniment-picker.component.spec.ts    | 24 +++++++++++
 .../accompaniment-picker.component.ts         | 41 +++++++++++++++++++
 src/app/shared/components/index.ts            | 27 ++++++------
 .../training-type-picker.component.ts         |  1 +
 9 files changed, 104 insertions(+), 56 deletions(-)
 create mode 100644 src/app/shared/components/accompaniment-picker/accompaniment-picker.component.html
 create mode 100644 src/app/shared/components/accompaniment-picker/accompaniment-picker.component.scss
 create mode 100644 src/app/shared/components/accompaniment-picker/accompaniment-picker.component.spec.ts
 create mode 100644 src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts

diff --git a/src/app/form/form-view/personal-offer-form/personal-offer-accompaniment/personal-offer-accompaniment.component.html b/src/app/form/form-view/personal-offer-form/personal-offer-accompaniment/personal-offer-accompaniment.component.html
index 66fa0b65b..74bf05fec 100644
--- a/src/app/form/form-view/personal-offer-form/personal-offer-accompaniment/personal-offer-accompaniment.component.html
+++ b/src/app/form/form-view/personal-offer-form/personal-offer-accompaniment/personal-offer-accompaniment.component.html
@@ -5,17 +5,6 @@
     <p>Facultatif</p>
   </div>
 
-  <div fxLayout="column" fxLayoutGap="32px">
-    <div *ngIf="onlineProcedures" class="btn-grid">
-      <span *ngFor="let module of onlineProcedures.modules">
-        <app-button
-          [ngClass]="{ selectedChoice: true }"
-          [extraClass]="isSelectedModule(module) ? 'selected' : ''"
-          [style]="buttonTypeEnum.CheckButton"
-          [text]="module.name"
-          (action)="toogleResult(module)"
-        ></app-button>
-      </span>
-    </div>
-  </div>
+  <app-accompaniment-picker [personalOfferForm]="personalOfferForm" [onlineProcedures]="onlineProcedures">
+  </app-accompaniment-picker>
 </form>
diff --git a/src/app/form/form-view/personal-offer-form/personal-offer-accompaniment/personal-offer-accompaniment.component.ts b/src/app/form/form-view/personal-offer-form/personal-offer-accompaniment/personal-offer-accompaniment.component.ts
index 105c54b0d..9ee5fc01e 100644
--- a/src/app/form/form-view/personal-offer-form/personal-offer-accompaniment/personal-offer-accompaniment.component.ts
+++ b/src/app/form/form-view/personal-offer-form/personal-offer-accompaniment/personal-offer-accompaniment.component.ts
@@ -1,8 +1,6 @@
 import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
 import { UntypedFormGroup } from '@angular/forms';
-import { ButtonType } from '../../../../shared/components/button/buttonType.enum';
 import { Category } from '../../../../structure-list/models/category.model';
-import { Module } from '../../../../structure-list/models/module.model';
 
 @Component({
   selector: 'app-personal-offer-accompaniment',
@@ -15,29 +13,7 @@ export class PersonalOfferAccompanimentComponent implements OnInit {
   @Input() onlineProcedures: Category;
   @Output() validateForm = new EventEmitter<any>();
 
-  public buttonTypeEnum = ButtonType;
-  public selectedModules: Module[] = [];
-
-
   ngOnInit(): void {
     this.validateForm.emit();
   }
-
-  public toogleResult(module: Module): void {
-    if (this.isSelectedModule(module)) {
-      const index = this.selectedModules.findIndex((_module) => _module.id === module.id);
-      this.selectedModules.splice(index, 1);
-    } else {
-      this.selectedModules.push(module);
-    }
-    this.personalOfferForm
-      .get('categories')
-      .get('onlineProcedures')
-      .patchValue(this.selectedModules.map((module) => module.id));
-  }
-
-  public isSelectedModule(module: Module): boolean {
-    if (this.selectedModules && this.selectedModules.includes(module)) return true;
-    return false;
-  }
 }
diff --git a/src/app/profile/profile.module.ts b/src/app/profile/profile.module.ts
index 0890b9a58..52be3a2ed 100644
--- a/src/app/profile/profile.module.ts
+++ b/src/app/profile/profile.module.ts
@@ -1,19 +1,19 @@
+import { CommonModule } from '@angular/common';
 import { NgModule } from '@angular/core';
 import { SharedModule } from '../shared/shared.module';
-import { CommonModule } from '@angular/common';
+import { EditComponent } from './edit/edit.component';
+import { PersonalOfferEditionComponent } from './personal-offer-edition/personal-offer-edition.component';
 import { ProfileRoutingModule } from './profile-routing.module';
+import { PersonalOfferComponent } from './profile-structure/personal-offer/personal-offer.component';
 import { ProfileStructureMemberComponent } from './profile-structure/profile-structure-member/profile-structure-member.component';
 import { ProfileStructureComponent } from './profile-structure/profile-structure.component';
 import { ProfileComponent } from './profile.component';
-import { StructureEditionSummaryComponent } from './structure-edition-summary/structure-edition-summary.component';
-import { EditComponent } from './edit/edit.component';
-import { StructureMembersManagementComponent } from './structure-members-management/structure-members-management.component';
 import { StructureAddMemberModalComponent } from './structure-add-member-modal/structure-add-member-modal.component';
-import { StructuresManagementComponent } from './structures-management/structures-management.component';
 import { MissingInformationComponent } from './structure-edition-summary/missing-information/missing-information.component';
 import { NoInformationComponent } from './structure-edition-summary/no-information/no-information.component';
-import { PersonalOfferComponent } from './profile-structure/personal-offer/personal-offer.component';
-import { PersonalOfferEditionComponent } from './personal-offer-edition/personal-offer-edition.component';
+import { StructureEditionSummaryComponent } from './structure-edition-summary/structure-edition-summary.component';
+import { StructureMembersManagementComponent } from './structure-members-management/structure-members-management.component';
+import { StructuresManagementComponent } from './structures-management/structures-management.component';
 
 @NgModule({
   declarations: [
diff --git a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.html b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.html
new file mode 100644
index 000000000..d3b879918
--- /dev/null
+++ b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.html
@@ -0,0 +1,13 @@
+<div fxLayout="column" fxLayoutGap="32px">
+  <div *ngIf="onlineProcedures" class="btn-grid">
+    <span *ngFor="let module of onlineProcedures.modules">
+      <app-button
+        [ngClass]="{ selectedChoice: true }"
+        [extraClass]="isSelectedModule(module) ? 'selected' : ''"
+        [style]="buttonTypeEnum.CheckButton"
+        [text]="module.name"
+        (action)="toogleResult(module)"
+      ></app-button>
+    </span>
+  </div>
+</div>
diff --git a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.scss b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.scss
new file mode 100644
index 000000000..2ac0963fb
--- /dev/null
+++ b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.scss
@@ -0,0 +1 @@
+@import '../../../../assets/scss/buttons';
diff --git a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.spec.ts b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.spec.ts
new file mode 100644
index 000000000..109198b25
--- /dev/null
+++ b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.spec.ts
@@ -0,0 +1,24 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AccompanimentPickerComponent } from './accompaniment-picker.component';
+
+describe('AccompanimentPickerComponent', () => {
+  let component: AccompanimentPickerComponent;
+  let fixture: ComponentFixture<AccompanimentPickerComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [AccompanimentPickerComponent],
+    }).compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(AccompanimentPickerComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts
new file mode 100644
index 000000000..39a1297d0
--- /dev/null
+++ b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts
@@ -0,0 +1,41 @@
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { UntypedFormGroup } from '@angular/forms';
+import { ButtonType } from '../button/buttonType.enum';
+import { Category } from '../../../structure-list/models/category.model';
+import { Module } from '../../../structure-list/models/module.model';
+
+@Component({
+  selector: 'app-accompaniment-picker',
+  templateUrl: './accompaniment-picker.component.html',
+  styleUrls: ['./accompaniment-picker.component.scss'],
+})
+export class AccompanimentPickerComponent implements OnInit {
+  @Input() personalOfferForm: UntypedFormGroup;
+  @Input() onlineProcedures: Category;
+  @Output() validateForm = new EventEmitter<any>();
+
+  public buttonTypeEnum = ButtonType;
+  public selectedModules: Module[] = [];
+
+  ngOnInit(): void {
+    this.validateForm.emit();
+  }
+
+  public toogleResult(module: Module): void {
+    if (this.isSelectedModule(module)) {
+      const index = this.selectedModules.findIndex((_module) => _module.id === module.id);
+      this.selectedModules.splice(index, 1);
+    } else {
+      this.selectedModules.push(module);
+    }
+    this.personalOfferForm
+      .get('categories')
+      .get('onlineProcedures')
+      .patchValue(this.selectedModules.map((module) => module.id));
+  }
+
+  public isSelectedModule(module: Module): boolean {
+    if (this.selectedModules && this.selectedModules.includes(module)) return true;
+    return false;
+  }
+}
diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts
index 1c8245ed8..4fde07078 100644
--- a/src/app/shared/components/index.ts
+++ b/src/app/shared/components/index.ts
@@ -1,26 +1,28 @@
-import { ButtonComponent } from './button/button.component';
-import { LogoCardComponent } from './logo-card/logo-card.component';
-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 { AccompanimentPickerComponent } from './accompaniment-picker/accompaniment-picker.component';
+import { InformationStepComponent } from '../../form/form-view/global-components/information-step/information-step.component';
 import { AddressAutocompleteComponent } from './address-autocomplete/address-autocomplete.component';
-import { StructureTypePickerComponent } from './structure-type-picker/structure-type-picker.component';
+import { ButtonComponent } from './button/button.component';
 import { CheckboxFormComponent } from './checkbox-form/checkbox-form.component';
-import { HourPickerComponent } from './hour-picker/hour-picker.component';
+import { CreateAccountFormComponent } from './create-account-form/create-account-form.component';
+import { CustomModalComponent } from './custom-modal/custom-modal.component';
 import { CopyPasteComponent } from './hour-picker/copy-paste/copy-paste.component';
-import { RadioFormComponent } from './radio-form/radio-form.component';
+import { HourPickerComponent } from './hour-picker/hour-picker.component';
+import { LogoCardComponent } from './logo-card/logo-card.component';
 import { ModalConfirmationComponent } from './modal-confirmation/modal-confirmation.component';
-import { CustomModalComponent } from './custom-modal/custom-modal.component';
 import { ModalJoinConfirmationComponent } from './modal-join-confirmation/modal-join-confirmation.component';
-import { StructureOptionsModalComponent } from './structure-options-modal/structure-options-modal.component';
 import { ModalOptionsComponent } from './modal-options/modal-options.component';
-import { TextInputModalComponent } from './text-input-modal/text-input-modal.component';
 import { PasswordFormComponent } from './password-form/password-form.component';
+import { RadioFormComponent } from './radio-form/radio-form.component';
+import { StructureOptionsModalComponent } from './structure-options-modal/structure-options-modal.component';
+import { StructureTypePickerComponent } from './structure-type-picker/structure-type-picker.component';
+import { SvgIconComponent } from './svg-icon/svg-icon.component';
+import { TextInputModalComponent } from './text-input-modal/text-input-modal.component';
 import { TrainingTypePickerComponent } from './training-type-picker/training-type-picker.component';
-import { InformationStepComponent } from '../../form/form-view/global-components/information-step/information-step.component';
+import { ValidatorFormComponent } from './validator-form/validator-form.component';
 
 // tslint:disable-next-line: max-line-length
 export {
+  AccompanimentPickerComponent,
   LogoCardComponent,
   SvgIconComponent,
   ButtonComponent,
@@ -43,6 +45,7 @@ export {
 
 // tslint:disable-next-line:variable-name
 export const SharedComponents = [
+  AccompanimentPickerComponent,
   LogoCardComponent,
   SvgIconComponent,
   ButtonComponent,
diff --git a/src/app/shared/components/training-type-picker/training-type-picker.component.ts b/src/app/shared/components/training-type-picker/training-type-picker.component.ts
index 77f4c990e..f146c444c 100644
--- a/src/app/shared/components/training-type-picker/training-type-picker.component.ts
+++ b/src/app/shared/components/training-type-picker/training-type-picker.component.ts
@@ -83,6 +83,7 @@ export class TrainingTypePickerComponent implements OnInit {
         if (!this.isModulePicked(categorie, module)) this.selectedChoices[index].modules.push(module);
       }
     }
+    this.selectedType.emit(this.selectedChoices);
   }
 
   public getCategoryCheckboxStatus(c: Category): string {
-- 
GitLab


From 00f3373cad15fae6f1890da7a448b889fc38b291 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Pailharey?= <rpailharey@grandlyon.com>
Date: Tue, 8 Nov 2022 14:38:05 +0100
Subject: [PATCH 04/12] feat: init pickers

---
 .../personal-offer-form.component.html        |   1 +
 .../personal-offer-form.component.ts          |   3 +-
 .../personal-offer-edition.component.html     |  59 ++++++-
 .../personal-offer-edition.component.scss     | 165 ++++++++++++++++++
 .../personal-offer-edition.component.ts       |  68 +++++++-
 .../profile-structure.component.html          |   4 +-
 .../accompaniment-picker.component.ts         |   4 +
 7 files changed, 290 insertions(+), 14 deletions(-)

diff --git a/src/app/form/form-view/personal-offer-form/personal-offer-form.component.html b/src/app/form/form-view/personal-offer-form/personal-offer-form.component.html
index b1cba7b30..9f94b3b6e 100644
--- a/src/app/form/form-view/personal-offer-form/personal-offer-form.component.html
+++ b/src/app/form/form-view/personal-offer-form/personal-offer-form.component.html
@@ -1,6 +1,7 @@
 <div class="no-max-width">
   <ng-container *ngIf="currentStep === personalOfferFormStep.personalOfferAccompaniment">
     <app-personal-offer-accompaniment
+      *ngIf="onlineProcedures"
       [structureName]="structureName"
       [personalOfferForm]="personalOfferForm"
       [onlineProcedures]="onlineProcedures"
diff --git a/src/app/form/form-view/personal-offer-form/personal-offer-form.component.ts b/src/app/form/form-view/personal-offer-form/personal-offer-form.component.ts
index 6b8c88fa4..e7237614f 100644
--- a/src/app/form/form-view/personal-offer-form/personal-offer-form.component.ts
+++ b/src/app/form/form-view/personal-offer-form/personal-offer-form.component.ts
@@ -27,9 +27,10 @@ export class PersonalOfferFormComponent implements OnChanges, OnInit {
 
   constructor(private searchService: SearchService, private router: Router) {}
 
-  ngOnInit(): void {
+  async ngOnInit(): Promise<void> {
     this.setCategories();
   }
+
   ngOnChanges(changes: SimpleChanges): void {
     if (changes.currentStep) {
       if (
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.html b/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
index 3ac215dbb..9219363bb 100644
--- a/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
@@ -1,5 +1,54 @@
-<p>personal-offer-edition works!</p>
-<p>{{ this.personalOffer._id }}</p>
-<p>{{ this.personalOffer.categoriesDisplay.onlineProcedures }}</p>
-<p>{{ this.personalOffer.categoriesDisplay.baseSkills }}</p>
-<p>{{ this.personalOffer.categoriesDisplay.advancedSkills }}</p>
+<div fxLayout="column" class="content-container full-screen">
+  <div class="edit-profile">
+    <div class="header">
+      <div class="title">
+        <a routerLink="/profile">
+          <svg aria-hidden="true">
+            <use [attr.xlink:href]="'assets/ico/sprite.svg#arrowBack'"></use>
+          </svg>
+        </a>
+        <h1>Gérer mon offre de service</h1>
+      </div>
+    </div>
+    <!-- Navigation -->
+    <div class="navigation">
+      <span
+        [ngClass]="{ tab: true, selected: currentTab === tabsEnum.onlineProcedures }"
+        (click)="navigateTo(tabsEnum.onlineProcedures)"
+        >Démarches en ligne</span
+      >
+      <span
+        [ngClass]="{ tab: true, selected: currentTab === tabsEnum.digitalSkills }"
+        (click)="navigateTo(tabsEnum.digitalSkills)"
+        >Compétences numériques</span
+      >
+    </div>
+    <!-- Content of tabs -->
+    <div class="content">
+      <div *ngIf="currentTab === tabsEnum.onlineProcedures">
+        <app-accompaniment-picker
+          *ngIf="personalOfferForm"
+          [personalOfferForm]="personalOfferForm"
+          [onlineProcedures]="onlineProcedures"
+        ></app-accompaniment-picker>
+      </div>
+
+      <div *ngIf="currentTab === tabsEnum.digitalSkills">
+        <app-training-type-picker
+          *ngIf="personalOfferForm"
+          [baseSkills]="personalOfferForm.get('categories').get('baseSkills').value"
+          [advancedSkills]="personalOfferForm.get('categories').get('advancedSkills').value"
+          [trainingCategories]="trainingCategories"
+          (selectedType)="setTrainingsFromCategories($event)"
+        ></app-training-type-picker>
+      </div>
+    </div>
+
+    <!-- Footer -->
+    <div class="footer">
+      <app-button [text]="'Annuler'" (action)="cancel()"></app-button>
+      <app-button [text]="'Valider'" [disabled]="!isPageValid()" (action)="confirm()" [style]="buttonTypeEnum.Primary">
+      </app-button>
+    </div>
+  </div>
+</div>
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.scss b/src/app/profile/personal-offer-edition/personal-offer-edition.component.scss
index e69de29bb..e653a2fac 100644
--- a/src/app/profile/personal-offer-edition/personal-offer-edition.component.scss
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.scss
@@ -0,0 +1,165 @@
+@import '../../../assets/scss/color';
+@import '../../../assets/scss/typography';
+@import '../../../assets/scss/hyperlink';
+@import '../../../assets/scss/shapes';
+@import '../../../assets/scss/breakpoint';
+
+.edit-profile {
+  display: flex;
+  flex-direction: column;
+  flex: 1;
+  max-width: 980px;
+  width: 100%;
+  margin: 16px auto 8px auto;
+  padding: 40px;
+  padding-bottom: 0px;
+  background: $white;
+  border: 1px solid $grey-6;
+  border-radius: 8px;
+
+  @media #{$tablet} {
+    margin: 0px 4px 4px 4px;
+    padding: 16px;
+    padding-bottom: 0px;
+    width: auto;
+  }
+
+  .header,
+  .navigation,
+  .title {
+    display: flex;
+  }
+
+  .header {
+    justify-content: space-between;
+    padding-bottom: 18px;
+    align-items: center;
+
+    .title {
+      align-items: center;
+      h1 {
+        color: $grey-1;
+        font-weight: lighter;
+        @media #{$tablet} {
+          @include lato-regular-20;
+        }
+      }
+      svg {
+        stroke: $black;
+        height: 40px;
+        width: 40px;
+        margin-right: 14px;
+        cursor: pointer;
+      }
+    }
+
+    .deleteAccount {
+      ::ng-deep {
+        svg {
+          height: 22px;
+          width: 22px;
+          margin-right: 4px;
+        }
+        span {
+          color: $red;
+          @include lato-regular-14;
+        }
+      }
+    }
+  }
+
+  .navigation {
+    justify-content: flex-start;
+    overflow-x: auto;
+    white-space: nowrap;
+
+    border-bottom: 1px solid $grey-4;
+
+    &::-webkit-scrollbar {
+      height: 8px;
+      margin-right: 4px;
+    }
+
+    .tab {
+      color: $grey-3;
+      margin-right: 40px;
+      padding-bottom: 8px;
+      &:hover {
+        cursor: pointer;
+      }
+      &.selected {
+        color: $grey-1;
+        border-bottom: 2px solid $grey-1;
+      }
+    }
+  }
+
+  .content {
+    padding-top: 24px;
+    flex: 1;
+    max-width: 600px;
+    p.subTitle {
+      @include lato-regular-16;
+      text-align: left;
+      margin-top: 0;
+      margin-bottom: 4px;
+    }
+
+    .credentialsTab {
+      .buttons {
+        margin-top: 25px;
+        display: flex;
+        flex-wrap: wrap;
+        gap: 18px;
+        ::ng-deep {
+          svg {
+            height: 22px;
+            width: 22px;
+            margin-right: 4px;
+          }
+        }
+      }
+    }
+    .credentialsTab ::ng-deep .secondary {
+      width: 220px !important;
+    }
+    .credentialsTab ::ng-deep .secondary .text {
+      place-content: center center !important;
+    }
+
+    .descriptionTab {
+      p.descriptionLength {
+        text-align: right;
+        @include lato-regular-14;
+        color: $grey-3;
+        font-style: italic;
+        margin-top: 8px;
+      }
+    }
+    .search-structure input {
+      width: 600px;
+    }
+    .structureResults {
+      position: absolute;
+      width: 600px;
+      z-index: 1;
+    }
+  }
+
+  .footer {
+    padding: 16px;
+    display: flex;
+    gap: 24px;
+    justify-content: center;
+    flex-wrap: wrap;
+    border-top: 1px solid $grey-4;
+    //To fit border to parent div
+    margin: 0 -40px;
+    @media #{$tablet} {
+      margin: 0 -16px;
+    }
+    ::ng-deep div svg {
+      height: 22px;
+    }
+  }
+}
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts b/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
index 604988c76..cbcc17b3e 100644
--- a/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
@@ -1,6 +1,17 @@
-import { PersonalOffer } from './../../models/personalOffer.model';
-import { Component, OnInit } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
+import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
 import { ActivatedRoute } from '@angular/router';
+import { CategoriesToggle } from '../../models/categoriesToggle.model';
+import { ButtonType } from '../../shared/components/button/buttonType.enum';
+import { CategoryEnum } from '../../shared/enum/category.enum';
+import { Category } from '../../structure-list/models/category.model';
+import { SearchService } from '../../structure-list/services/search.service';
+import { PersonalOffer } from './../../models/personalOffer.model';
+
+enum tabsEnum {
+  onlineProcedures,
+  digitalSkills,
+}
 
 @Component({
   selector: 'app-personal-offer-edition',
@@ -8,16 +19,61 @@ import { ActivatedRoute } from '@angular/router';
   styleUrls: ['./personal-offer-edition.component.scss'],
 })
 export class PersonalOfferEditionComponent implements OnInit {
-  public personalOffer: PersonalOffer;
+  public tabsEnum = tabsEnum;
+  public buttonTypeEnum = ButtonType;
+  public currentTab: tabsEnum = tabsEnum.onlineProcedures;
+  public personalOfferForm: UntypedFormGroup = null;
+  public onlineProcedures: Category;
+  public trainingCategories: CategoriesToggle[] = [];
 
-  constructor(private route: ActivatedRoute) {}
+  @Input() personalOffer: PersonalOffer;
+
+  constructor(private route: ActivatedRoute, private searchService: SearchService) {}
 
   ngOnInit(): void {
-    this.route.data.subscribe((data) => {
+    this.route.data.subscribe(async (data) => {
       if (data.personalOffer) {
+        await this.setCategories();
         this.personalOffer = data.personalOffer;
-        console.log(this.personalOffer);
+        this.personalOfferForm = new UntypedFormGroup({
+          categories: new UntypedFormGroup({
+            onlineProcedures: new UntypedFormControl(this.personalOffer.categories.onlineProcedures),
+            baseSkills: new UntypedFormControl(this.personalOffer.categories.baseSkills),
+            advancedSkills: new UntypedFormControl(this.personalOffer.categories.advancedSkills),
+          }),
+        });
       }
     });
   }
+
+  async setCategories(): Promise<void> {
+    const categories = await this.searchService.getCategories().toPromise();
+    categories.forEach((categ) => {
+      switch (categ.id) {
+        case CategoryEnum.onlineProcedures: {
+          this.onlineProcedures = categ;
+          break;
+        }
+        case CategoryEnum.baseSkills:
+        case CategoryEnum.advancedSkills: {
+          this.trainingCategories.push({ category: categ, openned: false });
+          break;
+        }
+        default:
+          break;
+      }
+    });
+  }
+
+  public navigateTo(tab: tabsEnum): void {
+    this.currentTab = tab;
+  }
+
+  public cancel(): void {}
+
+  public isPageValid(): boolean {
+    return true;
+  }
+
+  public confirm(): void {}
 }
diff --git a/src/app/profile/profile-structure/profile-structure.component.html b/src/app/profile/profile-structure/profile-structure.component.html
index 37d544242..f824d3deb 100644
--- a/src/app/profile/profile-structure/profile-structure.component.html
+++ b/src/app/profile/profile-structure/profile-structure.component.html
@@ -90,8 +90,6 @@
           </div>
         </div>
       </div>
-      <app-personal-offer *ngIf="this.personalOffer" [personalOffer]="personalOffer" [isPublic]="isPublic">
-      </app-personal-offer>
       <div *ngIf="members.length > 0" fxLayout="column" fxLayoutGap="9px">
         <div fxLayout="row" fxLayoutAlign="space-between center">
           <p class="section-title">membres</p>
@@ -119,6 +117,8 @@
           <app-profile-structure-member [member]="member"></app-profile-structure-member>
         </div>
       </div>
+      <app-personal-offer *ngIf="this.personalOffer" [personalOffer]="personalOffer" [isPublic]="isPublic">
+      </app-personal-offer>
       <div class="call-to-action" *ngIf="!isPublic && members.length === 0" fxLayout="row" fxLayoutAlign="center">
         <app-button
           [type]="'button'"
diff --git a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts
index 39a1297d0..c7cf22840 100644
--- a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts
+++ b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts
@@ -1,3 +1,4 @@
+import { filter } from 'rxjs/operators';
 import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
 import { UntypedFormGroup } from '@angular/forms';
 import { ButtonType } from '../button/buttonType.enum';
@@ -18,6 +19,9 @@ export class AccompanimentPickerComponent implements OnInit {
   public selectedModules: Module[] = [];
 
   ngOnInit(): void {
+    this.selectedModules = this.onlineProcedures.modules.filter((module) =>
+      this.personalOfferForm.get('categories').get('onlineProcedures').value.includes(module.id)
+    );
     this.validateForm.emit();
   }
 
-- 
GitLab


From 5b56eb4280838c6d5ca091038e6b9e3026e11288 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Pailharey?= <rpailharey@grandlyon.com>
Date: Wed, 9 Nov 2022 12:45:12 +0100
Subject: [PATCH 05/12] feat: cancel and validate

---
 .../personal-offer-edition.component.html     |  9 ++-
 .../personal-offer-edition.component.ts       | 61 ++++++++++++++-----
 src/app/services/personal-offer.service.ts    |  7 +++
 .../accompaniment-picker.component.ts         | 12 +++-
 .../training-type-picker.component.ts         | 12 +++-
 5 files changed, 79 insertions(+), 22 deletions(-)

diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.html b/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
index 9219363bb..c23920854 100644
--- a/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
@@ -46,8 +46,13 @@
 
     <!-- Footer -->
     <div class="footer">
-      <app-button [text]="'Annuler'" (action)="cancel()"></app-button>
-      <app-button [text]="'Valider'" [disabled]="!isPageValid()" (action)="confirm()" [style]="buttonTypeEnum.Primary">
+      <app-button [text]="'Annuler'" (action)="cancel()" [disabled]="personalOfferForm?.pristine"></app-button>
+      <app-button
+        [text]="'Valider'"
+        (action)="confirm()"
+        [style]="buttonTypeEnum.Primary"
+        [disabled]="personalOfferForm?.pristine"
+      >
       </app-button>
     </div>
   </div>
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts b/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
index cbcc17b3e..7834d337e 100644
--- a/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
@@ -1,12 +1,14 @@
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
 import { ActivatedRoute } from '@angular/router';
 import { CategoriesToggle } from '../../models/categoriesToggle.model';
+import { NotificationService } from '../../services/notification.service';
 import { ButtonType } from '../../shared/components/button/buttonType.enum';
 import { CategoryEnum } from '../../shared/enum/category.enum';
 import { Category } from '../../structure-list/models/category.model';
 import { SearchService } from '../../structure-list/services/search.service';
 import { PersonalOffer } from './../../models/personalOffer.model';
+import { PersonalOfferService } from './../../services/personal-offer.service';
 
 enum tabsEnum {
   onlineProcedures,
@@ -19,33 +21,44 @@ enum tabsEnum {
   styleUrls: ['./personal-offer-edition.component.scss'],
 })
 export class PersonalOfferEditionComponent implements OnInit {
-  public tabsEnum = tabsEnum;
   public buttonTypeEnum = ButtonType;
+  public tabsEnum = tabsEnum;
   public currentTab: tabsEnum = tabsEnum.onlineProcedures;
+
+  public personalOffer: PersonalOffer;
   public personalOfferForm: UntypedFormGroup = null;
+  private initialPersonalOffer;
   public onlineProcedures: Category;
   public trainingCategories: CategoriesToggle[] = [];
 
-  @Input() personalOffer: PersonalOffer;
-
-  constructor(private route: ActivatedRoute, private searchService: SearchService) {}
+  constructor(
+    private route: ActivatedRoute,
+    private searchService: SearchService,
+    private personalOfferService: PersonalOfferService,
+    private notificationService: NotificationService
+  ) {}
 
   ngOnInit(): void {
     this.route.data.subscribe(async (data) => {
       if (data.personalOffer) {
         await this.setCategories();
         this.personalOffer = data.personalOffer;
-        this.personalOfferForm = new UntypedFormGroup({
-          categories: new UntypedFormGroup({
-            onlineProcedures: new UntypedFormControl(this.personalOffer.categories.onlineProcedures),
-            baseSkills: new UntypedFormControl(this.personalOffer.categories.baseSkills),
-            advancedSkills: new UntypedFormControl(this.personalOffer.categories.advancedSkills),
-          }),
-        });
+        this.personalOfferForm = this.createPersonalOfferForm(this.personalOffer);
+        this.initialPersonalOffer = this.personalOfferForm.value;
       }
     });
   }
 
+  private createPersonalOfferForm(personalOfferState): UntypedFormGroup {
+    return new UntypedFormGroup({
+      categories: new UntypedFormGroup({
+        onlineProcedures: new UntypedFormControl(personalOfferState.categories.onlineProcedures),
+        baseSkills: new UntypedFormControl(personalOfferState.categories.baseSkills),
+        advancedSkills: new UntypedFormControl(personalOfferState.categories.advancedSkills),
+      }),
+    });
+  }
+
   async setCategories(): Promise<void> {
     const categories = await this.searchService.getCategories().toPromise();
     categories.forEach((categ) => {
@@ -69,11 +82,27 @@ export class PersonalOfferEditionComponent implements OnInit {
     this.currentTab = tab;
   }
 
-  public cancel(): void {}
+  public cancel(): void {
+    this.personalOfferForm = this.createPersonalOfferForm(this.initialPersonalOffer);
+  }
 
-  public isPageValid(): boolean {
-    return true;
+  public confirm(): void {
+    this.personalOfferService
+      .updatePersonalOffer(this.personalOffer._id, this.personalOfferForm.get('categories').value)
+      .subscribe(() => {
+        this.notificationService.showSuccess('Vos informations ont bien été enregistrées');
+        this.initialPersonalOffer = this.personalOfferForm.value;
+        this.personalOfferForm.markAsPristine();
+      });
   }
 
-  public confirm(): void {}
+  public setTrainingsFromCategories(categories: Category[]) {
+    for (const categorie of categories) {
+      const moduleIds: string[] = categorie.modules.map((module) => module.id);
+      if (this.personalOfferForm.get('categories').get(categorie.id)) {
+        this.personalOfferForm.get('categories').get(categorie.id).patchValue(moduleIds);
+        this.personalOfferForm.get('categories').get(categorie.id).markAsDirty();
+      }
+    }
+  }
 }
diff --git a/src/app/services/personal-offer.service.ts b/src/app/services/personal-offer.service.ts
index 19a7424f5..32eefcd4a 100644
--- a/src/app/services/personal-offer.service.ts
+++ b/src/app/services/personal-offer.service.ts
@@ -2,6 +2,7 @@ import { HttpClient } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 import { Observable } from 'rxjs';
 import { PersonalOffer } from '../models/personalOffer.model';
+import { Category } from '../structure-list/models/category.model';
 
 @Injectable({
   providedIn: 'root',
@@ -17,4 +18,10 @@ export class PersonalOfferService {
   public getPersonalOffer(id: string): Observable<PersonalOffer> {
     return this.http.get<PersonalOffer>(`${this.baseUrl}/${id}`);
   }
+
+  public updatePersonalOffer(id: string, categories: Category[]): Observable<any> {
+    return this.http.put<PersonalOffer>(`${this.baseUrl}/${id}`, {
+      categories: categories,
+    });
+  }
 }
diff --git a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts
index c7cf22840..764fd9f27 100644
--- a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts
+++ b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts
@@ -1,5 +1,5 @@
 import { filter } from 'rxjs/operators';
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { Component, EventEmitter, Input, OnInit, Output, OnChanges, SimpleChanges } from '@angular/core';
 import { UntypedFormGroup } from '@angular/forms';
 import { ButtonType } from '../button/buttonType.enum';
 import { Category } from '../../../structure-list/models/category.model';
@@ -10,7 +10,7 @@ import { Module } from '../../../structure-list/models/module.model';
   templateUrl: './accompaniment-picker.component.html',
   styleUrls: ['./accompaniment-picker.component.scss'],
 })
-export class AccompanimentPickerComponent implements OnInit {
+export class AccompanimentPickerComponent implements OnInit, OnChanges {
   @Input() personalOfferForm: UntypedFormGroup;
   @Input() onlineProcedures: Category;
   @Output() validateForm = new EventEmitter<any>();
@@ -19,6 +19,13 @@ export class AccompanimentPickerComponent implements OnInit {
   public selectedModules: Module[] = [];
 
   ngOnInit(): void {
+    this.initSelectedModule();
+  }
+  ngOnChanges(): void {
+    this.initSelectedModule();
+  }
+
+  private initSelectedModule() {
     this.selectedModules = this.onlineProcedures.modules.filter((module) =>
       this.personalOfferForm.get('categories').get('onlineProcedures').value.includes(module.id)
     );
@@ -36,6 +43,7 @@ export class AccompanimentPickerComponent implements OnInit {
       .get('categories')
       .get('onlineProcedures')
       .patchValue(this.selectedModules.map((module) => module.id));
+    this.personalOfferForm.get('categories').get('onlineProcedures').markAsDirty();
   }
 
   public isSelectedModule(module: Module): boolean {
diff --git a/src/app/shared/components/training-type-picker/training-type-picker.component.ts b/src/app/shared/components/training-type-picker/training-type-picker.component.ts
index f146c444c..5ddce8b86 100644
--- a/src/app/shared/components/training-type-picker/training-type-picker.component.ts
+++ b/src/app/shared/components/training-type-picker/training-type-picker.component.ts
@@ -1,4 +1,4 @@
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { Component, EventEmitter, Input, OnInit, Output, OnChanges, SimpleChanges } from '@angular/core';
 import { cloneDeep, remove } from 'lodash';
 import { CategoriesToggle } from 'src/app/models/categoriesToggle.model';
 import { Category } from '../../../structure-list/models/category.model';
@@ -11,7 +11,7 @@ import { ButtonType } from '../button/buttonType.enum';
   templateUrl: './training-type-picker.component.html',
   styleUrls: ['./training-type-picker.component.scss'],
 })
-export class TrainingTypePickerComponent implements OnInit {
+export class TrainingTypePickerComponent implements OnInit, OnChanges {
   @Input() public baseSkills: string[];
   @Input() public advancedSkills: string[];
   @Input() public trainingCategories: CategoriesToggle[];
@@ -26,6 +26,14 @@ export class TrainingTypePickerComponent implements OnInit {
     this.trainingCategories.forEach((data) => {
       this.categories.push(data.category);
     });
+    this.initSelectedChoice();
+  }
+
+  ngOnChanges(): void {
+    this.initSelectedChoice();
+  }
+
+  private initSelectedChoice() {
     this.selectedChoices = cloneDeep(this.categories);
     this.selectedChoices.forEach((category) => {
       let selectedModulesId: string[] = [];
-- 
GitLab


From 9e84f652479eebf4a488c0723314dd6bd0907461 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Pailharey?= <rpailharey@grandlyon.com>
Date: Wed, 9 Nov 2022 16:55:35 +0100
Subject: [PATCH 06/12] wip: create offer link

---
 src/app/form/form-view/guards/personalOffer.guard.ts           | 3 ++-
 .../profile/profile-structure/profile-structure.component.html | 2 +-
 .../profile/profile-structure/profile-structure.component.ts   | 3 +++
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/app/form/form-view/guards/personalOffer.guard.ts b/src/app/form/form-view/guards/personalOffer.guard.ts
index d7fc5a8bf..67f422064 100644
--- a/src/app/form/form-view/guards/personalOffer.guard.ts
+++ b/src/app/form/form-view/guards/personalOffer.guard.ts
@@ -10,7 +10,8 @@ export class PersonalOfferGuard implements CanActivate {
   canActivate(route: ActivatedRouteSnapshot): UrlTree | boolean {
     if (
       route.routeConfig.path === 'personaloffer' &&
-      (this.router.routerState.snapshot.url === '/form/profile' ||
+      (this.router.routerState.snapshot.url === '/profile' ||
+        this.router.routerState.snapshot.url === '/form/profile' ||
         this.router.routerState.snapshot.url === '/form/structure')
     ) {
       return true;
diff --git a/src/app/profile/profile-structure/profile-structure.component.html b/src/app/profile/profile-structure/profile-structure.component.html
index f824d3deb..d98717637 100644
--- a/src/app/profile/profile-structure/profile-structure.component.html
+++ b/src/app/profile/profile-structure/profile-structure.component.html
@@ -136,7 +136,7 @@
           [text]="'Ajouter une offre'"
           [style]="buttonTypeEnum.SecondaryUltraWide"
           [routerLinkActive]="'active'"
-          [disabled]="true"
+          (click)="goToOffer()"
         ></app-button>
       </div>
     </div>
diff --git a/src/app/profile/profile-structure/profile-structure.component.ts b/src/app/profile/profile-structure/profile-structure.component.ts
index a036f6366..611e9ae3b 100644
--- a/src/app/profile/profile-structure/profile-structure.component.ts
+++ b/src/app/profile/profile-structure/profile-structure.component.ts
@@ -84,6 +84,9 @@ export class ProfileStructureComponent implements OnInit {
   public goToEdit(step: structureFormStep): void {
     this.router.navigate(['/form/structure', this.structureWithOwners.structure._id, structureFormStep[step]]);
   }
+  public goToOffer(): void {
+    this.router.navigate(['/form/personaloffer'], { state: { structure: this.structure } });
+  }
   public isValid(): boolean {
     return this.structureForm.valid;
   }
-- 
GitLab


From 645d882975f734a47b4155c0812011eca5aee815 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Pailharey?= <rpailharey@grandlyon.com>
Date: Mon, 14 Nov 2022 11:29:10 +0100
Subject: [PATCH 07/12] feat: add new personal offer

---
 src/app/form/form-view/form-view.component.ts             | 8 ++++++--
 .../progress-bar/progress-bar.component.ts                | 1 -
 .../profile-structure/profile-structure.component.ts      | 2 +-
 3 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/app/form/form-view/form-view.component.ts b/src/app/form/form-view/form-view.component.ts
index 68e2abb9b..6f7adc684 100644
--- a/src/app/form/form-view/form-view.component.ts
+++ b/src/app/form/form-view/form-view.component.ts
@@ -92,6 +92,10 @@ export class FormViewComponent implements OnInit, AfterViewInit {
   }
 
   async ngOnInit(): Promise<void> {
+    // Get structure from history (this is used to create personal offer from profile)
+    if (history.state.structure) {
+      this.structure = history.state.structure;
+    }
     this.routeParam = this.router.routerState.snapshot.url.split('/')[2];
 
     this.initPage();
@@ -128,9 +132,9 @@ export class FormViewComponent implements OnInit, AfterViewInit {
 
   private initPage(): void {
     const profileFormSteps: number = Object.keys(profileFormStep).length / 2;
-    const personnalOfferFormSteps: number = Object.keys(personalOfferFormStep).length / 2 - 1;
+    const personalOfferFormSteps: number = Object.keys(personalOfferFormStep).length / 2 - 1;
     const structureFormSteps: number = Object.keys(structureFormStep).length / 2;
-    const totalFormSteps: number = profileFormSteps + personnalOfferFormSteps + structureFormSteps;
+    const totalFormSteps: number = profileFormSteps + personalOfferFormSteps + structureFormSteps;
     if (formType[this.routeParam] === formType.account) {
       this.nbSteps = 3;
       this.currentPage = accountFormStep.accountInfo;
diff --git a/src/app/form/form-view/global-components/progress-bar/progress-bar.component.ts b/src/app/form/form-view/global-components/progress-bar/progress-bar.component.ts
index 0f67bb3c7..a7ad96e43 100644
--- a/src/app/form/form-view/global-components/progress-bar/progress-bar.component.ts
+++ b/src/app/form/form-view/global-components/progress-bar/progress-bar.component.ts
@@ -17,7 +17,6 @@ export class ProgressBarComponent implements OnChanges {
   public progressStatus: number;
   public formTypeEnum = formType;
   public profileFormSteps: number = Object.keys(profileFormStep).length / 2;
-  public personnalOfferFormSteps: number = Object.keys(personalOfferFormStep).length / 2;
   public structureFormSteps: number = Object.keys(structureFormStep).length / 2;
 
   ngOnChanges(changes: SimpleChanges): void {
diff --git a/src/app/profile/profile-structure/profile-structure.component.ts b/src/app/profile/profile-structure/profile-structure.component.ts
index 611e9ae3b..145856861 100644
--- a/src/app/profile/profile-structure/profile-structure.component.ts
+++ b/src/app/profile/profile-structure/profile-structure.component.ts
@@ -67,7 +67,7 @@ export class ProfileStructureComponent implements OnInit {
     if (!this.userProfile.job.hasPersonalOffer || this.userProfile.personalOffers.length === 0) return null;
     // Check if structure has personal offers
     if (this.structure.personalOffers.length === 0) return null;
-    // Return personnal offer if the user has one in this structure
+    // Return personal offer if the user has one in this structure
     const personalOffer = this.structure.personalOffers.filter((structureOffer) =>
       this.userProfile.personalOffers.includes(structureOffer._id)
     )[0];
-- 
GitLab


From b9eb8b28e1314fc54ae32ff5b4d55e9b03988f25 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Pailharey?= <rpailharey@grandlyon.com>
Date: Mon, 14 Nov 2022 13:51:33 +0100
Subject: [PATCH 08/12] feat: added button icons

---
 src/app/profile/edit/edit.component.html              | 11 +++++++----
 .../personal-offer-edition.component.html             |  8 +++++++-
 2 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/app/profile/edit/edit.component.html b/src/app/profile/edit/edit.component.html
index 2c2d53817..5da29150e 100644
--- a/src/app/profile/edit/edit.component.html
+++ b/src/app/profile/edit/edit.component.html
@@ -202,9 +202,10 @@
 
     <!-- Footer -->
     <div class="footer" *ngIf="currentTab !== tabsEnum.credentials">
-      <app-button [text]="'Annuler'" (action)="cancel()"></app-button>
+      <app-button [text]="'Annuler'" [iconBtn]="'close'" (action)="cancel()"></app-button>
       <app-button
         [text]="'Valider'"
+        [iconBtn]="'check'"
         [disabled]="!isPageValid()"
         (action)="confirm()"
         [style]="buttonTypeEnum.Primary"
@@ -265,9 +266,10 @@
         </div>
 
         <div class="buttons">
-          <app-button [text]="'Annuler'" (action)="closeModal()"></app-button>
+          <app-button [text]="'Annuler'" [iconBtn]="'close'" (action)="closeModal()"></app-button>
           <app-button
             [text]="'Valider'"
+            [iconBtn]="'check'"
             [style]="buttonTypeEnum.Primary"
             [disabled]="!isPageValid()"
             (action)="confirm()"
@@ -387,9 +389,10 @@
           </div>
         </div>
         <div class="buttons">
-          <app-button [text]="'Annuler'" (action)="closeModal()"></app-button>
+          <app-button [text]="'Annuler'" [iconBtn]="'close'" (action)="closeModal()"></app-button>
           <app-button
             [text]="'Valider'"
+            [iconBtn]="'check'"
             [style]="buttonTypeEnum.Primary"
             [disabled]="!isPageValid()"
             (action)="confirm()"
@@ -434,7 +437,7 @@
         </div>
 
         <div class="buttons">
-          <app-button [text]="'Annuler'" (action)="closeModal()"></app-button>
+          <app-button [text]="'Annuler'" [iconBtn]="'close'" (action)="closeModal()"></app-button>
           <app-button
             [text]="'Supprimer'"
             [style]="buttonTypeEnum.Primary"
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.html b/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
index c23920854..02fd32306 100644
--- a/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
@@ -46,9 +46,15 @@
 
     <!-- Footer -->
     <div class="footer">
-      <app-button [text]="'Annuler'" (action)="cancel()" [disabled]="personalOfferForm?.pristine"></app-button>
+      <app-button
+        [text]="'Annuler'"
+        [iconBtn]="'close'"
+        (action)="cancel()"
+        [disabled]="personalOfferForm?.pristine"
+      ></app-button>
       <app-button
         [text]="'Valider'"
+        [iconBtn]="'check'"
         (action)="confirm()"
         [style]="buttonTypeEnum.Primary"
         [disabled]="personalOfferForm?.pristine"
-- 
GitLab


From 80cafa989dd4978066be5420a7cbb19c06a330cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Pailharey?= <rpailharey@grandlyon.com>
Date: Mon, 14 Nov 2022 14:26:40 +0100
Subject: [PATCH 09/12] feat: removed btn icon in modals

---
 src/app/profile/edit/edit.component.html | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/app/profile/edit/edit.component.html b/src/app/profile/edit/edit.component.html
index 5da29150e..339b0d4a6 100644
--- a/src/app/profile/edit/edit.component.html
+++ b/src/app/profile/edit/edit.component.html
@@ -266,10 +266,9 @@
         </div>
 
         <div class="buttons">
-          <app-button [text]="'Annuler'" [iconBtn]="'close'" (action)="closeModal()"></app-button>
+          <app-button [text]="'Annuler'" (action)="closeModal()"></app-button>
           <app-button
             [text]="'Valider'"
-            [iconBtn]="'check'"
             [style]="buttonTypeEnum.Primary"
             [disabled]="!isPageValid()"
             (action)="confirm()"
@@ -389,10 +388,9 @@
           </div>
         </div>
         <div class="buttons">
-          <app-button [text]="'Annuler'" [iconBtn]="'close'" (action)="closeModal()"></app-button>
+          <app-button [text]="'Annuler'" (action)="closeModal()"></app-button>
           <app-button
             [text]="'Valider'"
-            [iconBtn]="'check'"
             [style]="buttonTypeEnum.Primary"
             [disabled]="!isPageValid()"
             (action)="confirm()"
@@ -437,7 +435,7 @@
         </div>
 
         <div class="buttons">
-          <app-button [text]="'Annuler'" [iconBtn]="'close'" (action)="closeModal()"></app-button>
+          <app-button [text]="'Annuler'" (action)="closeModal()"></app-button>
           <app-button
             [text]="'Supprimer'"
             [style]="buttonTypeEnum.Primary"
-- 
GitLab


From 33e142f5e5d0fcd251d660d8d9c72467e9c07ec8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Pailharey?= <rpailharey@grandlyon.com>
Date: Mon, 14 Nov 2022 18:04:21 +0100
Subject: [PATCH 10/12] fix: review

---
 .../personal-offer-form.component.ts          |  2 +-
 .../personal-offer-edition.component.html     |  2 +-
 .../personal-offer-edition.component.scss     | 62 +------------------
 .../personal-offer-edition.component.spec.ts  | 23 -------
 .../personal-offer-edition.component.ts       |  4 +-
 src/app/resolvers/personal-offer.resolver.ts  |  2 -
 src/app/services/personal-offer.service.ts    |  6 +-
 .../accompaniment-picker.component.html       | 16 +++--
 .../accompaniment-picker.component.spec.ts    | 24 -------
 .../accompaniment-picker.component.ts         |  5 +-
 .../training-type-picker.component.html       |  8 +--
 .../training-type-picker.component.scss       |  1 -
 .../training-type-picker.component.ts         |  2 +-
 13 files changed, 22 insertions(+), 135 deletions(-)
 delete mode 100644 src/app/profile/personal-offer-edition/personal-offer-edition.component.spec.ts
 delete mode 100644 src/app/shared/components/accompaniment-picker/accompaniment-picker.component.spec.ts

diff --git a/src/app/form/form-view/personal-offer-form/personal-offer-form.component.ts b/src/app/form/form-view/personal-offer-form/personal-offer-form.component.ts
index e7237614f..296bd9287 100644
--- a/src/app/form/form-view/personal-offer-form/personal-offer-form.component.ts
+++ b/src/app/form/form-view/personal-offer-form/personal-offer-form.component.ts
@@ -27,7 +27,7 @@ export class PersonalOfferFormComponent implements OnChanges, OnInit {
 
   constructor(private searchService: SearchService, private router: Router) {}
 
-  async ngOnInit(): Promise<void> {
+  ngOnInit(): void {
     this.setCategories();
   }
 
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.html b/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
index 02fd32306..fc4ac9d2b 100644
--- a/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.html
@@ -1,5 +1,5 @@
 <div fxLayout="column" class="content-container full-screen">
-  <div class="edit-profile">
+  <div class="edit-personal-offer">
     <div class="header">
       <div class="title">
         <a routerLink="/profile">
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.scss b/src/app/profile/personal-offer-edition/personal-offer-edition.component.scss
index e653a2fac..17b70b85a 100644
--- a/src/app/profile/personal-offer-edition/personal-offer-edition.component.scss
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.scss
@@ -4,7 +4,7 @@
 @import '../../../assets/scss/shapes';
 @import '../../../assets/scss/breakpoint';
 
-.edit-profile {
+.edit-personal-offer {
   display: flex;
   flex-direction: column;
   flex: 1;
@@ -52,20 +52,6 @@
         cursor: pointer;
       }
     }
-
-    .deleteAccount {
-      ::ng-deep {
-        svg {
-          height: 22px;
-          width: 22px;
-          margin-right: 4px;
-        }
-        span {
-          color: $red;
-          @include lato-regular-14;
-        }
-      }
-    }
   }
 
   .navigation {
@@ -98,52 +84,6 @@
     padding-top: 24px;
     flex: 1;
     max-width: 600px;
-    p.subTitle {
-      @include lato-regular-16;
-      text-align: left;
-      margin-top: 0;
-      margin-bottom: 4px;
-    }
-
-    .credentialsTab {
-      .buttons {
-        margin-top: 25px;
-        display: flex;
-        flex-wrap: wrap;
-        gap: 18px;
-        ::ng-deep {
-          svg {
-            height: 22px;
-            width: 22px;
-            margin-right: 4px;
-          }
-        }
-      }
-    }
-    .credentialsTab ::ng-deep .secondary {
-      width: 220px !important;
-    }
-    .credentialsTab ::ng-deep .secondary .text {
-      place-content: center center !important;
-    }
-
-    .descriptionTab {
-      p.descriptionLength {
-        text-align: right;
-        @include lato-regular-14;
-        color: $grey-3;
-        font-style: italic;
-        margin-top: 8px;
-      }
-    }
-    .search-structure input {
-      width: 600px;
-    }
-    .structureResults {
-      position: absolute;
-      width: 600px;
-      z-index: 1;
-    }
   }
 
   .footer {
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.spec.ts b/src/app/profile/personal-offer-edition/personal-offer-edition.component.spec.ts
deleted file mode 100644
index c603900ef..000000000
--- a/src/app/profile/personal-offer-edition/personal-offer-edition.component.spec.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { PersonalOfferEditionComponent } from './personal-offer-edition.component';
-
-describe('PersonalOfferEditionComponent', () => {
-  let component: PersonalOfferEditionComponent;
-  let fixture: ComponentFixture<PersonalOfferEditionComponent>;
-
-  beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [ PersonalOfferEditionComponent ]
-    })
-    .compileComponents();
-
-    fixture = TestBed.createComponent(PersonalOfferEditionComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts b/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
index 7834d337e..627816a17 100644
--- a/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
@@ -1,6 +1,6 @@
 import { Component, OnInit } from '@angular/core';
 import { UntypedFormControl, UntypedFormGroup } from '@angular/forms';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Data } from '@angular/router';
 import { CategoriesToggle } from '../../models/categoriesToggle.model';
 import { NotificationService } from '../../services/notification.service';
 import { ButtonType } from '../../shared/components/button/buttonType.enum';
@@ -39,7 +39,7 @@ export class PersonalOfferEditionComponent implements OnInit {
   ) {}
 
   ngOnInit(): void {
-    this.route.data.subscribe(async (data) => {
+    this.route.data.subscribe(async (data: Data) => {
       if (data.personalOffer) {
         await this.setCategories();
         this.personalOffer = data.personalOffer;
diff --git a/src/app/resolvers/personal-offer.resolver.ts b/src/app/resolvers/personal-offer.resolver.ts
index 4ae34f051..55809560b 100644
--- a/src/app/resolvers/personal-offer.resolver.ts
+++ b/src/app/resolvers/personal-offer.resolver.ts
@@ -4,7 +4,6 @@ import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router';
 import { forkJoin, Observable, Subject } from 'rxjs';
 import { catchError, map } from 'rxjs/operators';
 import { PersonalOfferService } from '../services/personal-offer.service';
-
 import { SearchService } from '../structure-list/services/search.service';
 import { Utils } from '../utils/utils';
 
@@ -34,7 +33,6 @@ export class PersonalOfferResolver implements Resolve<PersonalOffer> {
         categories: this.searchService.getCategories(),
       }).subscribe((res) => {
         const personalOffer = this.utils.setServiceCategories(res.categories, res.personalOffer) as PersonalOffer;
-        // return res;
         this.setSubject(personalOffer);
       });
 
diff --git a/src/app/services/personal-offer.service.ts b/src/app/services/personal-offer.service.ts
index 32eefcd4a..93b485b6c 100644
--- a/src/app/services/personal-offer.service.ts
+++ b/src/app/services/personal-offer.service.ts
@@ -11,15 +11,15 @@ export class PersonalOfferService {
   private readonly baseUrl = 'api/personal-offers';
   constructor(private http: HttpClient) {}
 
-  public createPersonalOffer(structureId: string, personalOffer: PersonalOffer): Observable<any> {
-    return this.http.post<any>(this.baseUrl, { structureId: structureId, personalOffer: personalOffer });
+  public createPersonalOffer(structureId: string, personalOffer: PersonalOffer): Observable<PersonalOffer> {
+    return this.http.post<PersonalOffer>(this.baseUrl, { structureId: structureId, personalOffer: personalOffer });
   }
 
   public getPersonalOffer(id: string): Observable<PersonalOffer> {
     return this.http.get<PersonalOffer>(`${this.baseUrl}/${id}`);
   }
 
-  public updatePersonalOffer(id: string, categories: Category[]): Observable<any> {
+  public updatePersonalOffer(id: string, categories: Category[]): Observable<PersonalOffer> {
     return this.http.put<PersonalOffer>(`${this.baseUrl}/${id}`, {
       categories: categories,
     });
diff --git a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.html b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.html
index d3b879918..786ace30d 100644
--- a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.html
+++ b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.html
@@ -1,13 +1,11 @@
 <div fxLayout="column" fxLayoutGap="32px">
   <div *ngIf="onlineProcedures" class="btn-grid">
-    <span *ngFor="let module of onlineProcedures.modules">
-      <app-button
-        [ngClass]="{ selectedChoice: true }"
-        [extraClass]="isSelectedModule(module) ? 'selected' : ''"
-        [style]="buttonTypeEnum.CheckButton"
-        [text]="module.name"
-        (action)="toogleResult(module)"
-      ></app-button>
-    </span>
+    <app-button
+      *ngFor="let module of onlineProcedures.modules"
+      [extraClass]="isSelectedModule(module) ? 'selected' : ''"
+      [style]="buttonTypeEnum.CheckButton"
+      [text]="module.name"
+      (action)="toogleResult(module)"
+    ></app-button>
   </div>
 </div>
diff --git a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.spec.ts b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.spec.ts
deleted file mode 100644
index 109198b25..000000000
--- a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.spec.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AccompanimentPickerComponent } from './accompaniment-picker.component';
-
-describe('AccompanimentPickerComponent', () => {
-  let component: AccompanimentPickerComponent;
-  let fixture: ComponentFixture<AccompanimentPickerComponent>;
-
-  beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      declarations: [AccompanimentPickerComponent],
-    }).compileComponents();
-  });
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AccompanimentPickerComponent);
-    component = fixture.componentInstance;
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-});
diff --git a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts
index 764fd9f27..cbd8c77f3 100644
--- a/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts
+++ b/src/app/shared/components/accompaniment-picker/accompaniment-picker.component.ts
@@ -1,9 +1,8 @@
-import { filter } from 'rxjs/operators';
-import { Component, EventEmitter, Input, OnInit, Output, OnChanges, SimpleChanges } from '@angular/core';
+import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
 import { UntypedFormGroup } from '@angular/forms';
-import { ButtonType } from '../button/buttonType.enum';
 import { Category } from '../../../structure-list/models/category.model';
 import { Module } from '../../../structure-list/models/module.model';
+import { ButtonType } from '../button/buttonType.enum';
 
 @Component({
   selector: 'app-accompaniment-picker',
diff --git a/src/app/shared/components/training-type-picker/training-type-picker.component.html b/src/app/shared/components/training-type-picker/training-type-picker.component.html
index 1ca460408..7f659ff15 100644
--- a/src/app/shared/components/training-type-picker/training-type-picker.component.html
+++ b/src/app/shared/components/training-type-picker/training-type-picker.component.html
@@ -1,5 +1,5 @@
-<div class="container">
-  <div class="boutonSection" *ngFor="let categorie of categories" fxLayout="column" fxLayoutGap="8px">
+<div fxLayout="column" fxLayoutGap="8px">
+  <div class="boutonSection" *ngFor="let categorie of categories">
     <ng-container>
       <div fxLayout="column" class="collapse" [ngClass]="{ notCollapsed: !isCategorieExpanded(categorie.id) }">
         <div
@@ -25,10 +25,10 @@
           </div>
           <div class="logo">
             <svg class="show" aria-hidden="true">
-              <use [attr.xlink:href]="'assets/form/sprite.svg#show'"></use>
+              <use [attr.xlink:href]="'assets/form/sprite.svg#unfold'"></use>
             </svg>
             <svg class="hide" aria-hidden="true">
-              <use [attr.xlink:href]="'assets/form/sprite.svg#hide'"></use>
+              <use [attr.xlink:href]="'assets/form/sprite.svg#fold'"></use>
             </svg>
           </div>
         </div>
diff --git a/src/app/shared/components/training-type-picker/training-type-picker.component.scss b/src/app/shared/components/training-type-picker/training-type-picker.component.scss
index 4cf4a1660..238ec2bd3 100644
--- a/src/app/shared/components/training-type-picker/training-type-picker.component.scss
+++ b/src/app/shared/components/training-type-picker/training-type-picker.component.scss
@@ -40,7 +40,6 @@ button {
   }
 }
 .boutonSection {
-  padding-top: 9px;
   border-radius: 6px 6px 0px 0px;
   .btnText {
     min-height: 36px;
diff --git a/src/app/shared/components/training-type-picker/training-type-picker.component.ts b/src/app/shared/components/training-type-picker/training-type-picker.component.ts
index 5ddce8b86..93413c372 100644
--- a/src/app/shared/components/training-type-picker/training-type-picker.component.ts
+++ b/src/app/shared/components/training-type-picker/training-type-picker.component.ts
@@ -1,4 +1,4 @@
-import { Component, EventEmitter, Input, OnInit, Output, OnChanges, SimpleChanges } from '@angular/core';
+import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
 import { cloneDeep, remove } from 'lodash';
 import { CategoriesToggle } from 'src/app/models/categoriesToggle.model';
 import { Category } from '../../../structure-list/models/category.model';
-- 
GitLab


From 70b7ba03785deb752bffeca6761825645fa88235 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Pailharey?= <rpailharey@grandlyon.com>
Date: Tue, 15 Nov 2022 17:36:33 +0100
Subject: [PATCH 11/12] fix: training category selection bug

---
 .../training-type-picker.component.ts                     | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/app/shared/components/training-type-picker/training-type-picker.component.ts b/src/app/shared/components/training-type-picker/training-type-picker.component.ts
index 93413c372..c2acf78ba 100644
--- a/src/app/shared/components/training-type-picker/training-type-picker.component.ts
+++ b/src/app/shared/components/training-type-picker/training-type-picker.component.ts
@@ -27,6 +27,11 @@ export class TrainingTypePickerComponent implements OnInit, OnChanges {
       this.categories.push(data.category);
     });
     this.initSelectedChoice();
+    this.selectedChoices.forEach((category) => {
+      if (category.modules.length) {
+        this.categoriesExpanded.push(category.id);
+      }
+    });
   }
 
   ngOnChanges(): void {
@@ -47,9 +52,6 @@ export class TrainingTypePickerComponent implements OnInit, OnChanges {
         default:
           throw new Error(`Unimplemented training type ${category.id}`);
       }
-      if (selectedModulesId.length) {
-        this.categoriesExpanded.push(category.id);
-      }
       category.modules = category.modules.filter((module) => selectedModulesId.includes(module.id));
     });
   }
-- 
GitLab


From 4abd7552053bb3ed226a0ff04006b6ba7a939897 Mon Sep 17 00:00:00 2001
From: Bastien DUMONT <bdumont@grandlyon.com>
Date: Wed, 16 Nov 2022 15:07:34 +0100
Subject: [PATCH 12/12] type data

---
 .../personal-offer-edition/personal-offer-edition.component.ts  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts b/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
index 627816a17..77867c21b 100644
--- a/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
+++ b/src/app/profile/personal-offer-edition/personal-offer-edition.component.ts
@@ -39,7 +39,7 @@ export class PersonalOfferEditionComponent implements OnInit {
   ) {}
 
   ngOnInit(): void {
-    this.route.data.subscribe(async (data: Data) => {
+    this.route.data.subscribe(async (data: Data & { personalOffer: PersonalOffer }) => {
       if (data.personalOffer) {
         await this.setCategories();
         this.personalOffer = data.personalOffer;
-- 
GitLab