rez-table-listing-mui 1.3.30 → 1.3.32

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez-table-listing-mui",
3
- "version": "1.3.30",
3
+ "version": "1.3.32",
4
4
  "type": "module",
5
5
  "description": "A rez table listing component built on TanStack Table",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,479 @@
1
+ import {
2
+ ChangeEvent,
3
+ Dispatch,
4
+ SetStateAction,
5
+ useEffect,
6
+ useState,
7
+ } from "react";
8
+ import {
9
+ Box,
10
+ Button,
11
+ Checkbox,
12
+ Dialog,
13
+ DialogContent,
14
+ DialogTitle,
15
+ Divider,
16
+ FormControlLabel,
17
+ IconButton,
18
+ TextField,
19
+ Typography,
20
+ } from "@mui/material";
21
+ import { CloseIcon } from "../../../../assets/svg";
22
+ import {
23
+ FilterFormComponentProps,
24
+ FilterMasterStateProps,
25
+ } from "../../../types/filter";
26
+ import CalendarTodayIcon from "@mui/icons-material/CalendarToday";
27
+ // import { set } from "react-hook-form";
28
+
29
+ const primary = "#7A5AF8";
30
+
31
+ interface SavedFilterModalViewProps {
32
+ open: boolean;
33
+ onClose: () => void;
34
+ onSave: () => void;
35
+ filterMaster: FilterMasterStateProps | null;
36
+ setFilterMaster: Dispatch<SetStateAction<FilterMasterStateProps | null>>;
37
+ columnsData: FilterFormComponentProps | undefined;
38
+ hasSavedFilters?: boolean;
39
+ forceShowSharingControls?: boolean;
40
+ }
41
+
42
+ const SavedFilterModalView = ({
43
+ open,
44
+ onClose,
45
+ onSave,
46
+ filterMaster,
47
+ setFilterMaster,
48
+ columnsData,
49
+
50
+ forceShowSharingControls = false,
51
+ }: SavedFilterModalViewProps) => {
52
+ const [shareWithTeam, setShareWithTeam] = useState<boolean>(false);
53
+ const [allowTeamEdit, setAllowTeamEdit] = useState<boolean>(false);
54
+ const filterNameValue = filterMaster?.saved_filters?.selectedName ?? "";
55
+ const isSaveDisabled =
56
+ !filterNameValue.trim() || (allowTeamEdit && !shareWithTeam);
57
+
58
+ const selectedFilterId = filterMaster?.saved_filters?.selectedId || "";
59
+
60
+ // Reset modal state when modal opens for a new filter
61
+ useEffect(() => {
62
+ if (!open) return;
63
+
64
+ const filterId = filterMaster?.saved_filters?.selectedId;
65
+
66
+ // NEW FILTER → RESET everything
67
+ if (!filterId) {
68
+ setFilterMaster((prev: any) => {
69
+ if (!prev) return prev;
70
+ return {
71
+ ...prev,
72
+ saved_filters: {
73
+ ...(prev.saved_filters ?? {}),
74
+ selectedId: null,
75
+ selectedName: "",
76
+ description: "",
77
+ is_shared: false,
78
+ is_editable: false,
79
+ },
80
+ };
81
+ });
82
+ setShareWithTeam(false);
83
+ setAllowTeamEdit(false);
84
+ return;
85
+ }
86
+
87
+ // EXISTING FILTER → Load from columnsData.saved_filter
88
+ const filterObj = columnsData?.saved_filter?.find(
89
+ (f: any) => f.id === filterId
90
+ );
91
+ if (filterObj) {
92
+ setFilterMaster((prev) => {
93
+ if (!prev) return prev;
94
+ return {
95
+ ...prev,
96
+ saved_filters: {
97
+ ...(prev.saved_filters ?? {}),
98
+ selectedId: filterId,
99
+ selectedName: filterObj.name || "",
100
+ description: filterObj.description || "",
101
+ is_shared: filterObj.is_shared || false,
102
+ is_editable: filterObj.is_editable === "true",
103
+ },
104
+ };
105
+ });
106
+ const initialShare = filterObj.is_shared ?? false;
107
+ const initialAllow = initialShare
108
+ ? filterObj.is_editable === "true"
109
+ : false;
110
+ setShareWithTeam(initialShare);
111
+ setAllowTeamEdit(initialAllow);
112
+ }
113
+ }, [
114
+ open,
115
+ columnsData,
116
+ filterMaster?.saved_filters?.selectedId,
117
+ setFilterMaster,
118
+ ]); // Added dependencies for loading
119
+
120
+ // Persist toggle state to filterMaster
121
+ const persistPreferences = (sharedValue: boolean, editableValue: boolean) => {
122
+ setFilterMaster((prev) => {
123
+ if (!prev) return prev;
124
+
125
+ return {
126
+ ...prev,
127
+ saved_filters: {
128
+ ...(prev.saved_filters ?? {}),
129
+ is_shared: sharedValue,
130
+ is_editable: editableValue,
131
+ },
132
+ };
133
+ });
134
+ };
135
+
136
+ // Handle Share Toggle
137
+
138
+ const handleShareToggle = (
139
+ _: ChangeEvent<HTMLInputElement>,
140
+ checked: boolean
141
+ ) => {
142
+ setShareWithTeam(checked);
143
+
144
+ const nextAllow = checked ? allowTeamEdit : false;
145
+ setAllowTeamEdit(nextAllow);
146
+
147
+ persistPreferences(checked, nextAllow);
148
+ };
149
+
150
+ // Handle Allow Edit Toggle
151
+
152
+ const handleAllowEditToggle = (
153
+ _: ChangeEvent<HTMLInputElement>,
154
+ checked: boolean
155
+ ) => {
156
+ if (!shareWithTeam) return; // Cannot enable edit without sharing
157
+
158
+ setAllowTeamEdit(checked);
159
+ persistPreferences(shareWithTeam, checked);
160
+ };
161
+
162
+ const shouldShowSharingControls = forceShowSharingControls || true; // always show
163
+
164
+ const getModalTitle = () => {
165
+ const selectedId = filterMaster?.saved_filters?.selectedId;
166
+
167
+ if (!selectedId) return "Save Filter"; // new filter
168
+
169
+ const filterObj = columnsData?.shared_filter?.find(
170
+ (f: any) => f.id === selectedId
171
+ );
172
+ if (filterObj?.is_shared) {
173
+ return "Shared Filter";
174
+ }
175
+ return "My Filter";
176
+ };
177
+
178
+ const handleSave = () => {
179
+ persistPreferences(shareWithTeam, allowTeamEdit);
180
+ onSave(); // call original save handler
181
+ };
182
+
183
+ // Get the selected filter object from columnsData.saved_filter
184
+ const selectedFilter = columnsData?.shared_filter?.find(
185
+ (f: any) => f.id === selectedFilterId
186
+ );
187
+
188
+ const selectedSavedFilter = columnsData?.shared_filter?.find(
189
+ (f: any) => f.id === selectedFilterId
190
+ );
191
+
192
+ // Check if we should show prefields
193
+ const showPrefields =
194
+ selectedSavedFilter && selectedSavedFilter.is_editable === "true";
195
+
196
+ // Disable checkboxes if prefields are shown
197
+ const showCheckboxes = !showPrefields;
198
+
199
+ return (
200
+ <Dialog open={open} onClose={onClose} maxWidth="sm" fullWidth>
201
+ <DialogTitle sx={{ display: "flex", justifyContent: "space-between" }}>
202
+ <Typography fontSize={18} fontWeight={600} marginTop={1}>
203
+ {getModalTitle()}
204
+ </Typography>
205
+ <Box onClick={onClose} sx={{ cursor: "pointer" }}>
206
+ <CloseIcon />
207
+ </Box>
208
+ </DialogTitle>
209
+
210
+ <Divider sx={{ mx: "25px", my: "2px", bgcolor: "#f9f7f727" }} />
211
+
212
+ <DialogContent sx={{ pt: 0 }}>
213
+ <Typography my={1} fontSize={"14px"}>
214
+ Give a name to this filter so you can easily reuse it later.
215
+ </Typography>
216
+ {/* Filter Name */}
217
+ <Typography mt={2} fontSize={11}>
218
+ Filter Name
219
+ </Typography>
220
+ <TextField
221
+ fullWidth
222
+ size="small"
223
+ value={filterNameValue}
224
+ onChange={(e) =>
225
+ setFilterMaster((prev) => {
226
+ if (!prev) return prev;
227
+ return {
228
+ ...prev,
229
+ saved_filters: {
230
+ ...(prev.saved_filters ?? {}),
231
+ selectedName: e.target.value,
232
+ },
233
+ };
234
+ })
235
+ }
236
+ sx={{
237
+ mb: 0,
238
+ "& .MuiOutlinedInput-root": {
239
+ width: "50%",
240
+ "& fieldset": { borderColor: "#888" },
241
+ "&:hover fieldset": { borderColor: "black" },
242
+ "&.Mui-focused fieldset": { borderColor: primary },
243
+ "& .MuiInputBase-input": { fontSize: "12px" },
244
+ },
245
+ }}
246
+ />
247
+ {/* Description */}
248
+ <Typography mt={2} fontSize={11}>
249
+ Description
250
+ </Typography>
251
+ <TextField
252
+ fullWidth
253
+ minRows={3}
254
+ size="small"
255
+ sx={{
256
+ mb: 1,
257
+ "& .MuiOutlinedInput-root": {
258
+ "& fieldset": { borderColor: "#888" },
259
+ "&:hover fieldset": { borderColor: "black" },
260
+ "&.Mui-focused fieldset": { borderColor: primary },
261
+ },
262
+ }}
263
+ onChange={(e) =>
264
+ setFilterMaster((prev) => {
265
+ if (!prev) return prev;
266
+ return {
267
+ ...prev,
268
+ saved_filters: {
269
+ ...(prev.saved_filters ?? {}),
270
+ description: e.target.value,
271
+ },
272
+ };
273
+ })
274
+ }
275
+ />
276
+ <Typography sx={{ mb: 1, fontSize: 12, color: "#888" }}>
277
+ (Max. 300 Characters)
278
+ </Typography>
279
+ {/* Sharing Controls */}
280
+ {showCheckboxes && shouldShowSharingControls && (
281
+ <Box
282
+ sx={{
283
+ display: "flex",
284
+ flexDirection: "column",
285
+ gap: 0.5,
286
+ "& .MuiFormControlLabel-label": { fontSize: "14px" },
287
+ }}
288
+ >
289
+ <FormControlLabel
290
+ control={
291
+ <Checkbox
292
+ size="small"
293
+ checked={shareWithTeam}
294
+ onChange={handleShareToggle}
295
+ sx={{
296
+ color: "black",
297
+ "&.Mui-checked": { color: "black" },
298
+ }}
299
+ />
300
+ }
301
+ label="Share with team member."
302
+ />
303
+
304
+ {shareWithTeam && (
305
+ <FormControlLabel
306
+ control={
307
+ <Checkbox
308
+ size="small"
309
+ checked={allowTeamEdit}
310
+ onChange={handleAllowEditToggle}
311
+ sx={{
312
+ color: "black",
313
+ "&.Mui-checked": { color: "black" },
314
+ }}
315
+ />
316
+ }
317
+ label="Allow editing to Team member"
318
+ />
319
+ )}
320
+ </Box>
321
+ )}
322
+ {/* prefields */}
323
+ {showPrefields && (
324
+ <Box
325
+ sx={{
326
+ display: "grid",
327
+ gridTemplateColumns: "1fr 1fr",
328
+ gap: 3,
329
+ mt: 2,
330
+ }}
331
+ >
332
+ <Box>
333
+ <Typography
334
+ sx={{
335
+ fontSize: "14px",
336
+ fontWeight: 500,
337
+ color: "#6b6b6b",
338
+ mb: 0.5,
339
+ }}
340
+ >
341
+ Created On*
342
+ </Typography>
343
+
344
+ <TextField
345
+ fullWidth
346
+ sx={{ bgcolor: "#0E0C0B0F" }}
347
+ size="small"
348
+ value={
349
+ selectedFilter?.created_date
350
+ ? new Date(selectedFilter.created_date).toLocaleString()
351
+ : ""
352
+ } // format date
353
+ InputProps={{
354
+ readOnly: true,
355
+ endAdornment: (
356
+ <IconButton size="small">
357
+ <CalendarTodayIcon fontSize="small" />
358
+ </IconButton>
359
+ ),
360
+ }}
361
+ />
362
+ </Box>
363
+
364
+ <Box>
365
+ <Typography
366
+ sx={{
367
+ fontSize: "14px",
368
+ fontWeight: 500,
369
+ color: "#6b6b6b",
370
+ mb: 0.5,
371
+ }}
372
+ >
373
+ Created By*
374
+ </Typography>
375
+ <TextField
376
+ fullWidth
377
+ sx={{ bgcolor: "#0E0C0B0F" }}
378
+ size="small"
379
+ value={selectedFilter?.name || ""} // Use created_by (assuming it can be name or ID)
380
+ InputProps={{ readOnly: true }}
381
+ />
382
+ </Box>
383
+
384
+ <Box>
385
+ <Typography
386
+ sx={{
387
+ fontSize: "14px",
388
+ fontWeight: 500,
389
+ color: "#6b6b6b",
390
+ mb: 0.5,
391
+ }}
392
+ >
393
+ Modified On*
394
+ </Typography>
395
+
396
+ <TextField
397
+ fullWidth
398
+ sx={{ bgcolor: "#0E0C0B0F" }}
399
+ size="small"
400
+ value={
401
+ selectedFilter?.modified_date
402
+ ? new Date(selectedFilter.modified_date).toLocaleString()
403
+ : ""
404
+ }
405
+ InputProps={{
406
+ readOnly: true,
407
+ endAdornment: (
408
+ <IconButton size="small">
409
+ <CalendarTodayIcon fontSize="small" />
410
+ </IconButton>
411
+ ),
412
+ }}
413
+ />
414
+ </Box>
415
+
416
+ <Box>
417
+ <Typography
418
+ sx={{
419
+ fontSize: "14px",
420
+ fontWeight: 500,
421
+ color: "#6b6b6b",
422
+ mb: 0.5,
423
+ }}
424
+ >
425
+ Modified By*
426
+ </Typography>
427
+ <TextField
428
+ fullWidth
429
+ sx={{ bgcolor: "#0E0C0B0F" }}
430
+ size="small"
431
+ value={selectedFilter?.modified_by || ""}
432
+ InputProps={{
433
+ readOnly: true,
434
+ }}
435
+ />
436
+ </Box>
437
+ </Box>
438
+ )}
439
+ {/* Buttons */}
440
+ <Box
441
+ sx={{ display: "flex", justifyContent: "flex-end", gap: 1, mt: 3 }}
442
+ >
443
+ <Button
444
+ variant="outlined"
445
+ onClick={onClose}
446
+ sx={{
447
+ borderColor: primary,
448
+ color: primary,
449
+ "&:hover": {
450
+ borderColor: primary,
451
+ backgroundColor: "rgba(122, 90, 248, 0.04)",
452
+ },
453
+ }}
454
+ >
455
+ Cancel
456
+ </Button>
457
+
458
+ <Button
459
+ variant="contained"
460
+ onClick={handleSave}
461
+ disabled={isSaveDisabled}
462
+ sx={{
463
+ backgroundColor: primary,
464
+ "&:hover": { backgroundColor: "#6A4DE8" },
465
+ "&.Mui-disabled": {
466
+ backgroundColor: "#d7cefd",
467
+ color: "rgba(255, 255, 255, 0.7)",
468
+ },
469
+ }}
470
+ >
471
+ Save
472
+ </Button>
473
+ </Box>
474
+ </DialogContent>
475
+ </Dialog>
476
+ );
477
+ };
478
+
479
+ export default SavedFilterModalView;
@@ -18,7 +18,7 @@ const FormDatePicker = ({
18
18
  filter,
19
19
  control,
20
20
  sx,
21
- views = ["day"],
21
+ views = ["day", "month", "year"],
22
22
  onValueChange,
23
23
  }: FormDatePickerProps) => {
24
24
  const isRange = filter.filter_operator === "between";
@@ -1,5 +1,3 @@
1
- // !! DON'T DELETE THIS OLD CODE !! //
2
-
3
1
  import { FormControl, MenuItem, Select, SxProps, Theme } from "@mui/material";
4
2
  import { Controller, UseFormSetValue } from "react-hook-form";
5
3
  import { FilterStateProps } from "../../../../../types/filter";
@@ -18,6 +16,24 @@ interface FormDropdownProps {
18
16
  onValueChange?: () => void;
19
17
  }
20
18
 
19
+ // Allowed date-based operators
20
+ const DATE_ALLOWED_OPERATORS = [
21
+ "equal",
22
+ "before",
23
+ "after",
24
+ "between",
25
+ "is",
26
+ "today",
27
+ "is_day_before",
28
+ "is_day_after",
29
+ "is_month_before",
30
+ "is_month_after",
31
+ "is_before",
32
+ "is_after",
33
+ "is_on_or_before",
34
+ "is_on_or_after",
35
+ ];
36
+
21
37
  const FormDropdown = ({
22
38
  filter,
23
39
  control,
@@ -58,6 +74,19 @@ const FormDropdown = ({
58
74
 
59
75
  field.onChange(e);
60
76
 
77
+ // Reset value if operator switches date → non-date or non-date → date
78
+ if (filter.filter_attribute_data_type === "date") {
79
+ const wasDateOp = DATE_ALLOWED_OPERATORS.includes(oldOperator);
80
+ const isDateOpNow =
81
+ DATE_ALLOWED_OPERATORS.includes(newOperator);
82
+
83
+ if (wasDateOp !== isDateOpNow) {
84
+ setValue(`${filter?.filter_attribute_name}.value`, "", {
85
+ shouldDirty: true,
86
+ });
87
+ }
88
+ }
89
+
61
90
  if (
62
91
  (filter?.filter_attribute_data_type === "date" ||
63
92
  filter?.filter_attribute_data_type === "year") &&
@@ -101,3 +130,107 @@ const FormDropdown = ({
101
130
  };
102
131
 
103
132
  export default FormDropdown;
133
+
134
+ // !! DON'T DELETE THIS OLD CODE !! //
135
+
136
+ // import { FormControl, MenuItem, Select, SxProps, Theme } from "@mui/material";
137
+ // import { Controller, UseFormSetValue } from "react-hook-form";
138
+ // import { FilterStateProps } from "../../../../../types/filter";
139
+ // import moment from "moment";
140
+
141
+ // interface FormDropdownProps {
142
+ // filter: FilterStateProps;
143
+ // control: any;
144
+ // setValue: UseFormSetValue<any>;
145
+ // dropdownList: {
146
+ // label?: string;
147
+ // value?: string;
148
+ // }[];
149
+ // isLoading?: boolean;
150
+ // sx?: SxProps<Theme>;
151
+ // onValueChange?: () => void;
152
+ // }
153
+
154
+ // const FormDropdown = ({
155
+ // filter,
156
+ // control,
157
+ // setValue,
158
+ // dropdownList,
159
+ // isLoading = false,
160
+ // sx,
161
+ // onValueChange,
162
+ // }: FormDropdownProps) => {
163
+ // return (
164
+ // <Controller
165
+ // name={`${filter?.filter_attribute_name}.operator`}
166
+ // control={control}
167
+ // defaultValue={filter?.filter_operator || dropdownList?.[0]?.value || ""}
168
+ // render={({ field }) => (
169
+ // <FormControl sx={sx} size="small">
170
+ // <Select
171
+ // {...field}
172
+ // variant="standard"
173
+ // sx={{
174
+ // fontSize: "0.875rem",
175
+ // minWidth: 50,
176
+ // border: "none",
177
+ // boxShadow: "none",
178
+ // "& .MuiSelect-icon": {
179
+ // top: "45%",
180
+ // transform: "translateY(-50%)",
181
+ // "& .MuiOutlinedInput-input": {
182
+ // padding: "12px 20px",
183
+ // },
184
+ // },
185
+ // }}
186
+ // disabled={isLoading}
187
+ // disableUnderline
188
+ // onChange={(e) => {
189
+ // const newOperator = e.target.value;
190
+ // const oldOperator = field.value;
191
+
192
+ // field.onChange(e);
193
+
194
+ // if (
195
+ // (filter?.filter_attribute_data_type === "date" ||
196
+ // filter?.filter_attribute_data_type === "year") &&
197
+ // newOperator !== oldOperator
198
+ // ) {
199
+ // if (newOperator === "today") {
200
+ // setValue(
201
+ // `${filter?.filter_attribute_name}.value`,
202
+ // moment().format("YYYY-MM-DD"),
203
+ // {
204
+ // shouldDirty: true,
205
+ // }
206
+ // );
207
+ // } else if (newOperator === "between") {
208
+ // setValue(`${filter?.filter_attribute_name}.value`, ["", ""], {
209
+ // shouldDirty: true,
210
+ // });
211
+ // } else if (
212
+ // oldOperator === "between" ||
213
+ // oldOperator === "today"
214
+ // ) {
215
+ // setValue(`${filter?.filter_attribute_name}.value`, "", {
216
+ // shouldDirty: true,
217
+ // });
218
+ // }
219
+ // }
220
+
221
+ // onValueChange?.();
222
+ // }}
223
+ // >
224
+ // {dropdownList?.map((item, idx) => (
225
+ // <MenuItem key={idx} value={item.value}>
226
+ // {item.label}
227
+ // </MenuItem>
228
+ // ))}
229
+ // </Select>
230
+ // </FormControl>
231
+ // )}
232
+ // />
233
+ // );
234
+ // };
235
+
236
+ // export default FormDropdown;
@@ -33,10 +33,7 @@ const FormTextfield = ({
33
33
  padding: "12px 20px",
34
34
  },
35
35
  }}
36
- // type={filter?.filter_attribute_data_type || "text" || "number"}
37
- type={
38
- filter?.filter_attribute_data_type === "number" ? "number" : "text"
39
- }
36
+ type={filter?.filter_attribute_data_type || "text"}
40
37
  placeholder={"Enter value"}
41
38
  disabled={isLoading}
42
39
  onChange={(e) => {