From 2a1ee3087e49747263eafd0dec4a494db09fbc22 Mon Sep 17 00:00:00 2001
From: nicoayci <nicoayci@users.noreply.github.com>
Date: Wed, 19 Feb 2025 12:29:45 +0100
Subject: [PATCH 1/3] feat: improve add attachment form for clarity

allow to retry if request creation failed
---
 .../file-upload/file-upload.component.html    |  7 ++++++-
 .../confirmation/confirmation.component.html  | 18 +++++++++---------
 .../confirmation/confirmation.component.ts    |  8 ++------
 .../information-input.component.html          |  8 +++++++-
 .../request-creation.component.html           | 10 +++++++---
 .../request-creation.component.ts             | 19 ++++++++++++++++---
 6 files changed, 47 insertions(+), 23 deletions(-)

diff --git a/src/app/component/common/file-upload/file-upload.component.html b/src/app/component/common/file-upload/file-upload.component.html
index a209094..6156ada 100644
--- a/src/app/component/common/file-upload/file-upload.component.html
+++ b/src/app/component/common/file-upload/file-upload.component.html
@@ -32,7 +32,12 @@
       <input
         class="fr-input"
         [(ngModel)]="description"
-        placeholder="description - entre 1 et {{ descMaxLength }} caractères" />
+        placeholder="Veuillez ajouter une description" />
+    </div>
+    <div
+      *ngIf="file && !descSizeOk()"
+      class="fr-fieldset__element fr-message--error">
+      La description doit contenir entre 1 et {{ descMaxLength }} caractères.
     </div>
     <div class="fr-fieldset__element">
       <div class="box-between">
diff --git a/src/app/component/court/request-creation/confirmation/confirmation.component.html b/src/app/component/court/request-creation/confirmation/confirmation.component.html
index 6a64ca9..b26175c 100644
--- a/src/app/component/court/request-creation/confirmation/confirmation.component.html
+++ b/src/app/component/court/request-creation/confirmation/confirmation.component.html
@@ -23,8 +23,8 @@
         <div class="fr-mb-4v">
           <span class="underlined">Pièces jointes</span>
           <ng-container *ngIf="attachmentLength">
-            ({{ attachmentLength }})</ng-container
-          >
+            ({{ attachmentLength }})
+          </ng-container>
           <ng-container *ngIf="!attachmentLength">
             : <em>aucune</em>
           </ng-container>
@@ -48,9 +48,9 @@
       <div class="fr-mt-10v">
         <div class="fr-mb-2v underlined">Informations complémentaires</div>
         <div class="extra-info">
-          <ng-container *ngIf="valueOf('extraInfo')">{{
-            valueOf('extraInfo')
-          }}</ng-container>
+          <ng-container *ngIf="valueOf('extraInfo')">
+            {{ valueOf('extraInfo') }}
+          </ng-container>
           <ng-container *ngIf="!valueOf('extraInfo')"
             ><em>aucune information complémentaire</em>
           </ng-container>
@@ -77,9 +77,9 @@
           </ng-container>
           experts suivants :
         </ng-container>
-        <ng-container *ngIf="selectedExperts.length === 1"
-          >à l'expert suivant :</ng-container
-        >
+        <ng-container *ngIf="selectedExperts.length === 1">
+          à l'expert suivant :
+        </ng-container>
         <div
           *ngFor="let expert of selectedExperts"
           class="fr-text--bold box-center fr-mt-6v fr-text--xl">
@@ -106,7 +106,7 @@
     <li>
       <button
         class="fr-btn"
-        (click)="confirmRequestCreation()"
+        (click)="requestCreationConfirmed.emit()"
         [disabled]="submitting">
         Confirmer
       </button>
diff --git a/src/app/component/court/request-creation/confirmation/confirmation.component.ts b/src/app/component/court/request-creation/confirmation/confirmation.component.ts
index 4fbf832..77fd6c9 100644
--- a/src/app/component/court/request-creation/confirmation/confirmation.component.ts
+++ b/src/app/component/court/request-creation/confirmation/confirmation.component.ts
@@ -15,7 +15,8 @@ export class ConfirmationComponent {
   @Input()
   requestCreationForm: FormGroup;
 
-  submitting = false;
+  @Input()
+  submitting: boolean;
 
   @Output()
   requestCreationConfirmed = new EventEmitter<void>();
@@ -23,11 +24,6 @@ export class ConfirmationComponent {
   @Output()
   stepChanged = new EventEmitter<-1>();
 
-  confirmRequestCreation() {
-    this.submitting = true;
-    this.requestCreationConfirmed.emit();
-  }
-
   valueOf(formControl: string): string {
     return this.requestCreationForm.get(formControl)?.value;
   }
diff --git a/src/app/component/court/request-creation/information-input/information-input.component.html b/src/app/component/court/request-creation/information-input/information-input.component.html
index 5b1a774..c7da39d 100644
--- a/src/app/component/court/request-creation/information-input/information-input.component.html
+++ b/src/app/component/court/request-creation/information-input/information-input.component.html
@@ -144,6 +144,10 @@
             (click)="showAddFile = true">
             Ajouter une pièce jointe
           </button>
+          <div *ngIf="showErrors && showAddFile" class="fr-message--error">
+            Pièce jointe en cours d'ajout. Veuillez finaliser l'ajout ou
+            annuler.
+          </div>
         </div>
 
         <div class="fr-input-group fr-mt-10v">
@@ -283,7 +287,9 @@
               <button
                 class="fr-btn"
                 (click)="validateInput()"
-                [disabled]="showErrors && !requestCreationForm.valid">
+                [disabled]="
+                  showErrors && (!requestCreationForm.valid || showAddFile)
+                ">
                 Étape suivante
               </button>
             </li>
diff --git a/src/app/component/court/request-creation/request-creation.component.html b/src/app/component/court/request-creation/request-creation.component.html
index e1a8f5e..0081d07 100644
--- a/src/app/component/court/request-creation/request-creation.component.html
+++ b/src/app/component/court/request-creation/request-creation.component.html
@@ -37,13 +37,17 @@
 <app-confirmation
   [hidden]="activeStep !== 3"
   [requestCreationForm]="requestCreationForm"
+  [submitting]="submitting"
   (stepChanged)="onStepChanged($event)"
   (requestCreationConfirmed)="createRequest()">
 </app-confirmation>
 
-<div *ngIf="requestCreationFailure" class="box-center">
-  <span class="fr-message--error">La demande n'a pas pu être enregistrée.</span>
-</div>
+<ng-container *ngIf="requestCreationRetry">
+  <div class="fr-message--error box-center">
+    La demande n'a pas pu être enregistrée<br />(vous pouvez réessayer dans
+    {{ requestCreationRetry }} sec.).
+  </div>
+</ng-container>
 
 <div *ngIf="errorFiles.length" class="box-center">
   <div class="fr-alert fr-alert--sm fr-alert--warning">
diff --git a/src/app/component/court/request-creation/request-creation.component.ts b/src/app/component/court/request-creation/request-creation.component.ts
index fa3d3e0..4b46f05 100644
--- a/src/app/component/court/request-creation/request-creation.component.ts
+++ b/src/app/component/court/request-creation/request-creation.component.ts
@@ -31,6 +31,7 @@ import { Request } from '../../../model/request';
 })
 export class RequestCreationComponent implements OnInit {
   basedOnLastRequest = false;
+  submitting = false;
 
   constructor(
     private formBuilder: FormBuilder,
@@ -153,7 +154,7 @@ export class RequestCreationComponent implements OnInit {
   activeStep = 1;
   selectedExperts: Expert[] = [];
   createdRequestId: number;
-  requestCreationFailure = false;
+  requestCreationRetry = 0;
   errorFiles: string[] = [];
 
   duplicatedProcedureId: string;
@@ -192,7 +193,7 @@ export class RequestCreationComponent implements OnInit {
         .filter(d => d.selected)
         .map(d => d.path)
     };
-
+    this.submitting = true;
     // create the request, get the id and then upload the files sequentially
     // warning: to upload the files in parallel, update backend mechanism and use forkJoin here
     this.apiService
@@ -224,7 +225,19 @@ export class RequestCreationComponent implements OnInit {
         })
       )
       .subscribe({
-        error: () => (this.requestCreationFailure = true),
+        error: () => {
+          this.requestCreationRetry = 5;
+          const countdownInterval = setInterval(() => {
+            if (this.requestCreationRetry > 0) {
+              this.requestCreationRetry -= 1;
+              if (this.requestCreationRetry === 0) {
+                this.submitting = false;
+              }
+            } else {
+              clearInterval(countdownInterval);
+            }
+          }, 1000);
+        },
         complete: () => {
           if (this.errorFiles.length === 0) this.goToRequest();
         }
-- 
GitLab


From 97591cfb01f9a72e1a18a3c58dfc1051b0697de1 Mon Sep 17 00:00:00 2001
From: Abdellatif ZAYTOUN <abdellatif.zaytoun@beta.gouv.fr>
Date: Tue, 25 Feb 2025 14:28:20 +0000
Subject: [PATCH 2/3] feat(): Filter experts by multiple expertise codes,
 including their parent expertise levels.

---
 src/app/app.module.ts                         |  2 ++
 .../admin/add-expert/add-expert.component.ts  | 16 +++++----
 .../appeal-court-select.component.html        |  2 ++
 .../filters/filters.component.html            | 30 +++-------------
 .../expert-list/filters/filters.component.ts  | 30 ++++------------
 .../expertise-select.component.css            |  0
 .../expertise-select.component.html           | 10 ++++++
 .../expertise-select.component.spec.ts        | 23 +++++++++++++
 .../expertise-select.component.ts             | 33 ++++++++++++++++++
 .../file-upload/file-upload.component.html    |  3 +-
 .../file-upload/file-upload.component.ts      |  4 +++
 .../request-expert.component.ts               |  2 +-
 src/app/model/pagination.ts                   | 34 +++++++++++++++++++
 src/app/rest/api.rest.service.ts              |  7 +---
 src/app/service/api.service.ts                |  4 ---
 src/app/service/expertise.service.spec.ts     | 16 +++++++++
 src/app/service/expertise.service.ts          | 31 +++++++++++++++++
 17 files changed, 180 insertions(+), 67 deletions(-)
 create mode 100644 src/app/component/common/expertise-select/expertise-select.component.css
 create mode 100644 src/app/component/common/expertise-select/expertise-select.component.html
 create mode 100644 src/app/component/common/expertise-select/expertise-select.component.spec.ts
 create mode 100644 src/app/component/common/expertise-select/expertise-select.component.ts
 create mode 100644 src/app/model/pagination.ts
 create mode 100644 src/app/service/expertise.service.spec.ts
 create mode 100644 src/app/service/expertise.service.ts

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 7a671ee..75a74e9 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -61,6 +61,7 @@ import { ExpertDetailModalComponent } from './component/common/expert-detail-mod
 import { ExpertDetailComponent } from './component/common/expert-detail/expert-detail.component';
 import { ExpertListComponent } from './component/common/expert-list/expert-list.component';
 import { FiltersComponent } from './component/common/expert-list/filters/filters.component';
+import { ExpertiseSelectComponent } from './component/common/expertise-select/expertise-select.component';
 import { FileDownloadComponent } from './component/common/file-download/file-download.component';
 import { FileUploadComponent } from './component/common/file-upload/file-upload.component';
 import { GoToRequestComponent } from './component/common/go-to-request/go-to-request.component';
@@ -174,6 +175,7 @@ registerLocaleData(localeFr, 'fr');
     PersonalDataComponent,
     GoToRequestComponent,
     AppealCourtSelectComponent,
+    ExpertiseSelectComponent,
     RequestFiltersComponent,
     InternalNoteListComponent,
     NoteItemComponent,
diff --git a/src/app/component/admin/add-expert/add-expert.component.ts b/src/app/component/admin/add-expert/add-expert.component.ts
index 325b65b..18ca701 100644
--- a/src/app/component/admin/add-expert/add-expert.component.ts
+++ b/src/app/component/admin/add-expert/add-expert.component.ts
@@ -1,10 +1,11 @@
 import { Component, OnInit } from '@angular/core';
-import { Observable } from 'rxjs';
-import { AppealCourt } from '../../../model/appeal-court';
-import { ApiService } from '../../../service/api.service';
+import { map, Observable } from 'rxjs';
+import { ExpertiseService } from 'src/app/service/expertise.service';
 import Utils from '../../../helper/utils';
+import { AppealCourt } from '../../../model/appeal-court';
 import { ExpertCreationForm } from '../../../model/expert-creation-form';
 import { Expertise } from '../../../model/expertise';
+import { ApiService } from '../../../service/api.service';
 import { AppealCourtService } from '../../../service/appeal-court.service';
 
 @Component({
@@ -38,15 +39,18 @@ export class AddExpertComponent implements OnInit {
   failure = false;
 
   constructor(
-    private apiService: ApiService,
-    private appealCourtService: AppealCourtService
+    private readonly apiService: ApiService,
+    private readonly expertiseService: ExpertiseService,
+    private readonly appealCourtService: AppealCourtService
   ) {}
 
   ngOnInit(): void {
     this.appealCourts$ = this.appealCourtService.getAppealCourts({
       enabledForExperts: true
     });
-    this.expertises$ = this.apiService.getExpertises();
+    this.expertises$ = this.expertiseService
+      .getExpertises('id,asc', false)
+      .pipe(map(response => response.content));
   }
 
   onSubmit() {
diff --git a/src/app/component/common/appeal-court-select/appeal-court-select.component.html b/src/app/component/common/appeal-court-select/appeal-court-select.component.html
index a0fcacc..6f077f7 100644
--- a/src/app/component/common/appeal-court-select/appeal-court-select.component.html
+++ b/src/app/component/common/appeal-court-select/appeal-court-select.component.html
@@ -3,5 +3,7 @@
   [options]="(appealCourtOptions$ | async) || []"
   [(ngModel)]="courtIds"
   [showSearch]="true"
+  [showClearSearch]="true"
+  [clearableOptions]="true"
   (selectionChange)="selectionChange()"
   appendTo="body"></dsfr-ext-multiselect>
diff --git a/src/app/component/common/expert-list/filters/filters.component.html b/src/app/component/common/expert-list/filters/filters.component.html
index 23d3004..a842363 100644
--- a/src/app/component/common/expert-list/filters/filters.component.html
+++ b/src/app/component/common/expert-list/filters/filters.component.html
@@ -10,31 +10,11 @@
         </div>
       </div>
       <div class="fr-col-12 fr-col-md-4">
-        <div class="fr-fieldset__element fr-pt-4v">
-          <ng-select
-            #expertisesSelect
-            *ngIf="expertises$ | async as expertises"
-            (change)="updateFilters()"
-            (focus)="setExpertisePlaceholder('focus')"
-            (blur)="setExpertisePlaceholder('blur')"
-            (clear)="expertisesSelect.close(); expertisesSelect.blur()"
-            [(ngModel)]="expertise"
-            appendTo="body"
-            [placeholder]="expertisePlaceholder"
-            notFoundText="Aucune rubrique correspondante"
-            dropdownPosition="bottom"
-            [closeOnSelect]="true"
-            [multiple]="false"
-            [searchable]="expertiseSearchable">
-            <ng-option
-              *ngFor="let expertise of expertises"
-              [value]="expertise.code">
-              <div class="box-left">
-                <div class="expertise-code fr-mr-2v">{{ expertise.code }}</div>
-                <div class="expertise-label">{{ expertise.label }}</div>
-              </div>
-            </ng-option>
-          </ng-select>
+        <div class="fr-fieldset__element fr-pt-2v">
+          <app-expertise-select
+            (expertiseCodesChanged)="
+              expertises = $event; updateFilters()
+            "></app-expertise-select>
         </div>
       </div>
       <div class="fr-col-12 fr-col-md-4 fr-pt-4v">
diff --git a/src/app/component/common/expert-list/filters/filters.component.ts b/src/app/component/common/expert-list/filters/filters.component.ts
index 7cf4967..9aa1437 100644
--- a/src/app/component/common/expert-list/filters/filters.component.ts
+++ b/src/app/component/common/expert-list/filters/filters.component.ts
@@ -1,7 +1,5 @@
 import { Component, EventEmitter, OnInit, Output } from '@angular/core';
-import { debounceTime, Observable, Subject } from 'rxjs';
-import { ApiService } from '../../../../service/api.service';
-import { Expertise } from '../../../../model/expertise';
+import { debounceTime, Subject } from 'rxjs';
 import { UserService } from '../../../../service/user.service';
 
 @Component({
@@ -10,32 +8,24 @@ import { UserService } from '../../../../service/user.service';
   styleUrls: ['./filters.component.css']
 })
 export class FiltersComponent implements OnInit {
-  expertises$: Observable<Expertise[]>;
   filters: ExpertFilters = {};
   courtIds: number[] = [];
-  expertise: string;
-  expertisePlaceholder: string;
-  expertiseSearchable = true;
+  expertises: string[];
   nameChunk = '';
   nameChunkSubject = new Subject<string>();
 
   @Output() filtersChanged = new EventEmitter<ExpertFilters>();
 
-  constructor(
-    private apiService: ApiService,
-    private userService: UserService
-  ) {
+  constructor(private readonly userService: UserService) {
     this.nameChunkSubject.pipe(debounceTime(500)).subscribe(() => {
       this.updateFilters();
     });
   }
 
   ngOnInit(): void {
-    this.setExpertisePlaceholder('blur');
-    this.expertises$ = this.apiService.getExpertises();
     if (this.userService.isCourt) {
       const appealCourt = this.userService.getUser()?.jurisdiction?.appealCourt;
-      if (appealCourt && appealCourt.enabledForExperts) {
+      if (appealCourt?.enabledForExperts) {
         this.courtIds.push(appealCourt.id);
       }
     }
@@ -45,27 +35,19 @@ export class FiltersComponent implements OnInit {
   updateFilters(): void {
     this.filters = {
       ...(this.courtIds && { courtIds: this.courtIds }),
-      ...(this.expertise && { expertise: this.expertise }),
+      ...(this.expertises && { expertises: this.expertises }),
       ...(this.nameChunk && { nameChunk: this.nameChunk })
     };
     this.filtersChanged.emit(this.filters);
-    this.expertiseSearchable = !this.expertise;
   }
 
   nameChunkChanged(): void {
     this.nameChunkSubject.next(this.nameChunk);
   }
-
-  setExpertisePlaceholder(event: 'focus' | 'blur') {
-    if (event === 'focus')
-      this.expertisePlaceholder = 'La rubrique contient ...';
-    if (event === 'blur')
-      this.expertisePlaceholder = 'Toutes les rubriques de la nomenclature';
-  }
 }
 
 export interface ExpertFilters {
   courtIds?: number[];
-  expertise?: string;
+  expertises?: string[];
   nameChunk?: string;
 }
diff --git a/src/app/component/common/expertise-select/expertise-select.component.css b/src/app/component/common/expertise-select/expertise-select.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/component/common/expertise-select/expertise-select.component.html b/src/app/component/common/expertise-select/expertise-select.component.html
new file mode 100644
index 0000000..678bac3
--- /dev/null
+++ b/src/app/component/common/expertise-select/expertise-select.component.html
@@ -0,0 +1,10 @@
+<dsfr-ext-multiselect
+  placeHolder="Toutes les rubriques de la nomenclature"
+  [options]="(expertisesOptions$ | async) || []"
+  [(ngModel)]="expertiseCodes"
+  [showSearch]="true"
+  [showClearSearch]="true"
+  [clearableOptions]="true"
+  [maxDisplayOptions]="0"
+  (selectionChange)="selectionChange()"
+  appendTo="body"></dsfr-ext-multiselect>
diff --git a/src/app/component/common/expertise-select/expertise-select.component.spec.ts b/src/app/component/common/expertise-select/expertise-select.component.spec.ts
new file mode 100644
index 0000000..e3e2b76
--- /dev/null
+++ b/src/app/component/common/expertise-select/expertise-select.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ExpertiseSelectComponent } from './expertise-select.component';
+
+describe('ExpertiseSelectComponent', () => {
+  let component: ExpertiseSelectComponent;
+  let fixture: ComponentFixture<ExpertiseSelectComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [ExpertiseSelectComponent]
+    })
+    .compileComponents();
+    
+    fixture = TestBed.createComponent(ExpertiseSelectComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/component/common/expertise-select/expertise-select.component.ts b/src/app/component/common/expertise-select/expertise-select.component.ts
new file mode 100644
index 0000000..862a1eb
--- /dev/null
+++ b/src/app/component/common/expertise-select/expertise-select.component.ts
@@ -0,0 +1,33 @@
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { DsfrSelectOption } from '@edugouvfr/ngx-dsfr';
+import { map, Observable } from 'rxjs';
+import { ExpertiseService } from 'src/app/service/expertise.service';
+
+@Component({
+  selector: 'app-expertise-select',
+  templateUrl: './expertise-select.component.html',
+  styleUrl: './expertise-select.component.css'
+})
+export class ExpertiseSelectComponent implements OnInit {
+  expertisesOptions$: Observable<DsfrSelectOption[]>;
+  @Input() expertiseCodes: string[];
+  @Output() expertiseCodesChanged = new EventEmitter<string[]>();
+
+  constructor(private readonly expertiseService: ExpertiseService) {}
+
+  ngOnInit(): void {
+    this.expertisesOptions$ = this.expertiseService
+      .getExpertises('id,asc', false)
+      .pipe(
+        map(response =>
+          response.content.map(e => {
+            return { label: '[' + e.code + '] ' + e.label, value: e.code };
+          })
+        )
+      );
+  }
+
+  selectionChange() {
+    this.expertiseCodesChanged.emit(this.expertiseCodes);
+  }
+}
diff --git a/src/app/component/common/file-upload/file-upload.component.html b/src/app/component/common/file-upload/file-upload.component.html
index 6156ada..e3d8453 100644
--- a/src/app/component/common/file-upload/file-upload.component.html
+++ b/src/app/component/common/file-upload/file-upload.component.html
@@ -13,7 +13,8 @@
         type="file"
         id="file-input"
         accept="application/pdf"
-        (change)="onFileChange($event)" />
+        (change)="onFileChange($event)"
+        (drop)="preventDrop()" />
       <button
         *ngIf="isDecision"
         class="fr-btn fr-icon-check-line"
diff --git a/src/app/component/common/file-upload/file-upload.component.ts b/src/app/component/common/file-upload/file-upload.component.ts
index 3e0e341..652b831 100644
--- a/src/app/component/common/file-upload/file-upload.component.ts
+++ b/src/app/component/common/file-upload/file-upload.component.ts
@@ -55,4 +55,8 @@ export class FileUploadComponent {
   get isDecision(): boolean {
     return this.fileType === FileType.DECISION;
   }
+
+  preventDrop() {
+    return false;
+  }
 }
diff --git a/src/app/component/common/request-list/request-item/request-expert/request-expert.component.ts b/src/app/component/common/request-list/request-item/request-expert/request-expert.component.ts
index 6225234..51de689 100644
--- a/src/app/component/common/request-list/request-item/request-expert/request-expert.component.ts
+++ b/src/app/component/common/request-list/request-item/request-expert/request-expert.component.ts
@@ -97,7 +97,7 @@ export class RequestExpertComponent implements OnInit {
         )
       ) {
         description =
-          "<br/><br/>Si vous souhaitez étendre cette date d'un jour supplémentaire, vous pouvez cliquer sur le bouton ci-dessous";
+          "<br/><br/>Pour allonger d'un jour supplémentaire le délai accordé à l'expert pour répondre à votre demande, vous pouvez cliquer sur le bouton ci-dessous";
 
         actions = [1].map(nbDays => ({
           label: `${nbDays} jour${nbDays > 1 ? 's' : ''}`,
diff --git a/src/app/model/pagination.ts b/src/app/model/pagination.ts
new file mode 100644
index 0000000..50cdbcb
--- /dev/null
+++ b/src/app/model/pagination.ts
@@ -0,0 +1,34 @@
+export interface Sort {
+  unsorted: boolean;
+  sorted: boolean;
+  empty: boolean;
+}
+
+export interface Pageable {
+  paged: boolean;
+  pageNumber: number;
+  pageSize: number;
+  offset: number;
+  sort: Sort;
+  unpaged: boolean;
+}
+
+export interface Content {
+  id: number;
+  code: string;
+  label: string;
+}
+
+export interface PaginatedResponse<T> {
+  content: T[];
+  pageable: Pageable;
+  totalPages: number;
+  totalElements: number;
+  last: boolean;
+  first: boolean;
+  size: number;
+  number: number;
+  sort: Sort;
+  numberOfElements: number;
+  empty: boolean;
+}
diff --git a/src/app/rest/api.rest.service.ts b/src/app/rest/api.rest.service.ts
index 71eab66..5707891 100644
--- a/src/app/rest/api.rest.service.ts
+++ b/src/app/rest/api.rest.service.ts
@@ -13,7 +13,6 @@ import { CourtService } from '../model/court-service';
 import { CourtUser, CourtUserForAdmin } from '../model/court-user';
 import { Expert } from '../model/expert';
 import { ExpertCreationForm } from '../model/expert-creation-form';
-import { Expertise } from '../model/expertise';
 import { ImportRequest } from '../model/import-request';
 import { Magistrate } from '../model/magistrate';
 import { Note } from '../model/note';
@@ -30,7 +29,7 @@ import { UserInfo } from '../model/user-info';
 export class ApiRestService {
   readonly BASE_PATH = `${environment.apiUrl}/api`;
 
-  constructor(private http: HttpClient) {}
+  constructor(private readonly http: HttpClient) {}
 
   getExperts(page: number, filters?: ExpertFilters): Observable<Page<Expert>> {
     return this.http.get<Page<Expert>>(`${this.BASE_PATH}/court/experts`, {
@@ -235,10 +234,6 @@ export class ApiRestService {
     );
   }
 
-  getExpertises(): Observable<Expertise[]> {
-    return this.http.get<Expertise[]>(`${this.BASE_PATH}/common/expertise`);
-  }
-
   getCourtServices(): Observable<CourtService[]> {
     return this.http.get<CourtService[]>(
       `${this.BASE_PATH}/common/court-service`
diff --git a/src/app/service/api.service.ts b/src/app/service/api.service.ts
index 07f4983..5bbf866 100644
--- a/src/app/service/api.service.ts
+++ b/src/app/service/api.service.ts
@@ -165,10 +165,6 @@ export class ApiService {
     return this.apiRestService.importAddedAndDeleted(courtId);
   }
 
-  getExpertises(): Observable<Expertise[]> {
-    return this.apiRestService.getExpertises();
-  }
-
   getCourtServices(): Observable<CourtService[]> {
     return this.apiRestService.getCourtServices();
   }
diff --git a/src/app/service/expertise.service.spec.ts b/src/app/service/expertise.service.spec.ts
new file mode 100644
index 0000000..78d9f74
--- /dev/null
+++ b/src/app/service/expertise.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { ExpertiseService } from './expertise.service';
+
+describe('ExpertiseService', () => {
+  let service: ExpertiseService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(ExpertiseService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});
diff --git a/src/app/service/expertise.service.ts b/src/app/service/expertise.service.ts
new file mode 100644
index 0000000..07d955f
--- /dev/null
+++ b/src/app/service/expertise.service.ts
@@ -0,0 +1,31 @@
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+import { environment } from 'src/environments/environment';
+import { Expertise } from '../model/expertise';
+import { PaginatedResponse } from '../model/pagination';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ExpertiseService {
+  private readonly API_URL = `${environment.apiUrl}/api/v2/expertises`;
+
+  constructor(private readonly http: HttpClient) {}
+
+  getExpertises(
+    sort: string = '',
+    paged: boolean = true,
+    pageNumber: number = 0,
+    pageSize: number = 10
+  ): Observable<PaginatedResponse<Expertise>> {
+    const params = new HttpParams()
+      .set('sort', sort)
+      .set('paged', paged)
+      .set('page', pageNumber)
+      .set('size', pageSize);
+    return this.http.get<PaginatedResponse<Expertise>>(`${this.API_URL}`, {
+      params
+    });
+  }
+}
-- 
GitLab


From 10af830b72e3d747c85ee991544ee35f37be0825 Mon Sep 17 00:00:00 2001
From: nicoayci <nicoayci@users.noreply.github.com>
Date: Tue, 25 Feb 2025 16:46:53 +0100
Subject: [PATCH 3/3] "outputHashing": "all",

---
 angular.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/angular.json b/angular.json
index be42955..176bd9d 100644
--- a/angular.json
+++ b/angular.json
@@ -13,6 +13,7 @@
         "build": {
           "builder": "@ngx-env/builder:browser",
           "options": {
+            "outputHashing": "all",
             "outputPath": "dist/selexpert-frontend",
             "index": "src/index.html",
             "main": "src/main.ts",
-- 
GitLab