@wasto/cms-components 1.5.10 → 1.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -47
- package/dist/index.d.mts +181 -51
- package/dist/index.d.ts +181 -51
- package/dist/index.js +3614 -3015
- package/dist/index.mjs +3021 -2418
- package/package.json +12 -2
- package/src/components/announcement-modal/AnnouncementModal.tsx +183 -183
- package/src/components/article/Article.tsx +21 -21
- package/src/components/article/ArticleCard.tsx +81 -81
- package/src/components/article/ArticleExcerpts.tsx +80 -80
- package/src/components/article/ArticleList.tsx +114 -114
- package/src/components/article/ArticleMarkdown.tsx +170 -170
- package/src/components/article/ArticleReaderModal.tsx +93 -93
- package/src/components/article/ArticleSingle.tsx +57 -57
- package/src/components/bottom-tab/BottomTab.tsx +392 -392
- package/src/components/button/Button.tsx +154 -154
- package/src/components/challenge/Badges.tsx +84 -84
- package/src/components/challenge/BadgesModal.tsx +166 -166
- package/src/components/challenge/Challenge.tsx +380 -380
- package/src/components/challenge/ChallengeModal.tsx +231 -231
- package/src/components/challenge/ChallengeVignette.tsx +249 -249
- package/src/components/challenge/ChallengesModal.tsx +243 -243
- package/src/components/challenge/QuizModal.tsx +381 -381
- package/src/components/challenge/ResultModal.tsx +144 -144
- package/src/components/chat/Chat.tsx +928 -928
- package/src/components/collection-calendar/CollectionCalendar.tsx +274 -274
- package/src/components/columns/Columns.tsx +33 -33
- package/src/components/consignes-modal/ConsignesModal.tsx +267 -267
- package/src/components/content-inset/ContentInset.tsx +33 -33
- package/src/components/header/Header.tsx +415 -391
- package/src/components/image/Image.tsx +15 -15
- package/src/components/image/ImageFrame.tsx +102 -102
- package/src/components/image/ImageMedia.tsx +31 -31
- package/src/components/image/ImageUrl.tsx +30 -30
- package/src/components/locations-map/LocationSheet.tsx +417 -417
- package/src/components/locations-map/LocationsMap.native.tsx +260 -260
- package/src/components/locations-map/LocationsMap.tsx +13 -13
- package/src/components/locations-map/LocationsMap.web.tsx +10 -10
- package/src/components/locations-map/LocationsMapShell.tsx +546 -527
- package/src/components/locations-map/LocationsMapView.tsx +108 -108
- package/src/components/locations-map/LocationsMapView.web.tsx +331 -331
- package/src/components/nav-button/NavButton.tsx +59 -59
- package/src/components/next-collection/NextCollection.tsx +636 -621
- package/src/components/notifications/NotificationsScreen.tsx +247 -247
- package/src/components/page/Page.tsx +102 -64
- package/src/components/parapraph/Paragraph.tsx +67 -67
- package/src/components/profile/ProfileScreen.tsx +236 -236
- package/src/components/push/PushTokenRegistrar.tsx +47 -0
- package/src/components/recycling-info/RecyclingInfo.tsx +189 -189
- package/src/components/safe-area/OptionalSafeAreaProvider.tsx +17 -17
- package/src/components/search/Search.tsx +320 -320
- package/src/components/select-street-modal/SelectStreetModal.tsx +475 -475
- package/src/components/title/Title.tsx +100 -100
- package/src/components/vision/Vision.tsx +447 -447
- package/src/components/vision/VisionActionCard.tsx +117 -117
- package/src/components/vision/VisionCapture.tsx +30 -30
- package/src/components/vision/VisionTapOverlay.tsx +122 -122
- package/src/contexts/AnnouncementsContext.tsx +171 -171
- package/src/contexts/AppDataContext.tsx +458 -0
- package/src/contexts/ChallengesContext.tsx +182 -182
- package/src/contexts/HeaderConfigContext.tsx +29 -0
- package/src/contexts/LocalContext.tsx +227 -227
- package/src/contexts/ThemeContext.tsx +37 -37
- package/src/contexts/WastoContext.tsx +132 -127
- package/src/helpers/blogPost.ts +72 -72
- package/src/helpers/generateQuizQuestions.ts +247 -247
- package/src/helpers/getContrastColor.ts +18 -18
- package/src/helpers/haptics.ts +20 -20
- package/src/helpers/hoursSchedule.ts +230 -230
- package/src/helpers/htmlContentToMarkdown.ts +114 -114
- package/src/helpers/layout.ts +54 -54
- package/src/helpers/loadLinearGradient.ts +22 -22
- package/src/helpers/mapLocations.ts +170 -0
- package/src/helpers/maquetteTheme.ts +76 -76
- package/src/helpers/markdownUnderlinePlugin.ts +32 -32
- package/src/helpers/nextCollection.ts +122 -0
- package/src/helpers/normalizeWasteTypeRecyclingInfo.ts +80 -80
- package/src/helpers/publicMediaUrl.ts +8 -8
- package/src/helpers/resolveApiAssetUrl.ts +21 -21
- package/src/helpers/systemRoutes.ts +7 -7
- package/src/helpers/themeStyles.ts +114 -114
- package/src/helpers/ttlCache.ts +41 -0
- package/src/helpers/useSafeAreaPadding.ts +14 -14
- package/src/hooks/useBadges.ts +100 -100
- package/src/hooks/useBlogPosts.ts +109 -105
- package/src/hooks/useChallenges.ts +110 -110
- package/src/hooks/useCities.ts +56 -56
- package/src/hooks/useCityAnnouncements.ts +118 -118
- package/src/hooks/useCollectionCalendar.ts +421 -421
- package/src/hooks/useColumns.ts +19 -19
- package/src/hooks/useConsignes.ts +62 -62
- package/src/hooks/useLocations.ts +120 -248
- package/src/hooks/useNextCollection.ts +163 -238
- package/src/hooks/useRecyclingInfo.ts +90 -90
- package/src/hooks/useWastoClient.ts +16 -16
- package/src/index.ts +161 -142
- package/src/navigation/NavigationContext.tsx +56 -56
- package/src/push/registerExpoPushToken.ts +133 -0
- package/src/sentry/initSentry.ts +120 -120
- package/src/types/Theme.ts +72 -72
- package/src/types/css-modules.d.ts +1 -1
|
@@ -1,238 +1,163 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
-
import { useLocal } from "../contexts/LocalContext";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const resolvedCityId = options.cityId ?? selectedCityId ?? null;
|
|
166
|
-
const pageSize = options.pageSize ?? 100;
|
|
167
|
-
const dateOffset = options.dateOffset ?? 0;
|
|
168
|
-
const selectedPlaceId = options.selectedPlaceId ?? localPlaceId ?? null;
|
|
169
|
-
|
|
170
|
-
const client = useWastoClient();
|
|
171
|
-
|
|
172
|
-
const [city, setCity] = useState<City | null>(null);
|
|
173
|
-
const [wasteTypes, setWasteTypes] = useState<WasteType[]>([]);
|
|
174
|
-
const [wasteCollectionPlaces, setWasteCollectionPlaces] = useState<WasteCollectionPlace[]>([]);
|
|
175
|
-
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
176
|
-
const [error, setError] = useState<string | null>(null);
|
|
177
|
-
|
|
178
|
-
const fetchData = useCallback(async () => {
|
|
179
|
-
if (!resolvedCityId) {
|
|
180
|
-
setCity(null);
|
|
181
|
-
setWasteTypes([]);
|
|
182
|
-
setWasteCollectionPlaces([]);
|
|
183
|
-
setIsLoading(false);
|
|
184
|
-
setError(null);
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
setIsLoading(true);
|
|
190
|
-
setError(null);
|
|
191
|
-
|
|
192
|
-
const [cityRes, typesRes, placesRes] = await Promise.all([
|
|
193
|
-
client.public.cities.getById(resolvedCityId),
|
|
194
|
-
client.public.cities.getWasteTypes(resolvedCityId, { pageSize }),
|
|
195
|
-
client.public.cities.getWasteCollectionPlaces(resolvedCityId, { pageSize }),
|
|
196
|
-
]);
|
|
197
|
-
|
|
198
|
-
setCity((cityRes ?? null) as City | null);
|
|
199
|
-
|
|
200
|
-
const types = (typesRes as { data?: unknown })?.data;
|
|
201
|
-
setWasteTypes(Array.isArray(types) ? (types as WasteType[]) : []);
|
|
202
|
-
|
|
203
|
-
const places = (placesRes as { data?: unknown })?.data;
|
|
204
|
-
setWasteCollectionPlaces(Array.isArray(places) ? (places as WasteCollectionPlace[]) : []);
|
|
205
|
-
} catch (err) {
|
|
206
|
-
const message =
|
|
207
|
-
err instanceof Error ? err.message : typeof err === "string" ? err : "Impossible de charger les données";
|
|
208
|
-
setError(message);
|
|
209
|
-
setCity(null);
|
|
210
|
-
setWasteTypes([]);
|
|
211
|
-
setWasteCollectionPlaces([]);
|
|
212
|
-
} finally {
|
|
213
|
-
setIsLoading(false);
|
|
214
|
-
}
|
|
215
|
-
}, [client.public.cities, pageSize, resolvedCityId]);
|
|
216
|
-
|
|
217
|
-
useEffect(() => {
|
|
218
|
-
void fetchData();
|
|
219
|
-
}, [fetchData]);
|
|
220
|
-
|
|
221
|
-
const collectionsForDay = useMemo(
|
|
222
|
-
() => getCollectionsForDay(wasteTypes, wasteCollectionPlaces, dateOffset, selectedPlaceId),
|
|
223
|
-
[wasteTypes, wasteCollectionPlaces, dateOffset, selectedPlaceId]
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
return {
|
|
227
|
-
cityId: resolvedCityId,
|
|
228
|
-
city,
|
|
229
|
-
wasteTypes,
|
|
230
|
-
wasteCollectionPlaces,
|
|
231
|
-
dateOffset,
|
|
232
|
-
dateLabel: getDateLabel(dateOffset),
|
|
233
|
-
collectionsForDay,
|
|
234
|
-
isLoading,
|
|
235
|
-
error,
|
|
236
|
-
refetch: fetchData,
|
|
237
|
-
};
|
|
238
|
-
}
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { useLocal } from "../contexts/LocalContext";
|
|
3
|
+
import {
|
|
4
|
+
NEXT_EVENTS_DAY_RADIUS,
|
|
5
|
+
useOptionalAppData,
|
|
6
|
+
} from "../contexts/AppDataContext";
|
|
7
|
+
import { useWastoClient } from "./useWastoClient";
|
|
8
|
+
import {
|
|
9
|
+
getCollectionsForDay,
|
|
10
|
+
getDateLabel,
|
|
11
|
+
type City,
|
|
12
|
+
type CollectionForDay,
|
|
13
|
+
type WasteCollectionPlace,
|
|
14
|
+
type WasteType,
|
|
15
|
+
} from "../helpers/nextCollection";
|
|
16
|
+
|
|
17
|
+
export type {
|
|
18
|
+
City,
|
|
19
|
+
WasteType,
|
|
20
|
+
WasteCollectionPlace,
|
|
21
|
+
CollectionForDay,
|
|
22
|
+
} from "../helpers/nextCollection";
|
|
23
|
+
|
|
24
|
+
export { getCollectionsForDay, getDateLabel } from "../helpers/nextCollection";
|
|
25
|
+
|
|
26
|
+
export type UseNextCollectionOptions = {
|
|
27
|
+
/** Si omis, on prend `selectedCityId` depuis `LocalContext`. */
|
|
28
|
+
cityId?: string | null;
|
|
29
|
+
/** Décalage (en jours) par rapport à aujourd’hui. 0 = aujourd’hui, 1 = demain, etc. */
|
|
30
|
+
dateOffset?: number;
|
|
31
|
+
/** Lieu de collecte sélectionné. Si omis : 1er lieu disponible. */
|
|
32
|
+
selectedPlaceId?: string | null;
|
|
33
|
+
pageSize?: number;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type UseNextCollectionResult = {
|
|
37
|
+
cityId: string | null;
|
|
38
|
+
city: City | null;
|
|
39
|
+
wasteTypes: WasteType[];
|
|
40
|
+
wasteCollectionPlaces: WasteCollectionPlace[];
|
|
41
|
+
/** Décalage (en jours) utilisé pour calculer `collectionsForDay`. */
|
|
42
|
+
dateOffset: number;
|
|
43
|
+
/** Libellé “HIER / AUJOURD’HUI / DEMAIN / Dans N jour(s)” */
|
|
44
|
+
dateLabel: string;
|
|
45
|
+
/** Types de déchets collectés pour ce jour (vide si aucune collecte) */
|
|
46
|
+
collectionsForDay: CollectionForDay[];
|
|
47
|
+
isLoading: boolean;
|
|
48
|
+
error: string | null;
|
|
49
|
+
refetch: () => Promise<void>;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Récupère les données nécessaires pour afficher le bloc "Prochaine collecte":
|
|
54
|
+
* - ville
|
|
55
|
+
* - types de déchets
|
|
56
|
+
* - lieux de collecte
|
|
57
|
+
*
|
|
58
|
+
* Préfère le cache partagé `AppDataProvider` (preload API) quand disponible.
|
|
59
|
+
*/
|
|
60
|
+
export function useNextCollection(options: UseNextCollectionOptions = {}): UseNextCollectionResult {
|
|
61
|
+
const { selectedCityId, selectedPlaceId: localPlaceId } = useLocal();
|
|
62
|
+
const appData = useOptionalAppData();
|
|
63
|
+
|
|
64
|
+
const resolvedCityId = options.cityId ?? selectedCityId ?? null;
|
|
65
|
+
const pageSize = options.pageSize ?? 100;
|
|
66
|
+
const dateOffset = options.dateOffset ?? 0;
|
|
67
|
+
const selectedPlaceId = options.selectedPlaceId ?? localPlaceId ?? null;
|
|
68
|
+
|
|
69
|
+
const client = useWastoClient();
|
|
70
|
+
|
|
71
|
+
const [city, setCity] = useState<City | null>(null);
|
|
72
|
+
const [wasteTypes, setWasteTypes] = useState<WasteType[]>([]);
|
|
73
|
+
const [wasteCollectionPlaces, setWasteCollectionPlaces] = useState<WasteCollectionPlace[]>([]);
|
|
74
|
+
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
75
|
+
const [error, setError] = useState<string | null>(null);
|
|
76
|
+
|
|
77
|
+
const useShared =
|
|
78
|
+
Boolean(appData) &&
|
|
79
|
+
(options.cityId == null ||
|
|
80
|
+
options.cityId === appData?.city?.id ||
|
|
81
|
+
options.cityId === selectedCityId);
|
|
82
|
+
|
|
83
|
+
const fetchData = useCallback(async () => {
|
|
84
|
+
if (useShared && appData) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!resolvedCityId) {
|
|
89
|
+
setCity(null);
|
|
90
|
+
setWasteTypes([]);
|
|
91
|
+
setWasteCollectionPlaces([]);
|
|
92
|
+
setIsLoading(false);
|
|
93
|
+
setError(null);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
setIsLoading(true);
|
|
99
|
+
setError(null);
|
|
100
|
+
|
|
101
|
+
const [cityRes, typesRes, placesRes] = await Promise.all([
|
|
102
|
+
client.public.cities.getById(resolvedCityId),
|
|
103
|
+
client.public.cities.getWasteTypes(resolvedCityId, { pageSize }),
|
|
104
|
+
client.public.cities.getWasteCollectionPlaces(resolvedCityId, { pageSize }),
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
setCity((cityRes ?? null) as City | null);
|
|
108
|
+
|
|
109
|
+
const types = (typesRes as { data?: unknown })?.data;
|
|
110
|
+
setWasteTypes(Array.isArray(types) ? (types as WasteType[]) : []);
|
|
111
|
+
|
|
112
|
+
const places = (placesRes as { data?: unknown })?.data;
|
|
113
|
+
setWasteCollectionPlaces(Array.isArray(places) ? (places as WasteCollectionPlace[]) : []);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
const message =
|
|
116
|
+
err instanceof Error
|
|
117
|
+
? err.message
|
|
118
|
+
: typeof err === "string"
|
|
119
|
+
? err
|
|
120
|
+
: "Impossible de charger les données";
|
|
121
|
+
setError(message);
|
|
122
|
+
setCity(null);
|
|
123
|
+
setWasteTypes([]);
|
|
124
|
+
setWasteCollectionPlaces([]);
|
|
125
|
+
} finally {
|
|
126
|
+
setIsLoading(false);
|
|
127
|
+
}
|
|
128
|
+
}, [appData, client.public.cities, pageSize, resolvedCityId, useShared]);
|
|
129
|
+
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
void fetchData();
|
|
132
|
+
}, [fetchData]);
|
|
133
|
+
|
|
134
|
+
const sharedWasteTypes = useShared && appData ? appData.wasteTypes : wasteTypes;
|
|
135
|
+
const sharedPlaces = useShared && appData ? appData.wasteCollectionPlaces : wasteCollectionPlaces;
|
|
136
|
+
const sharedCity = useShared && appData ? appData.city : city;
|
|
137
|
+
|
|
138
|
+
const collectionsForDay = useMemo(() => {
|
|
139
|
+
if (
|
|
140
|
+
useShared &&
|
|
141
|
+
appData &&
|
|
142
|
+
dateOffset >= -NEXT_EVENTS_DAY_RADIUS &&
|
|
143
|
+
dateOffset <= NEXT_EVENTS_DAY_RADIUS &&
|
|
144
|
+
appData.nextEventsByOffset[dateOffset]
|
|
145
|
+
) {
|
|
146
|
+
return appData.nextEventsByOffset[dateOffset];
|
|
147
|
+
}
|
|
148
|
+
return getCollectionsForDay(sharedWasteTypes, sharedPlaces, dateOffset, selectedPlaceId);
|
|
149
|
+
}, [appData, dateOffset, selectedPlaceId, sharedPlaces, sharedWasteTypes, useShared]);
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
cityId: resolvedCityId,
|
|
153
|
+
city: sharedCity,
|
|
154
|
+
wasteTypes: sharedWasteTypes,
|
|
155
|
+
wasteCollectionPlaces: sharedPlaces,
|
|
156
|
+
dateOffset,
|
|
157
|
+
dateLabel: getDateLabel(dateOffset),
|
|
158
|
+
collectionsForDay,
|
|
159
|
+
isLoading: useShared && appData ? appData.isBootstrapping : isLoading,
|
|
160
|
+
error: useShared && appData ? appData.scheduleError : error,
|
|
161
|
+
refetch: useShared && appData ? appData.refreshSchedule : fetchData,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
|
-
import { useLocal } from "../contexts/LocalContext";
|
|
3
|
-
import {
|
|
4
|
-
normalizeWasteTypeToRecyclingInfo,
|
|
5
|
-
type RecyclingInfoItem,
|
|
6
|
-
} from "../helpers/normalizeWasteTypeRecyclingInfo";
|
|
7
|
-
import { useNextCollection, type City, type WasteType } from "./useNextCollection";
|
|
8
|
-
|
|
9
|
-
export type { RecyclingInfoItem };
|
|
10
|
-
|
|
11
|
-
export type UseRecyclingInfoOptions = {
|
|
12
|
-
/** Si omis, utilise `LocalContext.selectedCityId`. */
|
|
13
|
-
cityId?: string | null;
|
|
14
|
-
/**
|
|
15
|
-
* Si renseigné : ne renvoie que ce type de déchet.
|
|
16
|
-
* Sinon : renvoie la liste complète des types pour la ville.
|
|
17
|
-
*/
|
|
18
|
-
wasteTypeId?: string | null;
|
|
19
|
-
pageSize?: number;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export type UseRecyclingInfoResult = {
|
|
23
|
-
cityId: string | null;
|
|
24
|
-
city: City | null;
|
|
25
|
-
wasteTypes: WasteType[];
|
|
26
|
-
/** Liste filtrée selon `wasteTypeId` (ou `wasteTypes` si pas de filtre). */
|
|
27
|
-
displayWasteTypes: WasteType[];
|
|
28
|
-
/**
|
|
29
|
-
* Infos de tri / recyclage normalisées (règles, détails, etc.) pour chaque type affiché.
|
|
30
|
-
* Préférer ce tableau pour l’UI plutôt que `displayWasteTypes` seul.
|
|
31
|
-
*/
|
|
32
|
-
recyclingInfos: RecyclingInfoItem[];
|
|
33
|
-
isLoading: boolean;
|
|
34
|
-
error: string | null;
|
|
35
|
-
refetch: () => Promise<void>;
|
|
36
|
-
getWasteTypeById: (id: string) => WasteType | undefined;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Hook utilitaire pour le composant `RecyclingInfo`.
|
|
41
|
-
* Il s’appuie sur `useNextCollection` pour récupérer la ville et les wasteTypes,
|
|
42
|
-
* puis applique un filtre optionnel par `wasteTypeId`.
|
|
43
|
-
*/
|
|
44
|
-
export function useRecyclingInfo(options: UseRecyclingInfoOptions = {}): UseRecyclingInfoResult {
|
|
45
|
-
const { selectedCityId } = useLocal();
|
|
46
|
-
const resolvedCityId = options.cityId ?? selectedCityId ?? null;
|
|
47
|
-
|
|
48
|
-
const {
|
|
49
|
-
city,
|
|
50
|
-
wasteTypes,
|
|
51
|
-
isLoading,
|
|
52
|
-
error,
|
|
53
|
-
refetch,
|
|
54
|
-
cityId,
|
|
55
|
-
} = useNextCollection({
|
|
56
|
-
cityId: resolvedCityId,
|
|
57
|
-
pageSize: options.pageSize,
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const wasteTypesList = useMemo(() => (wasteTypes ?? []) as WasteType[], [wasteTypes]);
|
|
61
|
-
const filterId = (options.wasteTypeId ?? "").trim();
|
|
62
|
-
|
|
63
|
-
const displayWasteTypes = useMemo(() => {
|
|
64
|
-
if (!filterId) return wasteTypesList;
|
|
65
|
-
return wasteTypesList.filter((w) => w.id === filterId);
|
|
66
|
-
}, [wasteTypesList, filterId]);
|
|
67
|
-
|
|
68
|
-
const recyclingInfos = useMemo(() => {
|
|
69
|
-
const list: RecyclingInfoItem[] = [];
|
|
70
|
-
for (const w of displayWasteTypes) {
|
|
71
|
-
const item = normalizeWasteTypeToRecyclingInfo(w);
|
|
72
|
-
if (item) list.push(item);
|
|
73
|
-
}
|
|
74
|
-
return list;
|
|
75
|
-
}, [displayWasteTypes]);
|
|
76
|
-
|
|
77
|
-
const getWasteTypeById = (id: string) => wasteTypesList.find((w) => w.id === id);
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
cityId,
|
|
81
|
-
city,
|
|
82
|
-
wasteTypes: wasteTypesList,
|
|
83
|
-
displayWasteTypes,
|
|
84
|
-
recyclingInfos,
|
|
85
|
-
isLoading,
|
|
86
|
-
error,
|
|
87
|
-
refetch,
|
|
88
|
-
getWasteTypeById,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { useLocal } from "../contexts/LocalContext";
|
|
3
|
+
import {
|
|
4
|
+
normalizeWasteTypeToRecyclingInfo,
|
|
5
|
+
type RecyclingInfoItem,
|
|
6
|
+
} from "../helpers/normalizeWasteTypeRecyclingInfo";
|
|
7
|
+
import { useNextCollection, type City, type WasteType } from "./useNextCollection";
|
|
8
|
+
|
|
9
|
+
export type { RecyclingInfoItem };
|
|
10
|
+
|
|
11
|
+
export type UseRecyclingInfoOptions = {
|
|
12
|
+
/** Si omis, utilise `LocalContext.selectedCityId`. */
|
|
13
|
+
cityId?: string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Si renseigné : ne renvoie que ce type de déchet.
|
|
16
|
+
* Sinon : renvoie la liste complète des types pour la ville.
|
|
17
|
+
*/
|
|
18
|
+
wasteTypeId?: string | null;
|
|
19
|
+
pageSize?: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type UseRecyclingInfoResult = {
|
|
23
|
+
cityId: string | null;
|
|
24
|
+
city: City | null;
|
|
25
|
+
wasteTypes: WasteType[];
|
|
26
|
+
/** Liste filtrée selon `wasteTypeId` (ou `wasteTypes` si pas de filtre). */
|
|
27
|
+
displayWasteTypes: WasteType[];
|
|
28
|
+
/**
|
|
29
|
+
* Infos de tri / recyclage normalisées (règles, détails, etc.) pour chaque type affiché.
|
|
30
|
+
* Préférer ce tableau pour l’UI plutôt que `displayWasteTypes` seul.
|
|
31
|
+
*/
|
|
32
|
+
recyclingInfos: RecyclingInfoItem[];
|
|
33
|
+
isLoading: boolean;
|
|
34
|
+
error: string | null;
|
|
35
|
+
refetch: () => Promise<void>;
|
|
36
|
+
getWasteTypeById: (id: string) => WasteType | undefined;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Hook utilitaire pour le composant `RecyclingInfo`.
|
|
41
|
+
* Il s’appuie sur `useNextCollection` pour récupérer la ville et les wasteTypes,
|
|
42
|
+
* puis applique un filtre optionnel par `wasteTypeId`.
|
|
43
|
+
*/
|
|
44
|
+
export function useRecyclingInfo(options: UseRecyclingInfoOptions = {}): UseRecyclingInfoResult {
|
|
45
|
+
const { selectedCityId } = useLocal();
|
|
46
|
+
const resolvedCityId = options.cityId ?? selectedCityId ?? null;
|
|
47
|
+
|
|
48
|
+
const {
|
|
49
|
+
city,
|
|
50
|
+
wasteTypes,
|
|
51
|
+
isLoading,
|
|
52
|
+
error,
|
|
53
|
+
refetch,
|
|
54
|
+
cityId,
|
|
55
|
+
} = useNextCollection({
|
|
56
|
+
cityId: resolvedCityId,
|
|
57
|
+
pageSize: options.pageSize,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const wasteTypesList = useMemo(() => (wasteTypes ?? []) as WasteType[], [wasteTypes]);
|
|
61
|
+
const filterId = (options.wasteTypeId ?? "").trim();
|
|
62
|
+
|
|
63
|
+
const displayWasteTypes = useMemo(() => {
|
|
64
|
+
if (!filterId) return wasteTypesList;
|
|
65
|
+
return wasteTypesList.filter((w) => w.id === filterId);
|
|
66
|
+
}, [wasteTypesList, filterId]);
|
|
67
|
+
|
|
68
|
+
const recyclingInfos = useMemo(() => {
|
|
69
|
+
const list: RecyclingInfoItem[] = [];
|
|
70
|
+
for (const w of displayWasteTypes) {
|
|
71
|
+
const item = normalizeWasteTypeToRecyclingInfo(w);
|
|
72
|
+
if (item) list.push(item);
|
|
73
|
+
}
|
|
74
|
+
return list;
|
|
75
|
+
}, [displayWasteTypes]);
|
|
76
|
+
|
|
77
|
+
const getWasteTypeById = (id: string) => wasteTypesList.find((w) => w.id === id);
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
cityId,
|
|
81
|
+
city,
|
|
82
|
+
wasteTypes: wasteTypesList,
|
|
83
|
+
displayWasteTypes,
|
|
84
|
+
recyclingInfos,
|
|
85
|
+
isLoading,
|
|
86
|
+
error,
|
|
87
|
+
refetch,
|
|
88
|
+
getWasteTypeById,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
|
-
import { createWastoClient, type WastoClient } from "@wasto/sdk";
|
|
3
|
-
import { useWasto } from "../contexts/WastoContext";
|
|
4
|
-
|
|
5
|
-
/** Shared Wasto HTTP client wired with optional API error reporting. */
|
|
6
|
-
export function useWastoClient(): WastoClient {
|
|
7
|
-
const { apiUrl, onRequestError } = useWasto();
|
|
8
|
-
return useMemo(
|
|
9
|
-
() =>
|
|
10
|
-
createWastoClient({
|
|
11
|
-
baseUrl: apiUrl,
|
|
12
|
-
onRequestError,
|
|
13
|
-
}),
|
|
14
|
-
[apiUrl, onRequestError],
|
|
15
|
-
);
|
|
16
|
-
}
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { createWastoClient, type WastoClient } from "@wasto/sdk";
|
|
3
|
+
import { useWasto } from "../contexts/WastoContext";
|
|
4
|
+
|
|
5
|
+
/** Shared Wasto HTTP client wired with optional API error reporting. */
|
|
6
|
+
export function useWastoClient(): WastoClient {
|
|
7
|
+
const { apiUrl, onRequestError } = useWasto();
|
|
8
|
+
return useMemo(
|
|
9
|
+
() =>
|
|
10
|
+
createWastoClient({
|
|
11
|
+
baseUrl: apiUrl,
|
|
12
|
+
onRequestError,
|
|
13
|
+
}),
|
|
14
|
+
[apiUrl, onRequestError],
|
|
15
|
+
);
|
|
16
|
+
}
|