Skip to content

Commit 24cbb90

Browse files
committed
List mesure audit statuts with trpc
1 parent fa430e8 commit 24cbb90

20 files changed

+604
-296
lines changed

apps/app/src/referentiels/ReferentielTable/useReferentiel.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ const useReferentielData = (referentiel: string | null) => {
210210
...(actionById[action.action_id] || {}),
211211
}));
212212
},
213-
[actionById]
213+
[actionById, rows]
214214
);
215215

216216
return {
@@ -266,7 +266,7 @@ const isSousAction = (action: ActionReferentiel) =>
266266
* mais cela paraissait plus compliqué
267267
* Ref: https://github.com/TanStack/table/blob/v7/src/plugin-hooks/useExpanded.js
268268
*/
269-
const useToggleRowExpandedReducer = <ActionSubset extends IAction>(
269+
export const useToggleRowExpandedReducer = <ActionSubset extends IAction>(
270270
rows: TActionsSubset<ActionSubset>
271271
) => {
272272
// état courant des touches "modificatrices"

apps/app/src/referentiels/audits/AuditSuivi/Cells.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { Icon } from '@/ui';
22
import { CellProps } from 'react-table';
33
import { BadgeAuditStatut } from '../BadgeAuditStatut';
4-
import { TAuditSuiviRow } from './queries';
4+
import { MesureAuditStatut } from './useTableData';
55

6-
type TCellProps = CellProps<TAuditSuiviRow>;
6+
type TCellProps = CellProps<MesureAuditStatut>;
77

88
/**
99
* Affiche une cellule contenant le statut d'audit d'une action
1010
*/
1111
export const CellAuditStatut = (props: TCellProps) => {
1212
const { value, row } = props;
1313

14-
return row.original.type === 'action' ? (
14+
return row.original.mesureType === 'action' ? (
1515
<BadgeAuditStatut statut={value || 'non_audite'} />
1616
) : null;
1717
};
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
import { Meta} from '@storybook/nextjs';
2-
import {action} from 'storybook/actions';
3-
import {FiltreOrdreDuJour} from './FiltreOrdreDuJour';
1+
import { Meta } from '@storybook/nextjs';
2+
import { action } from 'storybook/actions';
3+
import { FiltreOrdreDuJour } from './FiltreOrdreDuJour';
44

55
export default {
66
component: FiltreOrdreDuJour,
77
} as Meta;
88

99
export const Tous = {
1010
args: {
11-
filters: {ordre_du_jour: ['tous']},
11+
filters: { ordreDuJour: ['tous'] },
1212
setFilters: action('setFilters'),
1313
},
1414
};
1515

1616
export const Selection = {
1717
args: {
18-
filters: {ordre_du_jour: ['true']},
18+
filters: { ordreDuJour: ['true'] },
1919
setFilters: action('setFilters'),
2020
},
2121
};

apps/app/src/referentiels/audits/AuditSuivi/FiltreOrdreDuJour.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import {
55
import { ITEM_ALL } from '@/ui';
66
import { TFiltreProps } from './filters';
77

8-
export const FILTER = 'ordre_du_jour';
9-
108
export const filterItems = [
119
{ value: ITEM_ALL, label: 'Tous' },
1210
{ value: 'true', label: 'Oui' },
@@ -27,9 +25,11 @@ export const FiltreOrdreDuJour = (props: TFiltreProps) => {
2725
label="A discuter - Séance d'audit"
2826
/>
2927
)}
30-
values={filters[FILTER]}
28+
values={filters.ordreDuJour}
3129
options={filterItems}
32-
onSelect={(newValues) => setFilters({ ...filters, [FILTER]: newValues })}
30+
onSelect={(newValues) =>
31+
setFilters({ ...filters, ordreDuJour: newValues })
32+
}
3333
/>
3434
);
3535
};

apps/app/src/referentiels/audits/AuditSuivi/Table.tsx

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useMemo, useRef } from 'react';
1+
import { useEffect, useMemo } from 'react';
22
import {
33
CellProps,
44
Column,
@@ -7,13 +7,13 @@ import {
77
useFlexLayout,
88
useTable,
99
} from 'react-table';
10+
import { ReferentielTable } from '../../ReferentielTable';
11+
import { CellAction } from '../../ReferentielTable/CellAction';
1012
import { CellAuditStatut, CellCheckmark } from './Cells';
1113
import { FiltreAuditStatut } from './FiltreAuditStatut';
1214
import { FiltreOrdreDuJour } from './FiltreOrdreDuJour';
1315
import { TAuditSuiviRow } from './queries';
14-
import { TableData } from './useTableData';
15-
import { CellAction } from '../../ReferentielTable/CellAction';
16-
import { ReferentielTable } from '../../ReferentielTable';
16+
import { MesureAuditStatut, TableData } from './useTableData';
1717

1818
export type TAuditSuiviTableProps = {
1919
tableData: TableData;
@@ -22,18 +22,18 @@ export type THeaderProps = HeaderProps<TAuditSuiviRow> & {
2222
setFilters: (filters: string[]) => void;
2323
};
2424
export type TCellProps = CellProps<TAuditSuiviRow>;
25-
export type TColumn = Column<TAuditSuiviRow>;
25+
export type TColumn = Column<MesureAuditStatut>;
2626

2727
// défini les colonnes de la table
2828
const COLUMNS: TColumn[] = [
2929
{
30-
accessor: 'nom', // la clé pour accéder à la valeur
30+
accessor: 'mesureNom', // la clé pour accéder à la valeur
3131
Header: 'Sous-actions', // rendu dans la ligne d'en-tête
3232
Cell: CellAction as any, // rendu d'une cellule
3333
width: '100%',
3434
},
3535
{
36-
accessor: 'ordre_du_jour',
36+
accessor: 'ordreDuJour',
3737
Header: FiltreOrdreDuJour as any,
3838
Cell: CellCheckmark,
3939
width: 180,
@@ -65,15 +65,12 @@ export const Table = (props: TAuditSuiviTableProps) => {
6565
const { toggleAllRowsExpanded } = tableInstance;
6666

6767
// initialement tout est déplié
68-
const isInitialLoading = useRef(true);
6968
useEffect(() => {
70-
if (table?.data?.length && isInitialLoading.current) {
71-
isInitialLoading.current = false;
69+
if (table.data.length) {
7270
toggleAllRowsExpanded(true);
7371
}
74-
}, [table?.data?.length, toggleAllRowsExpanded, isInitialLoading]);
72+
}, [table.data.length, toggleAllRowsExpanded]);
7573

76-
// rendu de la table
7774
return (
7875
<ReferentielTable
7976
dataTest="suivi-audit"

apps/app/src/referentiels/audits/AuditSuivi/filters.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
import { ITEM_ALL } from '@/ui';
22

33
export type TFilters = {
4-
ordre_du_jour: string[];
4+
ordreDuJour: string[];
55
statut: string[];
66
};
77

88
export type TSetFilters = (newFilter: TFilters | null) => void;
99

1010
// valeurs par défaut des filtres
1111
export const initialFilters: TFilters = {
12-
ordre_du_jour: [ITEM_ALL],
12+
ordreDuJour: [ITEM_ALL],
1313
statut: [ITEM_ALL],
1414
};
1515

1616
export const noFilters: TFilters = initialFilters;
1717

1818
// mapping nom des filtres => params dans l'url
1919
export const nameToShortNames = {
20-
ordre_du_jour: 'o',
20+
ordreDuJour: 'o',
2121
statut: 's',
2222
};
2323

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,9 @@
1-
import { DBClient } from '@/api';
2-
3-
import { ITEM_ALL } from '@/ui';
41
import { ActionReferentiel } from '../../DEPRECATED_scores.types';
52
import { TAuditStatut } from '../types';
6-
import { TFilters } from './filters';
73

84
// un sous-ensemble des champs pour alimenter notre table
95
export type TAuditSuiviRow = ActionReferentiel & {
106
action_id: string;
117
statut: TAuditStatut;
12-
ordre_du_jour: boolean;
13-
};
14-
15-
// toutes les entrées d'un référentiel pour une collectivité et des filtres donnés
16-
export const fetchRows = async (
17-
supabase: DBClient,
18-
collectivite_id: number | null,
19-
referentiel: string | null,
20-
filters: TFilters
21-
) => {
22-
// la requête
23-
const query = supabase
24-
.from('suivi_audit')
25-
.select('action_id,statut,ordre_du_jour')
26-
.match({ collectivite_id, referentiel });
27-
28-
// applique les filtres
29-
const { statut, ordre_du_jour } = filters;
30-
const and = [];
31-
if (statut?.length && !statut.includes(ITEM_ALL)) {
32-
const statuts = statut.join(',');
33-
and.push(`or(statut.in.(${statut}), statuts.ov.{${statuts}})`);
34-
}
35-
if (ordre_du_jour?.length && !ordre_du_jour.includes(ITEM_ALL)) {
36-
const odj = ordre_du_jour.map((o) => o === 'true');
37-
and.push(
38-
`or(ordre_du_jour.in.(${odj}), ordres_du_jour.ov.{${odj.join(',')}})`
39-
);
40-
}
41-
if (and.length) {
42-
query.or(`and(${and.join(',')})`);
43-
}
44-
45-
// attends les données
46-
const { error, data, count } = await query;
47-
48-
if (error) {
49-
throw new Error(error.message);
50-
}
51-
52-
const rows = data as TAuditSuiviRow[];
53-
54-
return { rows, count };
8+
ordreDuJour: boolean;
559
};
Lines changed: 91 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
11
import { useCollectiviteId } from '@/api/collectivites';
2-
import { useSupabase } from '@/api/utils/supabase/use-supabase';
2+
import { RouterOutput, useTRPC } from '@/api/utils/trpc/client';
33
import { useSearchParams } from '@/app/core-logic/hooks/query';
4+
import {
5+
ActionTypeEnum,
6+
getIdentifiantFromActionId,
7+
getLevelFromActionId,
8+
} from '@/domain/referentiels';
9+
import { ITEM_ALL } from '@/ui';
410
import { useQuery } from '@tanstack/react-query';
511
import { TableOptions } from 'react-table';
612
import { useReferentielId } from '../../referentiel-context';
7-
import { useReferentiel } from '../../ReferentielTable/useReferentiel';
813
import { initialFilters, nameToShortNames, TFilters } from './filters';
9-
import { fetchRows, TAuditSuiviRow } from './queries';
1014

1115
export type UseTableData = () => TableData;
1216

17+
export type MesureAuditStatut =
18+
RouterOutput['referentiels']['labellisations']['listMesureAuditStatuts'][0];
19+
1320
export type TableData = {
1421
/** données à passer à useTable */
1522
table: Pick<
16-
TableOptions<TAuditSuiviRow>,
23+
TableOptions<MesureAuditStatut>,
1724
'data' | 'getRowId' | 'getSubRows' | 'autoResetExpanded'
1825
>;
1926
/** Indique que le chargement des données est en cours */
@@ -22,10 +29,6 @@ export type TableData = {
2229
filters: TFilters;
2330
/** Nombre de filtres actifs */
2431
filtersCount: number;
25-
/** Nombre de lignes après filtrage */
26-
count: number;
27-
/** Nombre total de lignes */
28-
total: number;
2932
/** pour remettre à jour les filtres */
3033
setFilters: (newFilter: TFilters) => void;
3134
};
@@ -36,7 +39,7 @@ export type TableData = {
3639
export const useTableData: UseTableData = () => {
3740
const collectiviteId = useCollectiviteId();
3841
const referentielId = useReferentielId();
39-
const supabase = useSupabase();
42+
const trpc = useTRPC();
4043

4144
// filtre initial
4245
const [filters, setFilters, filtersCount] = useSearchParams<TFilters>(
@@ -46,27 +49,91 @@ export const useTableData: UseTableData = () => {
4649
);
4750

4851
// chargement des données en fonction des filtres
49-
const { data, isLoading } = useQuery({
50-
queryKey: ['audit-suivi', collectiviteId, referentielId, filters],
51-
queryFn: () => fetchRows(supabase, collectiviteId, referentielId, filters),
52+
const { data: allMesuresStatuts, isLoading } = useQuery(
53+
trpc.referentiels.labellisations.listMesureAuditStatuts.queryOptions({
54+
collectiviteId,
55+
referentielId,
56+
})
57+
);
58+
59+
const { statut, ordreDuJour } = filters;
60+
61+
const filterBySelectedStatutAndOrdreDuJour = (row: MesureAuditStatut) => {
62+
// Pas de filtre sur les axes et sous-axes, uniquement les mesures
63+
if (row.mesureType !== ActionTypeEnum.ACTION) {
64+
return true;
65+
}
66+
67+
if (!statut.includes(ITEM_ALL) && !statut.includes(row.statut)) {
68+
return false;
69+
}
70+
71+
if (
72+
!ordreDuJour.includes(ITEM_ALL) &&
73+
!ordreDuJour.map((o) => o === 'true').includes(row.ordreDuJour)
74+
) {
75+
return false;
76+
}
77+
78+
return true;
79+
};
80+
81+
const mapToMatchReferentielTableRow = (row: MesureAuditStatut) => ({
82+
...row,
83+
84+
// Pour compatibilité avec la gestion de l'affichage de ReferentielTable
85+
// À cleaner quand tous les usages de `useReferentiel` auront été supprimés
86+
action_id: row.mesureId,
87+
depth: getLevelFromActionId(row.mesureId),
88+
identifiant: getIdentifiantFromActionId(row.mesureId),
5289
});
53-
const { rows: actionsAuditStatut } = data || {};
5490

55-
// chargement du référentiel
56-
const {
57-
table,
58-
total,
59-
count,
60-
isLoading: isLoadingReferentiel,
61-
} = useReferentiel(referentielId, collectiviteId, actionsAuditStatut);
91+
const rows = (allMesuresStatuts ?? [])
92+
.filter(filterBySelectedStatutAndOrdreDuJour)
93+
.map(mapToMatchReferentielTableRow);
94+
95+
const level1Rows = rows.filter(
96+
(row) => getLevelFromActionId(row.mesureId) === 1
97+
);
98+
const level2Rows = rows.filter(
99+
(row) => getLevelFromActionId(row.mesureId) === 2
100+
);
101+
const level3Rows = rows.filter(
102+
(row) => getLevelFromActionId(row.mesureId) === 3
103+
);
62104

63105
return {
64-
table,
106+
table: {
107+
data: level1Rows.filter((level1Row) =>
108+
level3Rows.some((level3Row) =>
109+
level3Row.mesureId.startsWith(level1Row.mesureId)
110+
)
111+
),
112+
getRowId: (row) => row.mesureId,
113+
getSubRows: (parentRow) => {
114+
if (getLevelFromActionId(parentRow.mesureId) === 2) {
115+
return level3Rows.filter((level3Row) =>
116+
level3Row.mesureId.startsWith(parentRow.mesureId)
117+
);
118+
}
119+
120+
if (getLevelFromActionId(parentRow.mesureId) === 1) {
121+
return level2Rows.filter(
122+
(level2Row) =>
123+
level2Row.mesureId.startsWith(parentRow.mesureId) &&
124+
level3Rows.some((level3Row) =>
125+
level3Row.mesureId.startsWith(level2Row.mesureId)
126+
)
127+
);
128+
}
129+
130+
return [];
131+
},
132+
autoResetExpanded: false,
133+
},
65134
filters,
66135
setFilters,
67136
filtersCount,
68-
isLoading: isLoading || isLoadingReferentiel,
69-
count,
70-
total,
137+
isLoading,
71138
};
72139
};

0 commit comments

Comments
 (0)