rez-table-listing-mui 1.2.14 → 1.2.15

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.
@@ -0,0 +1,513 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import {
3
+ Box,
4
+ Select,
5
+ MenuItem,
6
+ FormControl,
7
+ Typography,
8
+ Checkbox,
9
+ FormControlLabel,
10
+ Grid,
11
+ Alert,
12
+ } from "@mui/material";
13
+
14
+ import ListingValues from "../common/listing-values";
15
+ import {
16
+ DndContext,
17
+ closestCenter,
18
+ KeyboardSensor,
19
+ MouseSensor,
20
+ TouchSensor,
21
+ useSensor,
22
+ useSensors,
23
+ DragEndEvent,
24
+ } from "@dnd-kit/core";
25
+ import {
26
+ QuickTabConfigProps,
27
+ SettingsQuickTabProps,
28
+ } from "../../../types/filter-settings";
29
+ import { TabsStyles } from "../style";
30
+ import InfoAlert from "../common/info-alert";
31
+ import { craftTableFilterSettingsOptionsProps } from "../../../types/table-options";
32
+ import { LANE_SELECTS } from "../constants";
33
+ import { Group } from "@mui/icons-material";
34
+
35
+ const GroupBy = ({
36
+ filterSettingStates,
37
+ columnsData,
38
+ tabsApiData,
39
+ tabsApiDataLoading,
40
+ }: {
41
+ filterSettingStates: craftTableFilterSettingsOptionsProps;
42
+ columnsData: any;
43
+ tabsApiData?: string[];
44
+ tabsApiDataLoading?: boolean;
45
+ }) => {
46
+ const { settingsData, setSettingsData, saveButtonError, setSaveButtonError } =
47
+ filterSettingStates;
48
+
49
+ const [searchTerm, setSearchTerm] = useState<string>("");
50
+ const [currentQuickAttribute, setCurrentQuickAttribute] = useState<string>(
51
+ settingsData?.quick_tab?.attribute || ""
52
+ );
53
+
54
+ const quickTabStates = settingsData?.quick_tab as any;
55
+
56
+ // In case there is no quick tab state from API
57
+ useEffect(() => {
58
+ const stateToArray =
59
+ (quickTabStates && Object.entries(quickTabStates)) || [];
60
+ const isEmptyState = stateToArray.length ? false : true;
61
+ console.log("columnsData", columnsData);
62
+ console.log("first", columnsData.lanes.map((item: any) => item.name)[0]);
63
+
64
+ if (isEmptyState) {
65
+ setSettingsData((prev) => ({
66
+ ...prev,
67
+ quick_tab: {
68
+ ...prev?.quick_tab,
69
+ attribute: LANE_SELECTS[0].value,
70
+ sorting: "asc",
71
+ },
72
+ }));
73
+ }
74
+ }, [columnsData]);
75
+
76
+ // When user changes attribute
77
+ useEffect(() => {
78
+ if (currentQuickAttribute === settingsData?.quick_tab?.attribute) return;
79
+
80
+ if (tabsApiData?.length) {
81
+ setSettingsData((prev) => ({
82
+ ...prev,
83
+ quick_tab: {
84
+ ...prev?.quick_tab,
85
+ hide_list: tabsApiData,
86
+ show_list: [],
87
+ },
88
+ }));
89
+
90
+ setCurrentQuickAttribute(settingsData?.quick_tab?.attribute || "");
91
+ }
92
+ }, [tabsApiData]);
93
+
94
+ // Validation when user changes show list or hide list
95
+ useEffect(() => {
96
+ const showList = quickTabStates?.show_list || [];
97
+ const hideList = quickTabStates?.hide_list || [];
98
+
99
+ if (showList || hideList) {
100
+ // Check if showList is valid (between 1 and 5 items)
101
+ const isValidShowList = showList.length > 0 && showList.length <= 5;
102
+ const ERROR_CODE = "quick_tab_error";
103
+
104
+ if (!isValidShowList) {
105
+ const errorMessage = {
106
+ type: ERROR_CODE,
107
+ message:
108
+ showList.length === 0
109
+ ? "Quick Lane: Please select at least one item"
110
+ : "Quick Lane: Please select no more than 5 items",
111
+ };
112
+
113
+ // Check if the error is already present in the messages array
114
+ const hasQuickTabError = saveButtonError?.messages?.some(
115
+ (message) => message.type === ERROR_CODE
116
+ );
117
+
118
+ // Update the error state
119
+
120
+ // Later we can use this to show error message when we will make error logic more simple
121
+ // setSaveButtonError((prev) => {
122
+ // const otherMessages =
123
+ // prev?.messages?.filter((message) => message.type !== ERROR_CODE) ||
124
+ // [];
125
+
126
+ // return {
127
+ // ...prev,
128
+ // hasError: true,
129
+ // messages: hasQuickTabError
130
+ // ? [...prev?.messages]
131
+ // : [...otherMessages, errorMessage],
132
+ // };
133
+ // });
134
+ } else {
135
+ const hasOtherMessages = saveButtonError?.messages?.some(
136
+ (message) => message.type !== ERROR_CODE
137
+ );
138
+ // Reset error state if the list is valid
139
+ // setSaveButtonError((prev) => ({
140
+ // ...prev,
141
+ // hasError: hasOtherMessages,
142
+ // messages:
143
+ // prev?.messages?.filter((message) => message.type !== ERROR_CODE) ||
144
+ // [],
145
+ // }));
146
+ }
147
+ }
148
+ }, [quickTabStates?.hide_list, quickTabStates?.show_list]);
149
+
150
+ const sortingOptions = [
151
+ { label: "A-Z", value: "asc" },
152
+ { label: "Z-A", value: "dsc" },
153
+ { label: "Count (Ascending)", value: "count_asc" },
154
+ { label: "Count (Descending)", value: "count_dsc" },
155
+ { label: "Custom", value: "custom" },
156
+ ];
157
+
158
+ // Convert show_list/hide_list to FilterValue[] for rendering only
159
+ const showListValues = (quickTabStates?.show_list || [])?.map((id: any) => ({
160
+ id,
161
+ label: id?.charAt(0)?.toUpperCase() + id?.slice(1),
162
+ }));
163
+ const hideListValues = (quickTabStates?.hide_list || []).map((id: any) => ({
164
+ id,
165
+ label: id?.charAt(0)?.toUpperCase() + id?.slice(1),
166
+ }));
167
+
168
+ const sensors = useSensors(
169
+ useSensor(MouseSensor),
170
+ useSensor(TouchSensor),
171
+ useSensor(KeyboardSensor)
172
+ );
173
+
174
+ // Drag and drop logic, update only local state
175
+ const handleDragEnd = (event: DragEndEvent) => {
176
+ const { active, over } = event;
177
+ if (!over) {
178
+ return;
179
+ }
180
+ const currentContainer = active.data.current?.containerId;
181
+ const overContainer = over.data.current?.containerId;
182
+ if (!currentContainer || !overContainer) return;
183
+ if (currentContainer === overContainer) {
184
+ // Reorder within the same list
185
+ let newShowList = [...(quickTabStates.show_list ?? [])];
186
+ let newHideList = [...(quickTabStates.hide_list ?? [])];
187
+ if (currentContainer === "list") {
188
+ const oldIndex = newHideList.indexOf(String(active.id));
189
+ const newIndex = newHideList.indexOf(String(over.id));
190
+ if (oldIndex !== -1 && newIndex !== -1) {
191
+ const [removed] = newHideList.splice(oldIndex, 1);
192
+ newHideList.splice(newIndex, 0, removed);
193
+ }
194
+ } else {
195
+ const oldIndex = newShowList.indexOf(String(active.id));
196
+ const newIndex = newShowList.indexOf(String(over.id));
197
+ if (oldIndex !== -1 && newIndex !== -1) {
198
+ const [removed] = newShowList.splice(oldIndex, 1);
199
+ newShowList.splice(newIndex, 0, removed);
200
+ }
201
+ }
202
+
203
+ setSettingsData((prev) => ({
204
+ ...prev,
205
+ quick_tab: {
206
+ ...prev?.quick_tab,
207
+ show_list: newShowList,
208
+ hide_list: newHideList,
209
+ },
210
+ }));
211
+ } else {
212
+ // Move between lists
213
+ let newShowList = [...(quickTabStates.show_list ?? [])];
214
+ let newHideList = [...(quickTabStates.hide_list ?? [])];
215
+ if (currentContainer === "list" && overContainer === "lanes") {
216
+ if (newShowList.length >= 5) return; // prevent overflow
217
+ // Move from hide to show
218
+
219
+ const idx = newHideList.indexOf(String(active.id));
220
+ if (idx !== -1) {
221
+ newHideList.splice(idx, 1);
222
+ newShowList.push(String(active.id));
223
+ }
224
+ } else if (currentContainer === "lanes" && overContainer === "list") {
225
+ // Move from show to hide
226
+ const idx = newShowList.indexOf(String(active.id));
227
+ if (idx !== -1) {
228
+ newShowList.splice(idx, 1);
229
+ newHideList.push(String(active.id));
230
+ }
231
+ }
232
+
233
+ setSettingsData((prev) => ({
234
+ ...prev,
235
+ quick_tab: {
236
+ ...prev?.quick_tab,
237
+ show_list: newShowList,
238
+ hide_list: newHideList,
239
+ },
240
+ }));
241
+ }
242
+ };
243
+
244
+ const filteredListValues = hideListValues.filter((value: any) =>
245
+ value?.label?.toLowerCase().includes(searchTerm.toLowerCase())
246
+ );
247
+
248
+ // Show All/Hide All logic (local only)
249
+ const handleShowAll = () => {
250
+ const currentShowList = quickTabStates.show_list || [];
251
+ const currentHideList = quickTabStates.hide_list || [];
252
+
253
+ const availableSlots = 5 - currentShowList.length;
254
+
255
+ if (availableSlots <= 0) return; // Already at limit
256
+
257
+ const limitedHideList = currentHideList.slice(0, availableSlots);
258
+
259
+ setSettingsData((prev) => ({
260
+ ...prev,
261
+ quick_tab: {
262
+ ...prev?.quick_tab,
263
+ show_list: [...currentShowList, ...limitedHideList],
264
+ hide_list: currentHideList.filter(
265
+ (item: string) => !limitedHideList.includes(item)
266
+ ),
267
+ },
268
+ }));
269
+ };
270
+
271
+ const handleHideAll = () => {
272
+ setSettingsData((prev) => ({
273
+ ...prev,
274
+ quick_tab: {
275
+ ...prev?.quick_tab,
276
+ hide_list: [
277
+ ...(prev?.quick_tab?.hide_list || []),
278
+ ...(prev?.quick_tab?.show_list || []),
279
+ ],
280
+ show_list: [],
281
+ },
282
+ }));
283
+ };
284
+
285
+ // Checkbox logic (local only)
286
+ const handleShowSubLaneChange = (e: React.ChangeEvent<HTMLInputElement>) => {
287
+ setSettingsData((prev) => ({
288
+ ...prev,
289
+ quick_tab: {
290
+ ...prev?.quick_tab,
291
+ showSubLane: e.target.checked,
292
+ },
293
+ }));
294
+ };
295
+
296
+ const handleShowColorColumnsChange = (
297
+ e: React.ChangeEvent<HTMLInputElement>
298
+ ) => {
299
+ setSettingsData((prev) => ({
300
+ ...prev,
301
+ quick_tab: {
302
+ ...prev?.quick_tab,
303
+ showColorColumns: e.target.checked,
304
+ },
305
+ }));
306
+ };
307
+
308
+ const handleItemToggle = (itemId: string, fromContainerId: string) => {
309
+ const toShowList = [...(quickTabStates.show_list ?? [])];
310
+ const toHideList = [...(quickTabStates.hide_list ?? [])];
311
+
312
+ if (fromContainerId === "list") {
313
+ if (toShowList.length >= 5) return; // prevent overflow
314
+ // Move from hide_list to show_list
315
+ const index = toHideList.indexOf(itemId);
316
+ if (index > -1) {
317
+ toHideList.splice(index, 1);
318
+ toShowList.push(itemId);
319
+ }
320
+ } else if (fromContainerId === "lanes") {
321
+ // Move from show_list to hide_list
322
+ const index = toShowList.indexOf(itemId);
323
+ if (index > -1) {
324
+ toShowList.splice(index, 1);
325
+ toHideList.push(itemId);
326
+ }
327
+ }
328
+
329
+ setSettingsData((prev) => ({
330
+ ...prev,
331
+ quick_tab: {
332
+ ...prev?.quick_tab,
333
+ show_list: toShowList,
334
+ hide_list: toHideList,
335
+ },
336
+ }));
337
+ };
338
+
339
+ const enableDND = quickTabStates?.sorting === "custom" ? true : false;
340
+
341
+ return (
342
+ <Box
343
+ sx={{
344
+ display: "flex",
345
+ flexDirection: "column",
346
+ // gap: "0.5rem",
347
+ height: "100%",
348
+ }}
349
+ >
350
+ <Typography variant="caption" sx={TabsStyles.mainTabsHeader}>
351
+ *Quick filter settings will be reflected in vertical lanes
352
+ </Typography>
353
+ <Box>
354
+ <Grid sx={{ position: "relative" }} container>
355
+ <Grid size={12}>
356
+ <Box>
357
+ <Grid sx={TabsStyles.mainTabDropdown} size={6}>
358
+ <FormControl sx={TabsStyles.mainTabSelect} size="small">
359
+ <Select
360
+ value={quickTabStates?.attribute || ""}
361
+ onChange={(e) =>
362
+ setSettingsData((prev) => ({
363
+ ...prev,
364
+ quick_tab: {
365
+ ...prev?.quick_tab,
366
+ attribute: e.target.value,
367
+ },
368
+ }))
369
+ }
370
+ displayEmpty
371
+ renderValue={(selected) => {
372
+ if (!selected) {
373
+ return <em>Select Attribute</em>;
374
+ }
375
+ return selected;
376
+ }}
377
+ >
378
+ {LANE_SELECTS.map((lane: any) => (
379
+ <MenuItem key={lane?.key} value={lane?.value}>
380
+ {lane?.value}
381
+ </MenuItem>
382
+ ))}
383
+ </Select>
384
+ </FormControl>
385
+ <FormControl
386
+ sx={TabsStyles.selectDropdownSeparator}
387
+ size="small"
388
+ >
389
+ <Select
390
+ value={quickTabStates?.sorting || "asc"}
391
+ onChange={(e) =>
392
+ setSettingsData((prev) => ({
393
+ ...prev,
394
+ quick_tab: {
395
+ ...prev?.quick_tab,
396
+ sorting: e.target.value,
397
+ },
398
+ }))
399
+ }
400
+ displayEmpty
401
+ renderValue={(selected) => {
402
+ if (!selected) {
403
+ return <em>Sort by</em>;
404
+ }
405
+ const option = sortingOptions.find(
406
+ (opt) => opt.value === selected
407
+ );
408
+ return option?.label || selected;
409
+ }}
410
+ >
411
+ {sortingOptions.map((option) => (
412
+ <MenuItem key={option?.value} value={option?.value}>
413
+ {option?.label}
414
+ </MenuItem>
415
+ ))}
416
+ </Select>
417
+ </FormControl>
418
+ </Grid>
419
+ </Box>
420
+ </Grid>
421
+ <Grid>
422
+ {/* <Alert
423
+ severity="info"
424
+ sx={{
425
+ fontSize: "12px",
426
+ color: "#088AB2",
427
+ }}
428
+ >
429
+ Please select at least 1 and at most 5 values to display as lanes.
430
+ </Alert> */}
431
+ </Grid>
432
+ <DndContext
433
+ sensors={sensors}
434
+ collisionDetection={closestCenter}
435
+ onDragEnd={handleDragEnd}
436
+ >
437
+ <Grid sx={{ mt: 2 }} container spacing={2} size={12}>
438
+ <ListingValues
439
+ buttonText="Show All"
440
+ onClick={handleShowAll}
441
+ headerText="List of Values"
442
+ filteredValues={filteredListValues}
443
+ searchTerm={searchTerm}
444
+ setSearchTerm={setSearchTerm}
445
+ containerId="list"
446
+ tabsApiDataLoading={tabsApiDataLoading}
447
+ onItemToggle={handleItemToggle}
448
+ enableDragAndDrop={enableDND}
449
+ />
450
+ <ListingValues
451
+ buttonText="Hide All"
452
+ onClick={handleHideAll}
453
+ headerText="View as Lanes"
454
+ filteredValues={showListValues}
455
+ containerId="lanes"
456
+ // tabsApiDataLoading={tabsApiDataLoading}
457
+ onItemToggle={handleItemToggle}
458
+ enableDragAndDrop={enableDND}
459
+ // AlertComponenet={
460
+ // <InfoAlert
461
+ // message="Please select at least 1 and at most 5 values to display as
462
+ // lanes."
463
+ // width={"49%"}
464
+ // position="absolute"
465
+ // color="#088AB2"
466
+ // top={10}
467
+ // zIndex={1}
468
+ // />
469
+ // }
470
+ />
471
+ </Grid>
472
+ </DndContext>
473
+ <Grid size={12}>
474
+ <Box sx={TabsStyles.checkboxStyle}>
475
+ <FormControlLabel
476
+ control={
477
+ <Checkbox
478
+ checked={quickTabStates?.showSubLane || false}
479
+ onChange={handleShowSubLaneChange}
480
+ size="small"
481
+ sx={{
482
+ "&.Mui-checked": {
483
+ color: "#7A5AF8",
484
+ },
485
+ }}
486
+ />
487
+ }
488
+ label="Show Sublane"
489
+ />
490
+ <FormControlLabel
491
+ control={
492
+ <Checkbox
493
+ checked={quickTabStates?.showColorColumns || false}
494
+ onChange={handleShowColorColumnsChange}
495
+ size="small"
496
+ sx={{
497
+ "&.Mui-checked": {
498
+ color: "#7A5AF8",
499
+ },
500
+ }}
501
+ />
502
+ }
503
+ label="Show Color columns"
504
+ />
505
+ </Box>
506
+ </Grid>
507
+ </Grid>
508
+ </Box>
509
+ </Box>
510
+ );
511
+ };
512
+
513
+ export default GroupBy;