Skip to content

Commit 3bd6c32

Browse files
adminadmin
authored andcommitted
feat: 1265 page historique renouvellement agrement
1 parent 7fa6bea commit 3bd6c32

File tree

18 files changed

+424
-50
lines changed

18 files changed

+424
-50
lines changed

.talismanrc

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ fileignoreconfig:
247247
checksum: e22412a54064dea24f7e200e9887726a4ef110f13570feea628c70c4fe2886a9
248248
- filename: packages/frontend-usagers/src/components/agrement/etapes-avancement.vue
249249
checksum: 88b30646940faf9e0726799588b6445f5292d9afd912df7bbc91ae61627b93b6
250+
- filename: packages/frontend-usagers/src/components/agrement/historique.vue
251+
checksum: 054caab3085a920f1a5017decf60ebd686cc2c1695eba776c40f5bdb3521dffc
250252
- filename: packages/frontend-usagers/src/components/agrement/projets/listeSejours.vue
251253
checksum: 8fedbc8821c31208f6a874718cd0f8805cd7990365928ce883ce1dcbd843decd
252254
- filename: packages/frontend-usagers/src/components/agrement/projets/sejoursPrevus.vue
@@ -273,11 +275,6 @@ fileignoreconfig:
273275
checksum: 846543dfd36589a6fe10859c28fe0ca4e3a4f084f58ea56b42fce695d30b31c8
274276
- filename: packages/frontend-usagers/src/components/utils/TableFull.vue
275277
checksum: 05743cc5c3dfd946e8ee41cb11571b1e505bbdb5260e9c59a53e4ca16cb11f08
276-
- filename: packages/frontend-usagers/src/pages/connexion/enregistrement.vue
277-
checksum: dd393d7c31566dfa3864bb9a63fe98f9bf040cc8321e8cad0f74e8634e8365f8
278-
- filename: packages/frontend-usagers/src/pages/connexion/index.vue
279-
checksum: fcacdc94675ff0c9eedddafc751e3dc1149edbc8d853494e3c42d935677eceff
280-
checksum: 6a0259a5bfca987ea2d83fb1fa90116d8dda92335214993671111d7a04425429
281278
- filename: packages/frontend-usagers/src/pages/agrement/[[agrementId]].vue
282279
checksum: 9f1b4a0e00392a84b23746ab66cad677bddc49b1f9999454b123cb7111c204c3
283280
- filename: packages/frontend-usagers/src/pages/connexion/enregistrement.vue

packages/backend/src/usagers/agrements/agrements.controller.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ export const AgrementController = {
4242
next(error);
4343
}
4444
},
45+
async getHistory(
46+
req: RouteRequest<AgrementUsagersRoutes["GetHistory"]>,
47+
res: RouteResponse<AgrementUsagersRoutes["GetHistory"]>,
48+
next: NextFunction,
49+
) {
50+
try {
51+
const history = await AgrementService.getHistory(
52+
Number(req.validatedParams!.agrementId),
53+
);
54+
res.status(200).json({ history });
55+
} catch (error) {
56+
next(error);
57+
}
58+
},
4559
async post(
4660
req: RouteRequest<AgrementUsagersRoutes["PostAgrement"]>,
4761
res: RouteResponse<AgrementUsagersRoutes["PostAgrement"]>,
@@ -51,9 +65,22 @@ export const AgrementController = {
5165
const agrement = req.validatedBody!;
5266
try {
5367
const id = await AgrementService.save(agrement);
68+
log.i("Agrement saved", { id });
69+
70+
// todo: supprimer/adapter cet exemple de tracking
71+
// await AgrementService.trackEvent({
72+
// agrementId: id,
73+
// boUserId: null,
74+
// metadata: null,
75+
// source: "Organisateur",
76+
// type: "Mise à jour page",
77+
// typePrecision: "Renuvellement en cours de complétion",
78+
// usagerUserId: 1,
79+
// });
80+
5481
res.json({ id });
5582
} catch (err) {
56-
log.w("DONE with error");
83+
log.w("DONE with error", err);
5784
next(err);
5885
}
5986
},

packages/backend/src/usagers/agrements/agrements.repository.ts

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import type {
2+
AgrementHistoryItem,
3+
AgrementHistoryRow,
4+
} from "@vao/shared-bridge";
15
import { AgrementDto } from "@vao/shared-bridge";
26

37
import { saveAdresse } from "../../services/adresse";
@@ -226,7 +230,6 @@ export const AgrementsRepository = {
226230
return agrementId;
227231
} catch (err) {
228232
await client.query("ROLLBACK");
229-
console.error("Erreur lors de la création d'un agrément :", err);
230233
throw err;
231234
} finally {
232235
client.release();
@@ -349,6 +352,104 @@ export const AgrementsRepository = {
349352

350353
return agrementDto;
351354
},
355+
async getHistory(agrementId: number): Promise<AgrementHistoryItem[]> {
356+
const client = await getPool().connect();
357+
try {
358+
const query = `
359+
SELECT
360+
h.id,
361+
h.source,
362+
h.agrement_id,
363+
h.usager_user_id,
364+
u.nom AS usager_nom,
365+
u.prenom AS usager_prenom,
366+
u.mail AS usager_mail,
367+
h.bo_user_id,
368+
b.nom AS bo_nom,
369+
b.prenom AS bo_prenom,
370+
b.mail AS bo_mail,
371+
h.type,
372+
h.type_precision,
373+
h.metadata,
374+
h.created_at
375+
FROM front.agrement_history h
376+
LEFT JOIN front.users u ON h.usager_user_id = u.id
377+
LEFT JOIN back.users b ON h.bo_user_id = b.id
378+
WHERE h.agrement_id = $1
379+
ORDER BY h.created_at DESC;
380+
`;
381+
const result = await client.query(query, [agrementId]);
382+
return result.rows.map((row: AgrementHistoryRow) => ({
383+
agrement_id: row.agrement_id,
384+
bo_user: row.bo_user_id
385+
? {
386+
id: row.bo_user_id,
387+
mail: row.bo_mail,
388+
nom: row.bo_nom,
389+
prenom: row.bo_prenom,
390+
}
391+
: null,
392+
created_at: row.created_at,
393+
id: row.id,
394+
metadata: row.metadata,
395+
source: row.source,
396+
type: row.type,
397+
type_precision: row.type_precision,
398+
usager_user: row.usager_user_id
399+
? {
400+
id: row.usager_user_id,
401+
mail: row.usager_mail,
402+
nom: row.usager_nom,
403+
prenom: row.usager_prenom,
404+
}
405+
: null,
406+
}));
407+
} finally {
408+
client.release();
409+
}
410+
},
411+
async insertHistoryEvent({
412+
source,
413+
agrementId,
414+
usagerUserId,
415+
boUserId,
416+
type,
417+
typePrecision,
418+
metadata,
419+
}: {
420+
source: string;
421+
agrementId: number;
422+
usagerUserId?: number | null;
423+
boUserId?: number | null;
424+
type?: string | null;
425+
typePrecision?: string | null;
426+
metadata?: Record<string, unknown> | null;
427+
}): Promise<number> {
428+
const query = `
429+
INSERT INTO front.agrement_history (
430+
source,
431+
agrement_id,
432+
usager_user_id,
433+
bo_user_id,
434+
type,
435+
type_precision,
436+
metadata,
437+
created_at
438+
) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
439+
RETURNING id;
440+
`;
441+
const values = [
442+
source,
443+
agrementId,
444+
usagerUserId ?? null,
445+
boUserId ?? null,
446+
type ?? null,
447+
typePrecision ?? null,
448+
metadata ?? null,
449+
];
450+
const { rows } = await getPool().query(query, values);
451+
return rows[0].id;
452+
},
352453
async update({
353454
agrement,
354455
dateFinValidite,
@@ -505,7 +606,6 @@ export const AgrementsRepository = {
505606
await client.query("COMMIT");
506607
} catch (err) {
507608
await client.query("ROLLBACK");
508-
console.error("Erreur lors de la mise à jour de l'agrément :", err);
509609
throw err;
510610
} finally {
511611
client.release();

packages/backend/src/usagers/agrements/agrements.route.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ router.post(
2424
AgrementController.post,
2525
);
2626

27+
// TODO: Ajouter requestValidatorMiddleware(AgrementUsagersRoutesSchema["GetAllActivites"]) pour valider la route des activités
2728
router.get("/activites", checkJWT, AgrementController.getAllActivites);
2829

30+
router.get(
31+
"/history/:agrementId",
32+
checkJWT,
33+
requestValidatorMiddleware(AgrementUsagersRoutesSchema["GetHistory"]),
34+
checkPermissionAgrement,
35+
AgrementController.getHistory,
36+
);
37+
2938
export default router;

packages/backend/src/usagers/agrements/agrements.service.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ export const AgrementService = {
5050
libelle: activite.libelle,
5151
}));
5252
},
53+
async getHistory(agrementId: number) {
54+
const history = await AgrementsRepository.getHistory(agrementId);
55+
return history;
56+
},
5357
async save(agrement: AgrementDto): Promise<number> {
5458
const dateFinValidite = addYears(agrement?.dateObtentionCertificat, 5);
5559
let agrementId = null;
@@ -69,4 +73,15 @@ export const AgrementService = {
6973
}
7074
return agrementId;
7175
},
76+
async trackEvent(event: {
77+
source: string;
78+
agrementId: number;
79+
usagerUserId?: number | null;
80+
boUserId?: number | null;
81+
type?: string | null;
82+
typePrecision?: string | null;
83+
metadata?: Record<string, unknown> | null;
84+
}) {
85+
return AgrementsRepository.insertHistoryEvent(event);
86+
},
7287
};

packages/frontend-usagers/src/components/agrement/etape-avancement.vue

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
></span>
88
<span v-if="state === 'success'" class="fr-sr-only">Étape terminée</span>
99
<span
10-
v-else-if="state === 'waiting'"
10+
v-if="state === 'waiting'"
1111
class="fr-icon-time-line icone icone--waiting"
1212
aria-hidden="true"
1313
></span>
14-
<span v-else-if="state === 'waiting'" class="fr-sr-only">Étape en attente</span>
14+
<span v-if="state === 'waiting'" class="fr-sr-only">Étape en attente</span>
1515
<div class="texte">
1616
<p class="texte-intitule">{{ libelle }}</p>
1717
<p class="texte-temporalite">
18-
<slot name="temporalite">{{ temporalite }}</slot>
18+
<slot name="temporalite"></slot>
1919
</p>
2020
</div>
2121
<p class="entite">{{ entite }}</p>
@@ -33,11 +33,6 @@ defineProps({
3333
type: String,
3434
required: true,
3535
},
36-
temporalite: {
37-
type: String,
38-
required: false,
39-
default: "",
40-
},
4136
entite: {
4237
type: String,
4338
required: true,
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<template>
2+
<div class="card">
3+
<div class="texte">
4+
<p class="texte-intitule">{{ type ?? "" }}</p>
5+
<p class="texte-temporalite">
6+
{{ formatFR(created_at) ?? "" }} - {{ type_precision ?? "" }}
7+
</p>
8+
</div>
9+
<p class="user">
10+
{{ usager_user?.mail ?? "" }}
11+
</p>
12+
</div>
13+
</template>
14+
15+
<script setup lang="ts">
16+
17+
import type { AgrementHistoryItem } from "@vao/shared-bridge";
18+
import { formatFR } from "@vao/shared-bridge";
19+
20+
const props = defineProps<{
21+
entry: AgrementHistoryItem
22+
}>();
23+
const { type, created_at, type_precision, usager_user } = props.entry;
24+
</script>
25+
<style scoped lang="scss">
26+
.card {
27+
display: flex;
28+
align-items: flex-start;
29+
width: 100%;
30+
min-height: 86px;
31+
border-radius: 4px;
32+
padding: 1rem;
33+
border: 1px solid
34+
var(--light-decisions-background-background-alt-grey-active, #cfcfcf);
35+
@media (max-width: 597px) {
36+
min-height: 110px;
37+
height: 110px;
38+
}
39+
}
40+
.card p {
41+
margin: 0;
42+
}
43+
.icone--success {
44+
color: green;
45+
}
46+
.texte {
47+
display: flex;
48+
flex-direction: column;
49+
gap: 4px;
50+
margin-left: 10px;
51+
flex-grow: 1;
52+
}
53+
.texte-intitule {
54+
font-size: 16px;
55+
font-style: normal;
56+
font-weight: 700;
57+
color: var(--light-decisions-artwork-artwork-major-blue-france, #000091);
58+
}
59+
.texte-temporalite {
60+
font-size: 14px;
61+
}
62+
.entite {
63+
flex-shrink: 0;
64+
font-size: 14px;
65+
}
66+
</style>

0 commit comments

Comments
 (0)