@addev-be/ui 0.11.7 → 0.12.0

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.
Files changed (186) hide show
  1. package/assets/icons/arrow-down-1-9.svg +1 -1
  2. package/assets/icons/arrow-down-a-z.svg +1 -1
  3. package/assets/icons/arrow-up-z-a.svg +1 -1
  4. package/assets/icons/check.svg +1 -1
  5. package/assets/icons/circle-check.svg +1 -1
  6. package/assets/icons/down.svg +1 -1
  7. package/assets/icons/filter-full.svg +1 -1
  8. package/assets/icons/filter.svg +1 -1
  9. package/assets/icons/hashtag.svg +1 -1
  10. package/assets/icons/image-slash.svg +1 -1
  11. package/assets/icons/left.svg +1 -1
  12. package/assets/icons/magnifier.svg +1 -1
  13. package/assets/icons/phone.svg +1 -1
  14. package/assets/icons/right.svg +1 -1
  15. package/assets/icons/sort-calendar-ascending.svg +5 -5
  16. package/assets/icons/spinner-third.svg +1 -1
  17. package/assets/icons/table-columns.svg +1 -1
  18. package/assets/icons/table-footer-slash.svg +4 -4
  19. package/assets/icons/table-footer.svg +3 -3
  20. package/assets/icons/up.svg +1 -1
  21. package/assets/icons/user-tie.svg +1 -1
  22. package/assets/icons/x-bar.svg +3 -3
  23. package/dist/components/auth/LoginForm.js +0 -1
  24. package/dist/components/data/DataGrid/AdvancedRequestDataGrid.d.ts +10 -0
  25. package/dist/components/data/DataGrid/AdvancedRequestDataGrid.js +173 -0
  26. package/dist/components/data/DataGrid/FilterValuesScroller.d.ts +13 -0
  27. package/dist/components/data/DataGrid/FilterValuesScroller.js +73 -0
  28. package/dist/components/data/DataGrid/VirtualScroller.d.ts +11 -0
  29. package/dist/components/data/DataGrid/VirtualScroller.js +41 -0
  30. package/dist/components/data/DataGrid/helpers/advancedRequests.d.ts +12 -0
  31. package/dist/components/data/DataGrid/helpers/advancedRequests.js +53 -0
  32. package/dist/components/data/DataGrid/helpers.d.ts +28 -0
  33. package/dist/components/data/DataGrid/helpers.js +436 -0
  34. package/dist/config/types.d.ts +11 -0
  35. package/dist/config/types.js +2 -0
  36. package/dist/providers/AuthenticationProvider/index.d.ts +0 -1
  37. package/dist/providers/AuthenticationProvider/index.js +14 -28
  38. package/dist/services/advancedRequests.d.ts +1 -1
  39. package/dist/services/requests/userPermissions.d.ts +4 -0
  40. package/dist/services/requests/userPermissions.js +20 -0
  41. package/dist/services/sqlRequests.d.ts +1 -1
  42. package/package.json +1 -1
  43. package/src/Icons.tsx +128 -128
  44. package/src/components/auth/LoginForm.tsx +84 -84
  45. package/src/components/auth/LoginPage.tsx +32 -32
  46. package/src/components/auth/PasswordRecoveryForm.tsx +52 -52
  47. package/src/components/auth/PasswordResetForm.tsx +112 -112
  48. package/src/components/auth/index.ts +4 -4
  49. package/src/components/auth/styles.ts +14 -14
  50. package/src/components/data/AdvancedRequestDataGrid/helpers/advancedRequests.ts +93 -93
  51. package/src/components/data/AdvancedRequestDataGrid/helpers/columns.tsx +262 -262
  52. package/src/components/data/AdvancedRequestDataGrid/helpers/index.ts +2 -2
  53. package/src/components/data/AdvancedRequestDataGrid/index.tsx +267 -267
  54. package/src/components/data/AdvancedRequestDataGrid/types.ts +47 -47
  55. package/src/components/data/DataGrid/DataGridCell.tsx +76 -76
  56. package/src/components/data/DataGrid/DataGridColumnsModal/helpers.ts +14 -14
  57. package/src/components/data/DataGrid/DataGridColumnsModal/hooks.tsx +59 -59
  58. package/src/components/data/DataGrid/DataGridColumnsModal/index.tsx +181 -181
  59. package/src/components/data/DataGrid/DataGridColumnsModal/styles.ts +104 -104
  60. package/src/components/data/DataGrid/DataGridEditableCell.tsx +43 -43
  61. package/src/components/data/DataGrid/DataGridFilterMenu/FilterValuesScroller.tsx +131 -131
  62. package/src/components/data/DataGrid/DataGridFilterMenu/hooks.tsx +78 -78
  63. package/src/components/data/DataGrid/DataGridFilterMenu/index.tsx +378 -378
  64. package/src/components/data/DataGrid/DataGridFilterMenu/styles.ts +97 -97
  65. package/src/components/data/DataGrid/DataGridFooter.tsx +43 -43
  66. package/src/components/data/DataGrid/DataGridHeader.tsx +128 -128
  67. package/src/components/data/DataGrid/DataGridHeaderCell.tsx +101 -101
  68. package/src/components/data/DataGrid/DataGridRowTemplate.tsx +70 -70
  69. package/src/components/data/DataGrid/FilterModalContent/index.tsx +136 -136
  70. package/src/components/data/DataGrid/FilterModalContent/styles.ts +22 -22
  71. package/src/components/data/DataGrid/constants.ts +6 -6
  72. package/src/components/data/DataGrid/helpers/columns.tsx +319 -319
  73. package/src/components/data/DataGrid/helpers/filters.ts +287 -287
  74. package/src/components/data/DataGrid/helpers/index.ts +2 -2
  75. package/src/components/data/DataGrid/hooks/index.ts +30 -30
  76. package/src/components/data/DataGrid/hooks/useDataGrid.tsx +338 -338
  77. package/src/components/data/DataGrid/hooks/useDataGridCopy.ts +175 -175
  78. package/src/components/data/DataGrid/hooks/useDataGridSettings.ts +48 -48
  79. package/src/components/data/DataGrid/index.tsx +110 -110
  80. package/src/components/data/DataGrid/styles.ts +336 -336
  81. package/src/components/data/DataGrid/types.ts +283 -283
  82. package/src/components/data/SmartQueryDataGrid/helpers/columns.tsx +333 -0
  83. package/src/components/data/SmartQueryDataGrid/helpers/hooks.ts +41 -0
  84. package/src/components/data/SmartQueryDataGrid/helpers/index.ts +2 -0
  85. package/src/components/data/SmartQueryDataGrid/helpers/smartQueries.ts +17 -0
  86. package/src/components/data/SmartQueryDataGrid/hooks.ts +75 -0
  87. package/src/components/data/SmartQueryDataGrid/index.tsx +338 -0
  88. package/src/components/data/SmartQueryDataGrid/types.ts +45 -0
  89. package/src/components/data/SqlRequestDataGrid/helpers/columns.tsx +306 -306
  90. package/src/components/data/SqlRequestDataGrid/helpers/index.ts +2 -2
  91. package/src/components/data/SqlRequestDataGrid/helpers/sqlRequests.ts +16 -16
  92. package/src/components/data/SqlRequestDataGrid/index.tsx +387 -387
  93. package/src/components/data/SqlRequestDataGrid/types.ts +48 -48
  94. package/src/components/data/SqlRequestGrid/filters/FiltersSidebar.tsx +106 -106
  95. package/src/components/data/SqlRequestGrid/filters/styles.ts +88 -88
  96. package/src/components/data/SqlRequestGrid/helpers/index.ts +1 -1
  97. package/src/components/data/SqlRequestGrid/helpers/sqlRequests.ts +16 -16
  98. package/src/components/data/SqlRequestGrid/index.tsx +310 -310
  99. package/src/components/data/SqlRequestGrid/styles.ts +20 -20
  100. package/src/components/data/SqlRequestGrid/types.ts +53 -53
  101. package/src/components/data/VirtualScroller/hooks.ts +71 -71
  102. package/src/components/data/VirtualScroller/index.tsx +89 -89
  103. package/src/components/data/VirtualScroller/styles.ts +58 -58
  104. package/src/components/data/VirtualScroller/types.ts +12 -12
  105. package/src/components/data/index.ts +21 -17
  106. package/src/components/forms/AutoTextArea.tsx +41 -41
  107. package/src/components/forms/Button.tsx +133 -133
  108. package/src/components/forms/IconButton.tsx +57 -57
  109. package/src/components/forms/IndeterminateCheckbox.tsx +46 -46
  110. package/src/components/forms/Select.tsx +40 -40
  111. package/src/components/forms/VerticalLabel.tsx +20 -20
  112. package/src/components/forms/index.ts +6 -6
  113. package/src/components/forms/styles.ts +31 -31
  114. package/src/components/index.ts +6 -6
  115. package/src/components/layout/Dropdown/index.tsx +112 -112
  116. package/src/components/layout/Dropdown/styles.ts +45 -45
  117. package/src/components/layout/Loading/index.tsx +29 -29
  118. package/src/components/layout/Loading/styles.ts +29 -29
  119. package/src/components/layout/Modal/index.tsx +51 -51
  120. package/src/components/layout/Modal/styles.ts +121 -121
  121. package/src/components/layout/index.ts +3 -3
  122. package/src/components/search/HighlightedText.tsx +37 -37
  123. package/src/components/search/QuickSearchBar.tsx +86 -86
  124. package/src/components/search/QuickSearchResults.tsx +86 -86
  125. package/src/components/search/index.ts +9 -9
  126. package/src/components/search/styles.ts +96 -96
  127. package/src/components/search/types.ts +26 -26
  128. package/src/components/ui/Avatar/index.tsx +54 -54
  129. package/src/components/ui/Card/index.tsx +14 -14
  130. package/src/components/ui/Card/styles.ts +37 -37
  131. package/src/components/ui/ContextMenu/index.tsx +79 -79
  132. package/src/components/ui/ContextMenu/styles.ts +119 -119
  133. package/src/components/ui/Label.tsx +90 -90
  134. package/src/components/ui/Message/index.tsx +57 -57
  135. package/src/components/ui/Message/styles.ts +40 -40
  136. package/src/components/ui/index.ts +5 -5
  137. package/src/config/index.ts +14 -14
  138. package/src/helpers/dates.ts +17 -17
  139. package/src/helpers/getScrollbarSize.ts +14 -14
  140. package/src/helpers/index.ts +3 -3
  141. package/src/helpers/numbers.ts +26 -26
  142. package/src/helpers/text.ts +13 -13
  143. package/src/hooks/index.ts +3 -3
  144. package/src/hooks/useElementSize.ts +24 -24
  145. package/src/hooks/useShowArchived.ts +28 -28
  146. package/src/hooks/useWindowSize.ts +20 -20
  147. package/src/index.ts +9 -9
  148. package/src/providers/AuthenticationProvider/helpers.ts +3 -3
  149. package/src/providers/AuthenticationProvider/index.tsx +243 -243
  150. package/src/providers/LoadingProvider/index.tsx +47 -47
  151. package/src/providers/PortalsProvider/index.tsx +54 -54
  152. package/src/providers/PortalsProvider/styles.ts +31 -31
  153. package/src/providers/SettingsProvider/index.tsx +70 -70
  154. package/src/providers/ThemeProvider/ThemeProvider.ts +63 -63
  155. package/src/providers/ThemeProvider/defaultTheme.ts +457 -457
  156. package/src/providers/ThemeProvider/index.ts +4 -4
  157. package/src/providers/ThemeProvider/types.ts +131 -131
  158. package/src/providers/TrackingProvider/index.tsx +71 -71
  159. package/src/providers/UiProviders/index.tsx +68 -68
  160. package/src/providers/UiProviders/styles.ts +10 -10
  161. package/src/providers/hooks.ts +12 -12
  162. package/src/providers/index.ts +8 -8
  163. package/src/services/HttpService.ts +92 -92
  164. package/src/services/WebSocketService.ts +137 -137
  165. package/src/services/advancedRequests.ts +102 -102
  166. package/src/services/base.ts +23 -23
  167. package/src/services/globalSearch.ts +29 -29
  168. package/src/services/hooks.ts +44 -44
  169. package/src/services/index.ts +20 -19
  170. package/src/services/requests/auth.ts +44 -44
  171. package/src/services/requests/generic.ts +62 -62
  172. package/src/services/requests/tracking.ts +12 -12
  173. package/src/services/requests/userProfiles.ts +35 -35
  174. package/src/services/requests/users.ts +28 -28
  175. package/src/services/smartQueries.ts +122 -0
  176. package/src/services/sqlRequests.ts +111 -111
  177. package/src/services/types/auth.ts +131 -131
  178. package/src/services/types/base.ts +10 -10
  179. package/src/services/types/generic.ts +92 -92
  180. package/src/services/types/tracking.ts +39 -39
  181. package/src/services/types/userProfiles.ts +107 -107
  182. package/src/services/types/users.ts +106 -106
  183. package/src/styles/animations.scss +30 -30
  184. package/src/styles/index.scss +42 -42
  185. package/src/typings.d.ts +13 -13
  186. package/tsconfig.json +18 -18
@@ -1,378 +1,378 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- /* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
3
-
4
- import * as styles from './styles';
5
-
6
- import {
7
- ArrowDown19Icon,
8
- ArrowDownAZIcon,
9
- ArrowDownBigSmallIcon,
10
- ArrowUp91Icon,
11
- ArrowUpBigSmallIcon,
12
- ArrowUpZAIcon,
13
- FilterIcon,
14
- FilterSlashIcon,
15
- IconFC,
16
- MagnifierIcon,
17
- SigmaIcon,
18
- SortCalendarAscendingIcon,
19
- SortCalendarDescendingIcon,
20
- TableFooterIcon,
21
- TableFooterSlashIcon,
22
- TallyIcon,
23
- XBarIcon,
24
- } from '../../../../Icons';
25
- import {
26
- DataGridContext,
27
- DataGridFilterType,
28
- DataGridFilterValue,
29
- DataGridFooterPredefinedFunction,
30
- } from '../types';
31
- import {
32
- applyFilters,
33
- defaultRendererAndFormatter,
34
- getDateGroups,
35
- } from '../helpers';
36
- import { intersection, uniq, without } from 'lodash';
37
- import {
38
- useCallback,
39
- useContext,
40
- useEffect,
41
- useMemo,
42
- useRef,
43
- useState,
44
- } from 'react';
45
-
46
- import { ContextMenu } from '../../../ui/ContextMenu';
47
- import { FilterValuesScroller } from './FilterValuesScroller';
48
- import { Input } from '../../../forms';
49
- import { MenuContainer } from '../../../ui/ContextMenu/styles';
50
- import { useFilterModal } from './hooks';
51
-
52
- type FilterValuesProps<R> = {
53
- columnKey: string;
54
- columnIndex: number;
55
- context: DataGridContext<R>;
56
- onClose?: () => void;
57
- contextMenu?: boolean;
58
- showTotalButton?: boolean;
59
- };
60
-
61
- const sortAsc: Record<DataGridFilterType, [string, IconFC]> = {
62
- number: ['Trier du plus petit au plus grand', ArrowDown19Icon],
63
- text: ['Trier de A à Z', ArrowDownAZIcon],
64
- date: ['Trier du plus ancien au plus récent', SortCalendarAscendingIcon],
65
- };
66
- const sortDesc: Record<DataGridFilterType, [string, IconFC]> = {
67
- number: ['Trier du plus grand au plus petit', ArrowUp91Icon],
68
- text: ['Trier de Z à A', ArrowUpZAIcon],
69
- date: ['Trier du plus récent au plus ancien', SortCalendarDescendingIcon],
70
- };
71
-
72
- const footerFunctionsTexts: Record<DataGridFooterPredefinedFunction, string> = {
73
- average: 'Moyenne',
74
- avg: 'Moyenne',
75
- count: 'Nombre',
76
- max: 'Maximum',
77
- min: 'Minimum',
78
- sum: 'Somme',
79
- };
80
- const footerFunctionsIcons: Record<DataGridFooterPredefinedFunction, IconFC> = {
81
- average: XBarIcon,
82
- avg: XBarIcon,
83
- count: TallyIcon,
84
- max: ArrowUpBigSmallIcon,
85
- min: ArrowDownBigSmallIcon,
86
- sum: SigmaIcon,
87
- };
88
-
89
- export const DataGridFilterMenu = <R,>({
90
- columnKey,
91
- context,
92
- onClose,
93
- contextMenu = true,
94
- showTotalButton = true,
95
- }: FilterValuesProps<R>) => {
96
- const { openModal, modal } = useFilterModal({ columnKey, context, onClose });
97
- const {
98
- filters = {},
99
- footers = {},
100
- rows,
101
- columns,
102
- setFilters,
103
- filterValuesLoader,
104
- setSorts,
105
- setFooters,
106
- } = useContext(context);
107
- const column = columns[columnKey] ?? {};
108
- const textFilterInputRef = useRef<HTMLInputElement>(null);
109
- const [textFilter, setTextFilter] = useState('');
110
-
111
- const [availableValues, setAvailableValues] = useState<DataGridFilterValue[]>(
112
- []
113
- );
114
-
115
- useEffect(() => {
116
- if (filterValuesLoader) {
117
- filterValuesLoader(columnKey).then((values) => {
118
- setAvailableValues(() => values);
119
- });
120
- } else {
121
- const otherFilters = Object.entries(filters)
122
- .filter(([key]) => key !== columnKey)
123
- .map(([, filter]) => filter);
124
- const availableRows = applyFilters(rows, otherFilters);
125
- const values = availableRows.map((row) => column.filter!.getter(row));
126
- setAvailableValues(() =>
127
- uniq(values).sort(
128
- column.filter?.type === 'number'
129
- ? (a, b) => (a as number) - (b as number)
130
- : (a, b) => (a as string).localeCompare(b as string)
131
- )
132
- );
133
- }
134
- }, [column.filter, columnKey, filterValuesLoader, filters, rows]);
135
-
136
- const selectedValues = useMemo(
137
- () => filters?.[columnKey]?.values ?? [],
138
- [columnKey, filters]
139
- );
140
-
141
- const clearFilter = useCallback(() => {
142
- const newFilters = { ...filters };
143
- delete newFilters[columnKey];
144
- setFilters(newFilters);
145
- onClose?.();
146
- }, [filters, columnKey, setFilters, onClose]);
147
-
148
- const setValuesChecked = useCallback(
149
- (values: any[], checked?: boolean) => {
150
- setFilters((prevFilters) => {
151
- const newValues = checked
152
- ? [...(prevFilters[columnKey]?.values ?? []), ...values]
153
- : without(prevFilters[columnKey]?.values ?? [], ...values);
154
- const newFilters = {
155
- ...prevFilters,
156
- };
157
- if (newValues.length === 0) {
158
- delete newFilters[columnKey];
159
- } else {
160
- newFilters[columnKey] = {
161
- ...(prevFilters[columnKey] ?? column.filter),
162
- operator: 'inArray',
163
- values: newValues,
164
- };
165
- }
166
- return newFilters;
167
- });
168
- },
169
- [setFilters, columnKey, column.filter]
170
- );
171
-
172
- const toggleValues = useCallback(
173
- (values: DataGridFilterValue[]) => {
174
- const checked =
175
- intersection(selectedValues, values).length === values.length;
176
- setValuesChecked(values, !checked);
177
- },
178
- [setValuesChecked, selectedValues]
179
- );
180
-
181
- const formatter = useMemo(
182
- () => column.filter?.formatter ?? defaultRendererAndFormatter,
183
- [column.filter?.formatter]
184
- );
185
- const renderer = useMemo(
186
- () => column.filter?.renderer ?? defaultRendererAndFormatter,
187
- [column.filter?.renderer]
188
- );
189
-
190
- const filteredAvailableValues = useMemo(
191
- () =>
192
- !textFilter
193
- ? availableValues
194
- : availableValues.filter((value) =>
195
- formatter(value)?.toLowerCase().includes(textFilter.toLowerCase())
196
- ),
197
- [availableValues, formatter, textFilter]
198
- );
199
-
200
- useEffect(() => {
201
- if (textFilterInputRef.current) {
202
- textFilterInputRef.current.focus();
203
- }
204
- }, []);
205
-
206
- const checkboxesComponent = useMemo(() => {
207
- const groups =
208
- column.type === 'date'
209
- ? getDateGroups(filteredAvailableValues)
210
- : undefined;
211
- return (
212
- <FilterValuesScroller
213
- values={filteredAvailableValues}
214
- selectedValues={selectedValues}
215
- onToggle={toggleValues}
216
- formatter={formatter}
217
- renderer={renderer}
218
- groups={groups}
219
- />
220
- );
221
- }, [
222
- column.type,
223
- filteredAvailableValues,
224
- formatter,
225
- renderer,
226
- selectedValues,
227
- toggleValues,
228
- ]);
229
-
230
- const onSortAscClicked = useCallback(() => {
231
- setSorts({ [columnKey]: 'asc' });
232
- onClose?.();
233
- }, [columnKey, onClose, setSorts]);
234
- const onSortDescClicked = useCallback(() => {
235
- setSorts({ [columnKey]: 'desc' });
236
- onClose?.();
237
- }, [columnKey, onClose, setSorts]);
238
-
239
- const hasFilters = filters[columnKey]?.values.length > 0;
240
- const [[sortAscText, SortAscIcon], [sortDescText, SortDescIcon]] = [
241
- sortAsc[column.filter?.type ?? 'text'],
242
- sortDesc[column.filter?.type ?? 'text'],
243
- ];
244
-
245
- const isFooterVisible =
246
- columnKey in footers && footers[columnKey] !== undefined;
247
- const showFooter = useCallback(
248
- (key: string) => {
249
- setFooters((prevFooters) => ({
250
- ...prevFooters,
251
- [columnKey]: key,
252
- }));
253
- onClose?.();
254
- },
255
- [columnKey, onClose, setFooters]
256
- );
257
- const hideFooter = useCallback(() => {
258
- setFooters((prevFooters) => {
259
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
260
- const { [columnKey]: _, ...newFooters } = prevFooters;
261
- return newFooters;
262
- });
263
- onClose?.();
264
- }, [columnKey, onClose, setFooters]);
265
-
266
- const content = (
267
- <>
268
- {modal}
269
- {column.sortGetter && (
270
- <>
271
- <ContextMenu.Item onClick={onSortAscClicked}>
272
- <SortAscIcon />
273
- {sortAscText}
274
- </ContextMenu.Item>
275
- <ContextMenu.Item onClick={onSortDescClicked}>
276
- <SortDescIcon />
277
- {sortDescText}
278
- </ContextMenu.Item>
279
- <ContextMenu.Divider />
280
- </>
281
- )}
282
- {isFooterVisible && typeof column.footer === 'function' && (
283
- <>
284
- <ContextMenu.Item onClick={hideFooter}>
285
- <TableFooterSlashIcon />
286
- Masquer le total
287
- </ContextMenu.Item>
288
- <ContextMenu.Divider />
289
- </>
290
- )}
291
- {showTotalButton && (
292
- <>
293
- {!isFooterVisible && typeof column.footer === 'function' && (
294
- <>
295
- <ContextMenu.Item onClick={() => showFooter('count')}>
296
- <TableFooterIcon />
297
- Afficher le total
298
- </ContextMenu.Item>
299
- <ContextMenu.Divider />
300
- </>
301
- )}
302
- {showTotalButton && typeof column.footer === 'object' && (
303
- <>
304
- <ContextMenu.ParentItem>
305
- <TableFooterIcon />
306
- Afficher le total
307
- <ContextMenu.SubMenu>
308
- {Object.keys(column.footer).map((key) => {
309
- const TotalIcon =
310
- footerFunctionsIcons[
311
- key as DataGridFooterPredefinedFunction
312
- ] ?? TableFooterIcon;
313
- return (
314
- <ContextMenu.Item
315
- key={key}
316
- onClick={() => showFooter(key)}
317
- >
318
- <TotalIcon />
319
- {key in footerFunctionsTexts
320
- ? footerFunctionsTexts[
321
- key as DataGridFooterPredefinedFunction
322
- ]
323
- : key}
324
- </ContextMenu.Item>
325
- );
326
- })}
327
- <ContextMenu.Divider />
328
- <ContextMenu.Item
329
- onClick={hideFooter}
330
- disabled={!isFooterVisible}
331
- >
332
- <TableFooterSlashIcon />
333
- Masquer le total
334
- </ContextMenu.Item>
335
- </ContextMenu.SubMenu>
336
- </ContextMenu.ParentItem>
337
- <ContextMenu.Divider />
338
- </>
339
- )}
340
- </>
341
- )}
342
- <ContextMenu.Item onClick={openModal}>
343
- <FilterIcon />
344
- Filtrer ...
345
- </ContextMenu.Item>
346
- <ContextMenu.Item
347
- $color="danger"
348
- onClick={clearFilter}
349
- disabled={!hasFilters}
350
- >
351
- <FilterSlashIcon />
352
- Supprimer le filtre
353
- </ContextMenu.Item>
354
- <ContextMenu.Divider />
355
- <styles.InputContainer>
356
- <MagnifierIcon />
357
- <Input
358
- ref={textFilterInputRef}
359
- type="text"
360
- name="search"
361
- id="search"
362
- placeholder="Rechercher ..."
363
- value={textFilter}
364
- onChange={(e) => setTextFilter(e.target.value)}
365
- />
366
- </styles.InputContainer>
367
- <styles.CheckboxesContainer>
368
- {checkboxesComponent}
369
- </styles.CheckboxesContainer>
370
- </>
371
- );
372
-
373
- if (!contextMenu) {
374
- return <MenuContainer>{content}</MenuContainer>;
375
- }
376
-
377
- return <ContextMenu>{content}</ContextMenu>;
378
- };
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
3
+
4
+ import * as styles from './styles';
5
+
6
+ import {
7
+ ArrowDown19Icon,
8
+ ArrowDownAZIcon,
9
+ ArrowDownBigSmallIcon,
10
+ ArrowUp91Icon,
11
+ ArrowUpBigSmallIcon,
12
+ ArrowUpZAIcon,
13
+ FilterIcon,
14
+ FilterSlashIcon,
15
+ IconFC,
16
+ MagnifierIcon,
17
+ SigmaIcon,
18
+ SortCalendarAscendingIcon,
19
+ SortCalendarDescendingIcon,
20
+ TableFooterIcon,
21
+ TableFooterSlashIcon,
22
+ TallyIcon,
23
+ XBarIcon,
24
+ } from '../../../../Icons';
25
+ import {
26
+ DataGridContext,
27
+ DataGridFilterType,
28
+ DataGridFilterValue,
29
+ DataGridFooterPredefinedFunction,
30
+ } from '../types';
31
+ import {
32
+ applyFilters,
33
+ defaultRendererAndFormatter,
34
+ getDateGroups,
35
+ } from '../helpers';
36
+ import { intersection, uniq, without } from 'lodash';
37
+ import {
38
+ useCallback,
39
+ useContext,
40
+ useEffect,
41
+ useMemo,
42
+ useRef,
43
+ useState,
44
+ } from 'react';
45
+
46
+ import { ContextMenu } from '../../../ui/ContextMenu';
47
+ import { FilterValuesScroller } from './FilterValuesScroller';
48
+ import { Input } from '../../../forms';
49
+ import { MenuContainer } from '../../../ui/ContextMenu/styles';
50
+ import { useFilterModal } from './hooks';
51
+
52
+ type FilterValuesProps<R> = {
53
+ columnKey: string;
54
+ columnIndex: number;
55
+ context: DataGridContext<R>;
56
+ onClose?: () => void;
57
+ contextMenu?: boolean;
58
+ showTotalButton?: boolean;
59
+ };
60
+
61
+ const sortAsc: Record<DataGridFilterType, [string, IconFC]> = {
62
+ number: ['Trier du plus petit au plus grand', ArrowDown19Icon],
63
+ text: ['Trier de A à Z', ArrowDownAZIcon],
64
+ date: ['Trier du plus ancien au plus récent', SortCalendarAscendingIcon],
65
+ };
66
+ const sortDesc: Record<DataGridFilterType, [string, IconFC]> = {
67
+ number: ['Trier du plus grand au plus petit', ArrowUp91Icon],
68
+ text: ['Trier de Z à A', ArrowUpZAIcon],
69
+ date: ['Trier du plus récent au plus ancien', SortCalendarDescendingIcon],
70
+ };
71
+
72
+ const footerFunctionsTexts: Record<DataGridFooterPredefinedFunction, string> = {
73
+ average: 'Moyenne',
74
+ avg: 'Moyenne',
75
+ count: 'Nombre',
76
+ max: 'Maximum',
77
+ min: 'Minimum',
78
+ sum: 'Somme',
79
+ };
80
+ const footerFunctionsIcons: Record<DataGridFooterPredefinedFunction, IconFC> = {
81
+ average: XBarIcon,
82
+ avg: XBarIcon,
83
+ count: TallyIcon,
84
+ max: ArrowUpBigSmallIcon,
85
+ min: ArrowDownBigSmallIcon,
86
+ sum: SigmaIcon,
87
+ };
88
+
89
+ export const DataGridFilterMenu = <R,>({
90
+ columnKey,
91
+ context,
92
+ onClose,
93
+ contextMenu = true,
94
+ showTotalButton = true,
95
+ }: FilterValuesProps<R>) => {
96
+ const { openModal, modal } = useFilterModal({ columnKey, context, onClose });
97
+ const {
98
+ filters = {},
99
+ footers = {},
100
+ rows,
101
+ columns,
102
+ setFilters,
103
+ filterValuesLoader,
104
+ setSorts,
105
+ setFooters,
106
+ } = useContext(context);
107
+ const column = columns[columnKey] ?? {};
108
+ const textFilterInputRef = useRef<HTMLInputElement>(null);
109
+ const [textFilter, setTextFilter] = useState('');
110
+
111
+ const [availableValues, setAvailableValues] = useState<DataGridFilterValue[]>(
112
+ []
113
+ );
114
+
115
+ useEffect(() => {
116
+ if (filterValuesLoader) {
117
+ filterValuesLoader(columnKey).then((values) => {
118
+ setAvailableValues(() => values);
119
+ });
120
+ } else {
121
+ const otherFilters = Object.entries(filters)
122
+ .filter(([key]) => key !== columnKey)
123
+ .map(([, filter]) => filter);
124
+ const availableRows = applyFilters(rows, otherFilters);
125
+ const values = availableRows.map((row) => column.filter!.getter(row));
126
+ setAvailableValues(() =>
127
+ uniq(values).sort(
128
+ column.filter?.type === 'number'
129
+ ? (a, b) => (a as number) - (b as number)
130
+ : (a, b) => (a as string).localeCompare(b as string)
131
+ )
132
+ );
133
+ }
134
+ }, [column.filter, columnKey, filterValuesLoader, filters, rows]);
135
+
136
+ const selectedValues = useMemo(
137
+ () => filters?.[columnKey]?.values ?? [],
138
+ [columnKey, filters]
139
+ );
140
+
141
+ const clearFilter = useCallback(() => {
142
+ const newFilters = { ...filters };
143
+ delete newFilters[columnKey];
144
+ setFilters(newFilters);
145
+ onClose?.();
146
+ }, [filters, columnKey, setFilters, onClose]);
147
+
148
+ const setValuesChecked = useCallback(
149
+ (values: any[], checked?: boolean) => {
150
+ setFilters((prevFilters) => {
151
+ const newValues = checked
152
+ ? [...(prevFilters[columnKey]?.values ?? []), ...values]
153
+ : without(prevFilters[columnKey]?.values ?? [], ...values);
154
+ const newFilters = {
155
+ ...prevFilters,
156
+ };
157
+ if (newValues.length === 0) {
158
+ delete newFilters[columnKey];
159
+ } else {
160
+ newFilters[columnKey] = {
161
+ ...(prevFilters[columnKey] ?? column.filter),
162
+ operator: 'inArray',
163
+ values: newValues,
164
+ };
165
+ }
166
+ return newFilters;
167
+ });
168
+ },
169
+ [setFilters, columnKey, column.filter]
170
+ );
171
+
172
+ const toggleValues = useCallback(
173
+ (values: DataGridFilterValue[]) => {
174
+ const checked =
175
+ intersection(selectedValues, values).length === values.length;
176
+ setValuesChecked(values, !checked);
177
+ },
178
+ [setValuesChecked, selectedValues]
179
+ );
180
+
181
+ const formatter = useMemo(
182
+ () => column.filter?.formatter ?? defaultRendererAndFormatter,
183
+ [column.filter?.formatter]
184
+ );
185
+ const renderer = useMemo(
186
+ () => column.filter?.renderer ?? defaultRendererAndFormatter,
187
+ [column.filter?.renderer]
188
+ );
189
+
190
+ const filteredAvailableValues = useMemo(
191
+ () =>
192
+ !textFilter
193
+ ? availableValues
194
+ : availableValues.filter((value) =>
195
+ formatter(value)?.toLowerCase().includes(textFilter.toLowerCase())
196
+ ),
197
+ [availableValues, formatter, textFilter]
198
+ );
199
+
200
+ useEffect(() => {
201
+ if (textFilterInputRef.current) {
202
+ textFilterInputRef.current.focus();
203
+ }
204
+ }, []);
205
+
206
+ const checkboxesComponent = useMemo(() => {
207
+ const groups =
208
+ column.type === 'date'
209
+ ? getDateGroups(filteredAvailableValues)
210
+ : undefined;
211
+ return (
212
+ <FilterValuesScroller
213
+ values={filteredAvailableValues}
214
+ selectedValues={selectedValues}
215
+ onToggle={toggleValues}
216
+ formatter={formatter}
217
+ renderer={renderer}
218
+ groups={groups}
219
+ />
220
+ );
221
+ }, [
222
+ column.type,
223
+ filteredAvailableValues,
224
+ formatter,
225
+ renderer,
226
+ selectedValues,
227
+ toggleValues,
228
+ ]);
229
+
230
+ const onSortAscClicked = useCallback(() => {
231
+ setSorts({ [columnKey]: 'asc' });
232
+ onClose?.();
233
+ }, [columnKey, onClose, setSorts]);
234
+ const onSortDescClicked = useCallback(() => {
235
+ setSorts({ [columnKey]: 'desc' });
236
+ onClose?.();
237
+ }, [columnKey, onClose, setSorts]);
238
+
239
+ const hasFilters = filters[columnKey]?.values.length > 0;
240
+ const [[sortAscText, SortAscIcon], [sortDescText, SortDescIcon]] = [
241
+ sortAsc[column.filter?.type ?? 'text'],
242
+ sortDesc[column.filter?.type ?? 'text'],
243
+ ];
244
+
245
+ const isFooterVisible =
246
+ columnKey in footers && footers[columnKey] !== undefined;
247
+ const showFooter = useCallback(
248
+ (key: string) => {
249
+ setFooters((prevFooters) => ({
250
+ ...prevFooters,
251
+ [columnKey]: key,
252
+ }));
253
+ onClose?.();
254
+ },
255
+ [columnKey, onClose, setFooters]
256
+ );
257
+ const hideFooter = useCallback(() => {
258
+ setFooters((prevFooters) => {
259
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
260
+ const { [columnKey]: _, ...newFooters } = prevFooters;
261
+ return newFooters;
262
+ });
263
+ onClose?.();
264
+ }, [columnKey, onClose, setFooters]);
265
+
266
+ const content = (
267
+ <>
268
+ {modal}
269
+ {column.sortGetter && (
270
+ <>
271
+ <ContextMenu.Item onClick={onSortAscClicked}>
272
+ <SortAscIcon />
273
+ {sortAscText}
274
+ </ContextMenu.Item>
275
+ <ContextMenu.Item onClick={onSortDescClicked}>
276
+ <SortDescIcon />
277
+ {sortDescText}
278
+ </ContextMenu.Item>
279
+ <ContextMenu.Divider />
280
+ </>
281
+ )}
282
+ {isFooterVisible && typeof column.footer === 'function' && (
283
+ <>
284
+ <ContextMenu.Item onClick={hideFooter}>
285
+ <TableFooterSlashIcon />
286
+ Masquer le total
287
+ </ContextMenu.Item>
288
+ <ContextMenu.Divider />
289
+ </>
290
+ )}
291
+ {showTotalButton && (
292
+ <>
293
+ {!isFooterVisible && typeof column.footer === 'function' && (
294
+ <>
295
+ <ContextMenu.Item onClick={() => showFooter('count')}>
296
+ <TableFooterIcon />
297
+ Afficher le total
298
+ </ContextMenu.Item>
299
+ <ContextMenu.Divider />
300
+ </>
301
+ )}
302
+ {showTotalButton && typeof column.footer === 'object' && (
303
+ <>
304
+ <ContextMenu.ParentItem>
305
+ <TableFooterIcon />
306
+ Afficher le total
307
+ <ContextMenu.SubMenu>
308
+ {Object.keys(column.footer).map((key) => {
309
+ const TotalIcon =
310
+ footerFunctionsIcons[
311
+ key as DataGridFooterPredefinedFunction
312
+ ] ?? TableFooterIcon;
313
+ return (
314
+ <ContextMenu.Item
315
+ key={key}
316
+ onClick={() => showFooter(key)}
317
+ >
318
+ <TotalIcon />
319
+ {key in footerFunctionsTexts
320
+ ? footerFunctionsTexts[
321
+ key as DataGridFooterPredefinedFunction
322
+ ]
323
+ : key}
324
+ </ContextMenu.Item>
325
+ );
326
+ })}
327
+ <ContextMenu.Divider />
328
+ <ContextMenu.Item
329
+ onClick={hideFooter}
330
+ disabled={!isFooterVisible}
331
+ >
332
+ <TableFooterSlashIcon />
333
+ Masquer le total
334
+ </ContextMenu.Item>
335
+ </ContextMenu.SubMenu>
336
+ </ContextMenu.ParentItem>
337
+ <ContextMenu.Divider />
338
+ </>
339
+ )}
340
+ </>
341
+ )}
342
+ <ContextMenu.Item onClick={openModal}>
343
+ <FilterIcon />
344
+ Filtrer ...
345
+ </ContextMenu.Item>
346
+ <ContextMenu.Item
347
+ $color="danger"
348
+ onClick={clearFilter}
349
+ disabled={!hasFilters}
350
+ >
351
+ <FilterSlashIcon />
352
+ Supprimer le filtre
353
+ </ContextMenu.Item>
354
+ <ContextMenu.Divider />
355
+ <styles.InputContainer>
356
+ <MagnifierIcon />
357
+ <Input
358
+ ref={textFilterInputRef}
359
+ type="text"
360
+ name="search"
361
+ id="search"
362
+ placeholder="Rechercher ..."
363
+ value={textFilter}
364
+ onChange={(e) => setTextFilter(e.target.value)}
365
+ />
366
+ </styles.InputContainer>
367
+ <styles.CheckboxesContainer>
368
+ {checkboxesComponent}
369
+ </styles.CheckboxesContainer>
370
+ </>
371
+ );
372
+
373
+ if (!contextMenu) {
374
+ return <MenuContainer>{content}</MenuContainer>;
375
+ }
376
+
377
+ return <ContextMenu>{content}</ContextMenu>;
378
+ };