Skip to content
Snippets Groups Projects
Commit 11bbfe2e authored by Marlène SIMONDANT's avatar Marlène SIMONDANT
Browse files

Merge branch '65-1-fiche-profil-annuaire-prendre-un-rdv-en-ligne-v2' into 'dev'

Resolve "[1] [Fiche profil annuaire] Prendre un rdv en ligne V2"

See merge request !564
parents c1464a6f e6619f2d
Branches
Tags
2 merge requests!572V2.4.0,!564Resolve "[1] [Fiche profil annuaire] Prendre un rdv en ligne V2"
Showing
with 271 additions and 136 deletions
<div *ngIf="structureRDV" class="container">
<div *ngIf="hasStructure" class="container">
<img src="../../../../../../assets/form/structureCreated.svg" alt="rdv illustration" />
<h2>Votre demande de RDV a été transmise !</h2>
<p>
La demande de <b>rendez-vous auprès d’un conseiller ou une conseillère numérique</b> a bien été transmise à
<b>{{ socialWorker.name | userName }} {{ socialWorker.surname | uppercase }}</b> au sein de la structure
<b>{{ structureRDV.structureName }}</b>
<b
>{{ socialWorker?.name || orientationService.rdvUser?.name | userName }}
{{ socialWorker?.surname || orientationService.rdvUser?.surname | uppercase }}</b
>
au sein de la structure
<b>{{ structureRDV?.structureName || selectedStructureRDV?.structureName }}</b>
</p>
</div>
<div *ngIf="!structureRDV" class="container">
<div *ngIf="!hasStructure" class="container">
<img src="../../../../../../assets/img/joinRefused.svg" alt="rdv illustration" />
<h2>Aucune structure correspondant à votre recherche ne propose de rendez-vous</h2>
<p>Renouvelez votre recherche avec des critères différents ou accédez à la cartographie</p>
......
......
......@@ -3,6 +3,7 @@ import { UntypedFormGroup } from '@angular/forms';
import { Owner } from '../../../../../models/owner.model';
import { Structure } from '../../../../../models/structure.model';
import { NotificationService } from '../../../../../services/notification.service';
import { OrientationService } from '../../../../../services/orientation.service';
@Component({
selector: 'app-appointment-end',
......@@ -13,13 +14,17 @@ export class AppointmentEndComponent implements OnInit {
@Input() form: UntypedFormGroup;
@Input() structureRDV: Structure;
@Input() socialWorker: Owner;
@Input() selectedStructureRDV: Structure;
@Output() setResetOrientation = new EventEmitter();
@Output() checkValidation = new EventEmitter<any>();
constructor(private notificationService: NotificationService) {}
public hasStructure: boolean = false;
constructor(private notificationService: NotificationService, public orientationService: OrientationService) {}
async ngOnInit(): Promise<void> {
this.checkValidation.emit();
if (this.form.get('structureRDV').value || this.structureRDV) this.hasStructure = true;
try {
if (!this.structureRDV) {
this.setResetOrientation.emit();
......
......
<form *ngIf="owners?.length > 0" [formGroup]="form">
<form *ngIf="owners?.length > 0 || (structures?.length > 0 && structuresListReady)" [formGroup]="form">
<div class="title">
<h3>Vous allez prendre rendez-vous avec la structure :</h3>
</div>
<div class="structure" tabindex="0" fxLayout="column">
<div fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="16px">
<div fxLayout="row" fxLayoutAlign="center center" fxLayoutGap="8px">
<app-svg-icon [type]="'ico'" [icon]="structureRDV?.getTypeStructureIcon()" [iconClass]="'icon-52'" />
<div fxLayout="column" fxLayoutAlign="end">
<div fxLayout="row" class="structure-name-container">
<span class="structure-name">{{ structureRDV.structureName }}</span>
</div>
<span class="structure-type">{{ structureRDV.getLabelTypeStructure() }}</span>
</div>
<div *ngIf="owners?.length > 0">
<div class="header" tabindex="0" fxLayout="column">
<app-card
class="structure-card"
role="listitem"
[noDetails]="true"
[structure]="structureRDV"
[isOrientation]="false"
[isOrientationRdv]="false"
></app-card>
</div>
<div fxLayout="column" fxLayoutAlign="none end">
<div class="structure-place">{{ structureRDV.address.commune }}</div>
</div>
</div>
</div>
<div class="profile">
<div class="selectList">
<h4>Sélectionnez un·e accompagnant·e numérique</h4>
<div
*ngFor="let owner of owners"
class="profile-single selected"
class="card-single selected"
fxLayout="row"
fxLayoutAlign="left center"
fxLayoutGap="16px"
......@@ -34,19 +29,72 @@
formControlName="socialWorkerId"
id="{{ owner._id }}"
value="{{ owner._id }}"
(change)="onRadioChange(owner)"
(change)="onSocialWorkerRadioChange(owner)"
/>
<label for="{{ owner._id }}">
<app-svg-icon class="avatar" [type]="'avatar'" [icon]="'defaultAvatar'" [iconClass]="'icon-40'" />
<app-svg-icon class="" [type]="'avatar'" [icon]="'defaultAvatar'" [iconClass]="'icon-40'" />
<div fxLayout="column" fxLayoutAlign="start">
<div fxLayout="row" class="profile-name-container">
<span class="profile-name">{{ owner.name | userName }} {{ owner.surname | uppercase }}</span>
<div fxLayout="row" class="selectListNameContainer">
<span class="selectListName">{{ owner.name | userName }} {{ owner.surname | uppercase }}</span>
</div>
<span class="profile-job">{{ owner.job?.name }}</span>
<span class="selectListJob">{{ owner.job?.name }}</span>
</div>
</label>
</div>
</div>
</div>
<div *ngIf="structures?.length > 0">
<div class="header" tabindex="0" fxLayout="column">
<div fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="16px">
<div fxLayout="row" fxLayoutAlign="center center" fxLayoutGap="8px">
<app-svg-icon class="avatar" [type]="'avatar'" [icon]="'defaultAvatar'" [iconClass]="'icon-40'" />
<div fxLayout="column" fxLayoutAlign="end">
<div fxLayout="row" class="headerNameContainer">
<span class="headerName"
>{{ orientationService.rdvUser.name | userName }}
{{ orientationService.rdvUser.surname | uppercase }}</span
>
</div>
<span class="headerType">{{ orientationService.rdvUser.job.name }}</span>
</div>
</div>
</div>
</div>
<div class="selectList">
<h4>Sélectionnez la structure de votre choix</h4>
<div
*ngFor="let structure of structures"
class="card-single structure-single selected"
fxLayout="row"
fxLayoutAlign="left center"
fxLayoutGap="16px"
[ngClass]="{ selected: selectedOption === structure._id }"
>
<input
type="radio"
formControlName="structureRDV"
id="{{ structure._id }}"
value="{{ structure._id }}"
(click)="onStructureRadioChange(structure)"
(keyup.enter)="onStructureRadioChange(structure)"
/>
<label class="listCards" for="{{ structure._id }}">
<app-card
class="structure-card"
role="listitem"
[noDetails]="true"
[structure]="structure"
[isOrientation]="false"
[isOrientationRdv]="false"
(click)="onStructureRadioChange(structure)"
(keyup.enter)="onStructureRadioChange(structure)"
></app-card>
</label>
</div>
</div>
</div>
<div class="details">
<label for="details-field">Précisez les besoins de la personne (disponibilités...) :</label>
<div fxLayout="row" fxLayoutAlign="left center" fxLayoutGap="16px">
......@@ -61,29 +109,34 @@
</div>
</div>
</form>
<div *ngIf="owners?.length === 0" class="noOwner">
<div *ngIf="owners?.length === 0 || (structures?.length === 0 && structuresListReady)" class="noResult">
<img src="../../../../../../assets/img/joinRefused.svg" alt="" />
<span *ngIf="isBaseskills" class="info">
<h2>
Aucun accompagnant·e numérique de cette structure ne peut répondre à
<span [ngPlural]="needsList.length"
<span [ngPlural]="this.form.get('onlineDemarcheType')?.value.length"
><ng-template ngPluralCase="1">votre besoin</ng-template
><ng-template ngPluralCase="other">l'ensemble de ces besoins</ng-template>
</span>
:
</h2>
<span *ngFor="let item of needsList; last as isLast">{{ item }}<span *ngIf="!isLast">, </span> </span>
<span *ngFor="let item of this.form.get('onlineDemarcheType')?.value; last as isLast"
>{{ item.text }}<span *ngIf="!isLast">, </span>
</span>
</span>
<span *ngIf="isOnlineProcedures" class="info">
<h2>
Aucun accompagnant·e numérique de cette structure ne peut accompagner pour
<span [ngPlural]="needsList.length"
<span [ngPlural]="this.form.get('onlineDemarcheType')?.value.length"
><ng-template ngPluralCase="1">cette démarche</ng-template
><ng-template ngPluralCase="other">l'ensemble de ces démarches</ng-template>
</span>
en ligne :
</h2>
<span *ngFor="let item of needsList; last as isLast">{{ item }}<span *ngIf="!isLast">, </span> </span>
<span *ngFor="let item of this.form.get('onlineDemarcheType')?.value; last as isLast"
>{{ item.text }}<span *ngIf="!isLast">, </span>
</span>
</span>
<p>Renouvelez votre recherche avec des critères différents ou accédez à la cartographie</p>
</div>
@import 'color';
@import 'typography';
.structure {
.header {
margin-top: 24px;
padding: 24px 32px 24px 48px;
padding: 0 48px;
border: 2px solid $grey-6;
border-radius: 8px 8px 0 0;
min-height: 110px;
display: flex;
justify-content: center;
.typeStructure {
::ng-deep .avatar svg {
top: -0.125em;
}
.headerType {
color: $grey-3;
@include lato-regular-16;
font-style: italic;
}
.structure-name-container {
.headerNameContainer {
align-items: center;
}
.structure-name {
.headerName {
@include lato-bold-18;
color: $grey-1;
width: 100%;
}
.structure-place {
color: $grey-3;
}
}
.profile {
.selectList {
padding: 0 32px 24px 48px;
border: 2px solid $grey-6;
border-top: none;
......@@ -34,22 +34,25 @@
display: flex;
justify-content: center;
flex-direction: column;
.profile-single {
.card-single {
padding: 16px 8px;
border: 1px solid transparent;
&.selected {
border-color: black;
}
&.structure-single {
padding: 0 12px;
}
}
.profile-job {
.selectListJob {
color: $grey-3;
@include lato-regular-16;
font-style: italic;
}
.profile-name-container {
.selectListNameContainer {
align-items: center;
}
.profile-name {
.selectListName {
@include lato-bold-18;
color: $grey-1;
width: 100%;
......@@ -65,6 +68,10 @@
flex: 1;
flex-direction: row;
cursor: pointer;
width: 100%;
&.listCards {
display: block;
}
}
}
.details {
......@@ -78,14 +85,12 @@
margin-top: 8px;
}
}
.noOwner,
.noOwner > span {
.noResult,
.noResult > span {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
justify-content: center;
height: 90%;
h2 {
margin-top: 2rem;
margin-bottom: 0.5rem;
......@@ -95,5 +100,6 @@
p {
text-align: center;
max-width: 600px;
margin: 2em 0 0 0;
}
}
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { AbstractControl, UntypedFormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Observable, forkJoin } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { Owner } from '../../../../../models/owner.model';
import { Structure } from '../../../../../models/structure.model';
import { OrientationService } from '../../../../../services/orientation.service';
import { StructureService } from '../../../../../services/structure.service';
@Component({
......@@ -17,34 +18,44 @@ export class MakeAppointmentComponent implements OnInit {
@Output() checkValidation = new EventEmitter<any>();
@Output() socialWorker = new EventEmitter<Owner>();
@Output() setResetOrientation = new EventEmitter();
@Output() selectedStructureRDV = new EventEmitter<Structure>();
public owners: Owner[];
public structures: Structure[] = [];
public isBaseskills: boolean = false;
public isOnlineProcedures: boolean = false;
public needsList = [];
public selectedOption: string;
public structuresListReady: boolean;
constructor(private structureService: StructureService) {}
private onlineDemarcheType;
private filtersOnOffers;
constructor(private structureService: StructureService, public orientationService: OrientationService) {}
ngOnInit(): void {
this.checkValidation.emit();
// Filters owners that accept appointments, have personal offers in the selected structure and match the needs previously selected
this.findOwners(this.structureRDV._id).subscribe((owners) => {
let filteredOwners = [];
this.owners = owners.filter((owner) => owner.withAppointment && owner.job?.hasPersonalOffer);
const onlineDemarcheType = this.form.get('onlineDemarcheType').value;
const filtersOnOffers = { onlineProcedures: [], baseSkills: [] };
// build arrays containing the needs the user selected in the form
onlineDemarcheType.forEach((filter) => {
if (this.structureRDV?._id || this.orientationService.rdvUser) {
this.onlineDemarcheType = this.form.get('onlineDemarcheType').value;
this.filtersOnOffers = { onlineProcedures: [], baseSkills: [] };
this.onlineDemarcheType.forEach((filter) => {
if (filter.name === 'onlineProcedures') {
filtersOnOffers.onlineProcedures.push(filter.value);
this.filtersOnOffers.onlineProcedures.push(filter.value);
this.isOnlineProcedures = true;
}
if (filter.name === 'baseSkills') {
filtersOnOffers.baseSkills.push(filter.value);
this.filtersOnOffers.baseSkills.push(filter.value);
this.isBaseskills = true;
}
});
}
// Filters owners that accept appointments, have personal offers in the selected structure and match the needs previously selected
if (this.structureRDV?._id) {
this.findOwners(this.structureRDV._id).subscribe((owners) => {
let filteredOwners = [];
this.owners = owners.filter((owner) => owner.withAppointment && owner.job?.hasPersonalOffer);
// make an array with the IDs of the structure's personalOffers
const structurePersonalOffersId = this.structureRDV.personalOffers.map((spo) => spo._id);
// for each owner
......@@ -55,8 +66,10 @@ export class MakeAppointmentComponent implements OnInit {
if (structurePersonalOffersId.includes(po._id)) {
// and if the needs the user selected are all part of the personalOffer, keep the owner for display
if (
filtersOnOffers.onlineProcedures.every((offer) => po.categories.onlineProcedures.includes(offer)) &&
filtersOnOffers.baseSkills.every((offer) => po.categories.baseSkills.includes(offer))
this.filtersOnOffers.onlineProcedures.every((offer) =>
po.categories.onlineProcedures.includes(offer)
) &&
this.filtersOnOffers.baseSkills.every((offer) => po.categories.baseSkills.includes(offer))
) {
if (!filteredOwners.includes(owner)) {
filteredOwners.push(owner);
......@@ -69,13 +82,51 @@ export class MakeAppointmentComponent implements OnInit {
this.owners = filteredOwners;
if (this.owners.length === 0) {
this.form.get('onlineDemarcheType')?.value.forEach((element) => {
this.needsList.push(element.text);
this.setResetOrientation.emit();
}
});
} else if (this.orientationService.rdvUser) {
// else if we come from the registry, list the registry's user's structures
this.selectedOption = this.form.get('structureRDV').value;
const structures$: Observable<any>[] = [];
this.structures = [];
this.orientationService.rdvUser.structuresLink.forEach((structureId) => {
structures$.push(
this.structureService.getStructure(structureId).pipe(
tap((structure) => {
this.structures.push(new Structure(structure));
})
)
);
});
forkJoin(structures$).subscribe(() => {
this.structures.sort((a, b) => a.structureName.localeCompare(b.structureName));
// filter structures that match selected needs
let filteredStructures = [];
// for each structure
this.structures.forEach((structure) => {
// check each of its personnalOffers
structure.personalOffers?.forEach((po) => {
//if the needs the user selected are all part of the personalOffer, keep the structure for display
if (
this.filtersOnOffers.onlineProcedures.every((offer) => po.categories.onlineProcedures.includes(offer)) &&
this.filtersOnOffers.baseSkills.every((offer) => po.categories.baseSkills.includes(offer))
) {
filteredStructures.push(structure);
}
});
});
this.structures = filteredStructures;
if (this.structures.length === 0) {
this.setResetOrientation.emit();
}
this.structuresListReady = true;
});
}
}
private findOwners(structureId: string): Observable<Owner[]> {
return this.structureService.getStructureWithOwners(structureId).pipe(map((result) => result.owners));
......@@ -85,11 +136,18 @@ export class MakeAppointmentComponent implements OnInit {
return this.form.get(nameControl);
}
public onRadioChange(socialWorker: Owner): void {
public onSocialWorkerRadioChange(socialWorker: Owner): void {
this.socialWorker.emit(socialWorker);
this.checkValidation.emit();
}
public onStructureRadioChange(structure: Structure): void {
this.selectedOption = structure._id;
this.form.get('socialWorkerId').setValue(this.orientationService.rdvUser._id);
this.selectedStructureRDV.emit(structure);
this.checkValidation.emit(structure);
}
public onDetailsChange(): void {
this.checkValidation.emit();
}
......
......
......@@ -50,6 +50,7 @@
[structureRDV]="structureRDV"
(checkValidation)="checkValidation()"
(socialWorker)="getSocialWorker($event)"
(selectedStructureRDV)="getSelectedStructureRDV($event)"
(setResetOrientation)="showResetOrientation()"
/>
<app-mediation-beneficiary-info
......@@ -62,6 +63,7 @@
*ngIf="currentStep === onlineDemarchesAppointmentSteps.rdvEnd"
[form]="form"
[structureRDV]="structureRDV"
[selectedStructureRDV]="selectedStructureRDV"
[socialWorker]="socialWorker"
(checkValidation)="checkValidation()"
(setResetOrientation)="showResetOrientation()"
......
......
......@@ -29,6 +29,7 @@ export class OnlineDemarchFormComponent {
@Input() profile: User;
@Input() categories: { [key: string]: Category };
@Input() structureRDV: Structure;
@Input() selectedStructureRDV: Structure;
@Output() validatePage = new EventEmitter<any>();
@Output() validateStructureRDV = new EventEmitter<Structure>();
@Output() setResetOrientation = new EventEmitter<any>();
......@@ -95,10 +96,12 @@ export class OnlineDemarchFormComponent {
}
}
/* Get socialWorker from makeAppointment to display it in rdvEnd */
public getSocialWorker(item: Owner) {
this.socialWorker = item;
}
public getSelectedStructureRDV(structure: Structure) {
this.selectedStructureRDV = structure;
}
public updatePublicChoice({ formControlName, choice }: { formControlName: string; choice: string }): void {
const event = !this.isInArray({ formControlName, term: choice });
......
......
......@@ -476,9 +476,11 @@ export class OrientationFormViewComponent implements OnInit, AfterContentChecked
if (
this.currentType === OnlineDemarche.appointment &&
this.currentStep === AppointmentSteps.pmrAccess - 1 &&
this.orientationService.rdvStructure
(this.orientationService.rdvStructure || this.orientationService.rdvUser)
) {
if (this.orientationService.rdvStructure) {
this.onlineDemarcheForm.get('structureRDV').patchValue(this.orientationService.rdvStructure._id);
}
// skip pmrAccess, location and carto
this.currentStep += 3;
}
......
......
......@@ -61,7 +61,6 @@ export class Structure {
public alreadySelected? = false;
public isClaimed?: boolean = null;
public idCNFS?: string;
public withAppointment?: boolean = null;
constructor(obj?: any) {
......
......
......@@ -12,6 +12,17 @@
(keyup.enter)="goBack()"
/>
<h1>Profil</h1>
<app-button
*ngIf="isPublic && userProfile.withAppointment"
class="hide-on-mobile rdv"
tabindex="none"
[routerLink]="['/orientation']"
[state]="{ rdvUser: userProfile }"
[iconBtn]="'rdv'"
[text]="'Prendre rendez-vous'"
[style]="buttonTypeEnum.SecondaryWide"
[routerLinkActive]="'active'"
/>
<app-button
*ngIf="!isPublic"
class="hide-on-mobile"
......
......
......@@ -104,3 +104,7 @@ section {
.addStructure {
margin: auto;
}
::ng-deep .rdv svg {
top: 0.3em;
}
......@@ -317,17 +317,3 @@ button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.btn-rdvs {
background: $blueRdvs;
width: 140px;
height: 36px;
span {
color: white;
}
::ng-deep svg {
stroke: none !important;
top: 3px;
left: 3px;
}
}
......@@ -15,6 +15,7 @@ export class CardComponent implements OnInit {
@Input() public isSelected: boolean;
@Input() public isOrientation: boolean = false;
@Input() public isOrientationRdv: boolean = false;
@Input() public noDetails: boolean = false;
@Output() public showDetails: EventEmitter<Structure> = new EventEmitter<Structure>();
@Output() public addToList: EventEmitter<Structure> = new EventEmitter<Structure>();
@Output() public selectRDV: EventEmitter<Structure> = new EventEmitter<Structure>();
......@@ -51,6 +52,7 @@ export class CardComponent implements OnInit {
}
public cardClicked(): void {
if (!this.noDetails) {
this.showDetails.emit(this.structure);
const queryString = this.route.snapshot.queryParamMap.get('search');
this.router.navigate([], {
......@@ -65,6 +67,7 @@ export class CardComponent implements OnInit {
},
});
}
}
public cardHover(): void {
this.hover.emit(this.structure);
......
......
......@@ -233,7 +233,7 @@ export class OrientationUtils {
valid: true,
};
pagesValidation[AppointmentSteps.makeAppointment] = {
valid: form.get('socialWorkerId').valid,
valid: form.get('socialWorkerId').valid || form.get('structureRDV').valid,
};
pagesValidation[AppointmentSteps.mediationBeneficiaryInfo] = {
valid:
......
......
......@@ -25,7 +25,6 @@ $orange-warning: #da6c2e;
/* OTHERS */
$blue: #348899;
$blue-light: #c9ecf3;
$blueRdvs: #1f2952;
/* APP COLORS */
$primary-color: $red;
$primary-color-dark: $red-dark;
......
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment