Skip to content

Commit da09df7

Browse files
abelkhaymarc-gavanier
authored andcommitted
feat: score completion field presence
1 parent ec0f94e commit da09df7

File tree

4 files changed

+123
-43
lines changed

4 files changed

+123
-43
lines changed
Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
<small>
1+
<small class="mx-1">
22
<span role="img" class="ri-numbers-fill ri-xl text-primary" aria-hidden="true"></span>
33
Cette fiche est remplie à
44
<span class="text-primary fw-bold">{{ scoreCompletion(lieuMediationNumerique) }}%</span>
55
</small>
6-
<div class="progress my-2">
6+
<div class="progress my-2 mx-1">
77
<div
88
class="progress-bar"
99
role="progressbar"
@@ -13,4 +13,37 @@
1313
aria-valuemin="0"
1414
aria-valuemax="100"></div>
1515
</div>
16-
<small class="text-primary">Vous pouvez mettre à jour ces informations en suivant les informations en bas de la fiche.</small>
16+
<small class="text-primary mx-1">
17+
Vous pouvez mettre à jour ces informations en suivant les informations en bas de la fiche.
18+
</small>
19+
<app-collapse #collapseScoreCompletion [control]="collapseScoreCompletionControl" aria-labelledby="headingAccesLibre">
20+
<table class="table">
21+
<tbody class="table-group">
22+
<tr
23+
[ngClass]="field.presence ? 'border-primary' : 'border-dark'"
24+
*ngFor="let field of sortScoreCompletionPresence(scoreCompletionPresence(lieuMediationNumerique))">
25+
<th scope="row" class="text-start fw-bold w-100">
26+
<small class="fw-bold" [ngClass]="field.presence ? 'text-primary' : 'text-dark'">{{ field.name }}</small>
27+
</th>
28+
<td class="text-end fw-bold">
29+
<small class="fw-bold" [ngClass]="field.presence ? 'text-primary' : 'text-dark'">
30+
<span *ngIf="field.presence" role="img" class="ri-check-line ri-xl text-primary" aria-hidden="true"></span>
31+
<span *ngIf="!field.presence" role="img" class="ri-close-line ri-xl text-dark" aria-hidden="true"></span>
32+
</small>
33+
</td>
34+
</tr>
35+
</tbody>
36+
</table>
37+
</app-collapse>
38+
<div class="d-flex justify-content-end">
39+
<button
40+
class="btn btn-link text-body fw-bold p-0"
41+
#collapseScoreCompletionControl
42+
aria-controls="collapseScoreCompletion"
43+
type="button"
44+
[class.collapsed]="collapseScoreCompletion.isCollapsed$ | async"
45+
[attr.aria-expanded]="collapseScoreCompletion.isExpanded$ | async"
46+
(click)="collapseScoreCompletion.toggle()">
47+
<small class="text-right">{{ (collapseScoreCompletion.isCollapsed$ | async) ? 'Détails' : 'Réduire' }}</small>
48+
</button>
49+
</div>

src/features/cartographie/components/lieux-mediation-numerique-details/score-completion/score-completion.component.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
2-
import { scoreCompletion } from './score-completion.presenter';
2+
import { scoreCompletionPresence, scoreCompletion } from './score-completion.presenter';
33
import { LieuMediationNumeriqueDetailsPresentation } from '@features/cartographie/presenters';
4+
import { ScorePresence } from './score-completion.presentation';
45

56
@Component({
67
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -11,4 +12,9 @@ export class ScoreCompletionComponent {
1112
@Input() public lieuMediationNumerique!: LieuMediationNumeriqueDetailsPresentation;
1213

1314
public scoreCompletion = scoreCompletion;
15+
public scoreCompletionPresence = scoreCompletionPresence;
16+
17+
public sortScoreCompletionPresence(scorePresence: ScorePresence[]): ScorePresence[] {
18+
return scorePresence.sort((a, b) => Number(b.presence) - Number(a.presence));
19+
}
1420
}
Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,64 @@
11
export const TOTAL_SCORE_COMPLETION: number = 41;
22

3-
type Score = number;
3+
export type ScoreDetail = {
4+
score: number;
5+
name: string;
6+
};
7+
8+
export type ScorePresence = {
9+
name: string;
10+
presence: boolean;
11+
};
412

513
type ScoreCompletion = {
6-
[key: string]: Score | ScoreContact | ScorePresentation | ScoreLocalisation;
14+
[key: string]: ScoreDetail | ScoreContact | ScorePresentation | ScoreLocalisation;
715
};
816

917
type ScoreContact = {
10-
telephone: Score;
11-
courriel: Score;
12-
site_web: Score;
18+
telephone: ScoreDetail;
19+
courriel: ScoreDetail;
20+
site_web: ScoreDetail;
1321
};
1422

1523
type ScorePresentation = {
16-
presentation_detail: Score;
17-
presentation_resume: Score;
24+
presentation_detail: ScoreDetail;
25+
presentation_resume: ScoreDetail;
1826
};
1927

2028
type ScoreLocalisation = {
21-
latitude: Score;
22-
longitude: Score;
29+
latitude: ScoreDetail;
30+
longitude: ScoreDetail;
2331
};
2432

2533
export const scoreCompletionTable: ScoreCompletion = {
26-
nom: 2,
27-
adresse: 2,
28-
commune: 2,
29-
code_postal: 2,
30-
services: 2,
31-
horaires: 2,
32-
typologies: 2,
34+
nom: { score: 2, name: 'Nom' },
35+
adresse: { score: 2, name: 'Adresse' },
36+
commune: { score: 2, name: 'Commune' },
37+
code_postal: { score: 2, name: 'Code postal' },
38+
services: { score: 2, name: 'Services' },
39+
horaires: { score: 2, name: 'Horaires' },
40+
typologies: { score: 2, name: 'Typologie' },
3341
contact: {
34-
telephone: 2,
35-
courriel: 2,
36-
site_web: 2
42+
telephone: { score: 2, name: 'Téléphone' },
43+
courriel: { score: 2, name: 'Courriel' },
44+
site_web: { score: 2, name: 'Site web' }
3745
},
3846
presentation: {
39-
presentation_detail: 2,
40-
presentation_resume: 2
47+
presentation_detail: { score: 2, name: 'Présentation détaillée' },
48+
presentation_resume: { score: 2, name: 'Présentation résumée' }
4149
},
42-
date_maj: 2,
43-
publics_accueillis: 2,
44-
conditions_acces: 2,
45-
labels_nationaux: 2,
46-
modalites_accompagnement: 2,
47-
accessibilite: 1,
50+
date_maj: { score: 2, name: 'Date de mise à jour' },
51+
publics_accueillis: { score: 2, name: 'Publics accueillis' },
52+
conditions_acces: { score: 2, name: 'Conditions d’accès' },
53+
labels_nationaux: { score: 2, name: 'Label nationaux' },
54+
autres_labels: { score: 2, name: 'Autres labels' },
55+
modalites_accompagnement: { score: 2, name: 'Modalités d’accompagnement' },
56+
accessibilite: { score: 1, name: 'Accessibilité' },
4857
localisation: {
49-
latitude: 1,
50-
longitude: 1
58+
latitude: { score: 1, name: 'Latitude' },
59+
longitude: { score: 1, name: 'Longitude' }
5160
},
52-
prise_rdv: 1,
53-
source: 1,
54-
pivot: 2
61+
prise_rdv: { score: 1, name: 'Prise de RDV' },
62+
source: { score: 1, name: 'Source' },
63+
pivot: { score: 2, name: 'Pivot' }
5564
};
Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { LieuMediationNumeriqueDetailsPresentation } from '../../../../cartographie/presenters';
2-
import { TOTAL_SCORE_COMPLETION, scoreCompletionTable } from './score-completion.presentation';
2+
import { ScoreDetail, ScorePresence, TOTAL_SCORE_COMPLETION, scoreCompletionTable } from './score-completion.presentation';
3+
4+
const NESTED_FIELDS: string[] = ['contact', 'presentation', 'localisation'];
35

46
const calculateNestedFieldScore = (
57
nestedFieldName: string,
@@ -9,24 +11,54 @@ const calculateNestedFieldScore = (
911
const nestedFieldValue = lieuMediationNumerique[nestedFieldName as keyof LieuMediationNumeriqueDetailsPresentation];
1012
Object.entries(scoreCompletionTable[nestedFieldName]).forEach(([key, value]) => {
1113
if (nestedFieldValue && nestedFieldValue.hasOwnProperty(key)) {
12-
nestedScore += value;
14+
nestedScore += value.score;
1315
}
1416
});
1517
return nestedScore;
1618
};
1719

1820
export const scoreCompletion = (lieuMediationNumerique: LieuMediationNumeriqueDetailsPresentation): number => {
19-
const nestedFields: string[] = ['contact', 'presentation', 'localisation'];
20-
2121
const scoreSimpleField: number = Object.keys(scoreCompletionTable)
22-
.filter((field) => !nestedFields.includes(field) && lieuMediationNumerique.hasOwnProperty(field))
23-
.reduce((score, curr) => score + (scoreCompletionTable[curr] as number), 0);
22+
.filter((field) => !NESTED_FIELDS.includes(field) && lieuMediationNumerique.hasOwnProperty(field))
23+
.reduce((score, curr) => score + (scoreCompletionTable[curr] as ScoreDetail).score, 0);
2424

25-
const scoreNestedField: number = nestedFields.reduce(
25+
const scoreNestedField: number = NESTED_FIELDS.reduce(
2626
(score, nestedField) => score + calculateNestedFieldScore(nestedField, lieuMediationNumerique),
2727
0
2828
);
2929

3030
const scoreCompletionPercent: number = ((scoreSimpleField + scoreNestedField) / TOTAL_SCORE_COMPLETION) * 100;
3131
return Math.round(scoreCompletionPercent);
3232
};
33+
34+
export const scoreCompletionPresence = (lieuMediationNumerique: LieuMediationNumeriqueDetailsPresentation): ScorePresence[] => {
35+
const fieldPresence: ScorePresence[] = [];
36+
37+
Object.keys(scoreCompletionTable).forEach((field) => {
38+
if (!NESTED_FIELDS.includes(field)) {
39+
fieldPresence.push({
40+
name: (scoreCompletionTable[field] as ScoreDetail).name,
41+
presence: lieuMediationNumerique.hasOwnProperty(field)
42+
});
43+
}
44+
});
45+
46+
const nestedFieldsPresence = (
47+
nestedFieldName: string,
48+
lieuMediationNumerique: LieuMediationNumeriqueDetailsPresentation
49+
): void => {
50+
const nestedFieldValue = lieuMediationNumerique[nestedFieldName as keyof LieuMediationNumeriqueDetailsPresentation] || {};
51+
Object.keys(scoreCompletionTable[nestedFieldName]).forEach((key) => {
52+
fieldPresence.push({
53+
name: (scoreCompletionTable[nestedFieldName] as Record<string, ScoreDetail>)[key].name,
54+
presence: nestedFieldValue.hasOwnProperty(key)
55+
});
56+
});
57+
};
58+
59+
NESTED_FIELDS.forEach((nestedField) => {
60+
nestedFieldsPresence(nestedField, lieuMediationNumerique);
61+
});
62+
63+
return fieldPresence;
64+
};

0 commit comments

Comments
 (0)