rez-table-listing-mui 1.3.30 → 1.3.31

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.31",
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,480 @@
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?.saved_filter?.find(
170
+ (f: any) => f.id === selectedId
171
+ );
172
+ if (filterObj?.is_shared) {
173
+ return "My Filter";
174
+ }
175
+
176
+ return "Shared Filter";
177
+ };
178
+
179
+ const handleSave = () => {
180
+ persistPreferences(shareWithTeam, allowTeamEdit);
181
+ onSave(); // call original save handler
182
+ };
183
+
184
+ // Get the selected filter object from columnsData.saved_filter
185
+ const selectedFilter = columnsData?.shared_filter?.find(
186
+ (f: any) => f.id === selectedFilterId
187
+ );
188
+
189
+ const selectedSavedFilter = columnsData?.shared_filter?.find(
190
+ (f: any) => f.id === selectedFilterId
191
+ );
192
+
193
+ // Check if we should show prefields
194
+ const showPrefields =
195
+ selectedSavedFilter && selectedSavedFilter.is_editable === "true";
196
+
197
+ // Disable checkboxes if prefields are shown
198
+ const showCheckboxes = !showPrefields;
199
+
200
+ return (
201
+ <Dialog open={open} onClose={onClose} maxWidth="sm" fullWidth>
202
+ <DialogTitle sx={{ display: "flex", justifyContent: "space-between" }}>
203
+ <Typography fontSize={18} fontWeight={600} marginTop={1}>
204
+ {getModalTitle()}
205
+ </Typography>
206
+ <Box onClick={onClose} sx={{ cursor: "pointer" }}>
207
+ <CloseIcon />
208
+ </Box>
209
+ </DialogTitle>
210
+
211
+ <Divider sx={{ mx: "25px", my: "2px", bgcolor: "#f9f7f727" }} />
212
+
213
+ <DialogContent sx={{ pt: 0 }}>
214
+ <Typography my={1} fontSize={"14px"}>
215
+ Give a name to this filter so you can easily reuse it later.
216
+ </Typography>
217
+ {/* Filter Name */}
218
+ <Typography mt={2} fontSize={11}>
219
+ Filter Name
220
+ </Typography>
221
+ <TextField
222
+ fullWidth
223
+ size="small"
224
+ value={filterNameValue}
225
+ onChange={(e) =>
226
+ setFilterMaster((prev) => {
227
+ if (!prev) return prev;
228
+ return {
229
+ ...prev,
230
+ saved_filters: {
231
+ ...(prev.saved_filters ?? {}),
232
+ selectedName: e.target.value,
233
+ },
234
+ };
235
+ })
236
+ }
237
+ sx={{
238
+ mb: 0,
239
+ "& .MuiOutlinedInput-root": {
240
+ width: "50%",
241
+ "& fieldset": { borderColor: "#888" },
242
+ "&:hover fieldset": { borderColor: "black" },
243
+ "&.Mui-focused fieldset": { borderColor: primary },
244
+ "& .MuiInputBase-input": { fontSize: "12px" },
245
+ },
246
+ }}
247
+ />
248
+ {/* Description */}
249
+ <Typography mt={2} fontSize={11}>
250
+ Description
251
+ </Typography>
252
+ <TextField
253
+ fullWidth
254
+ minRows={3}
255
+ size="small"
256
+ sx={{
257
+ mb: 1,
258
+ "& .MuiOutlinedInput-root": {
259
+ "& fieldset": { borderColor: "#888" },
260
+ "&:hover fieldset": { borderColor: "black" },
261
+ "&.Mui-focused fieldset": { borderColor: primary },
262
+ },
263
+ }}
264
+ onChange={(e) =>
265
+ setFilterMaster((prev) => {
266
+ if (!prev) return prev;
267
+ return {
268
+ ...prev,
269
+ saved_filters: {
270
+ ...(prev.saved_filters ?? {}),
271
+ description: e.target.value,
272
+ },
273
+ };
274
+ })
275
+ }
276
+ />
277
+ <Typography sx={{ mb: 1, fontSize: 12, color: "#888" }}>
278
+ (Max. 300 Characters)
279
+ </Typography>
280
+ {/* Sharing Controls */}
281
+ {showCheckboxes && shouldShowSharingControls && (
282
+ <Box
283
+ sx={{
284
+ display: "flex",
285
+ flexDirection: "column",
286
+ gap: 0.5,
287
+ "& .MuiFormControlLabel-label": { fontSize: "14px" },
288
+ }}
289
+ >
290
+ <FormControlLabel
291
+ control={
292
+ <Checkbox
293
+ size="small"
294
+ checked={shareWithTeam}
295
+ onChange={handleShareToggle}
296
+ sx={{
297
+ color: "black",
298
+ "&.Mui-checked": { color: "black" },
299
+ }}
300
+ />
301
+ }
302
+ label="Share with team member."
303
+ />
304
+
305
+ {shareWithTeam && (
306
+ <FormControlLabel
307
+ control={
308
+ <Checkbox
309
+ size="small"
310
+ checked={allowTeamEdit}
311
+ onChange={handleAllowEditToggle}
312
+ sx={{
313
+ color: "black",
314
+ "&.Mui-checked": { color: "black" },
315
+ }}
316
+ />
317
+ }
318
+ label="Allow editing to Team member"
319
+ />
320
+ )}
321
+ </Box>
322
+ )}
323
+ {/* prefields */}
324
+ {showPrefields && (
325
+ <Box
326
+ sx={{
327
+ display: "grid",
328
+ gridTemplateColumns: "1fr 1fr",
329
+ gap: 3,
330
+ mt: 2,
331
+ }}
332
+ >
333
+ <Box>
334
+ <Typography
335
+ sx={{
336
+ fontSize: "14px",
337
+ fontWeight: 500,
338
+ color: "#6b6b6b",
339
+ mb: 0.5,
340
+ }}
341
+ >
342
+ Created On*
343
+ </Typography>
344
+
345
+ <TextField
346
+ fullWidth
347
+ sx={{ bgcolor: "#0E0C0B0F" }}
348
+ size="small"
349
+ value={
350
+ selectedFilter?.created_date
351
+ ? new Date(selectedFilter.created_date).toLocaleString()
352
+ : ""
353
+ } // format date
354
+ InputProps={{
355
+ readOnly: true,
356
+ endAdornment: (
357
+ <IconButton size="small">
358
+ <CalendarTodayIcon fontSize="small" />
359
+ </IconButton>
360
+ ),
361
+ }}
362
+ />
363
+ </Box>
364
+
365
+ <Box>
366
+ <Typography
367
+ sx={{
368
+ fontSize: "14px",
369
+ fontWeight: 500,
370
+ color: "#6b6b6b",
371
+ mb: 0.5,
372
+ }}
373
+ >
374
+ Created By*
375
+ </Typography>
376
+ <TextField
377
+ fullWidth
378
+ sx={{ bgcolor: "#0E0C0B0F" }}
379
+ size="small"
380
+ value={selectedFilter?.name || ""} // Use created_by (assuming it can be name or ID)
381
+ InputProps={{ readOnly: true }}
382
+ />
383
+ </Box>
384
+
385
+ <Box>
386
+ <Typography
387
+ sx={{
388
+ fontSize: "14px",
389
+ fontWeight: 500,
390
+ color: "#6b6b6b",
391
+ mb: 0.5,
392
+ }}
393
+ >
394
+ Modified On*
395
+ </Typography>
396
+
397
+ <TextField
398
+ fullWidth
399
+ sx={{ bgcolor: "#0E0C0B0F" }}
400
+ size="small"
401
+ value={
402
+ selectedFilter?.modified_date
403
+ ? new Date(selectedFilter.modified_date).toLocaleString()
404
+ : ""
405
+ }
406
+ InputProps={{
407
+ readOnly: true,
408
+ endAdornment: (
409
+ <IconButton size="small">
410
+ <CalendarTodayIcon fontSize="small" />
411
+ </IconButton>
412
+ ),
413
+ }}
414
+ />
415
+ </Box>
416
+
417
+ <Box>
418
+ <Typography
419
+ sx={{
420
+ fontSize: "14px",
421
+ fontWeight: 500,
422
+ color: "#6b6b6b",
423
+ mb: 0.5,
424
+ }}
425
+ >
426
+ Modified By*
427
+ </Typography>
428
+ <TextField
429
+ fullWidth
430
+ sx={{ bgcolor: "#0E0C0B0F" }}
431
+ size="small"
432
+ value={selectedFilter?.modified_by || ""}
433
+ InputProps={{
434
+ readOnly: true,
435
+ }}
436
+ />
437
+ </Box>
438
+ </Box>
439
+ )}
440
+ {/* Buttons */}
441
+ <Box
442
+ sx={{ display: "flex", justifyContent: "flex-end", gap: 1, mt: 3 }}
443
+ >
444
+ <Button
445
+ variant="outlined"
446
+ onClick={onClose}
447
+ sx={{
448
+ borderColor: primary,
449
+ color: primary,
450
+ "&:hover": {
451
+ borderColor: primary,
452
+ backgroundColor: "rgba(122, 90, 248, 0.04)",
453
+ },
454
+ }}
455
+ >
456
+ Cancel
457
+ </Button>
458
+
459
+ <Button
460
+ variant="contained"
461
+ onClick={handleSave}
462
+ disabled={isSaveDisabled}
463
+ sx={{
464
+ backgroundColor: primary,
465
+ "&:hover": { backgroundColor: "#6A4DE8" },
466
+ "&.Mui-disabled": {
467
+ backgroundColor: "#d7cefd",
468
+ color: "rgba(255, 255, 255, 0.7)",
469
+ },
470
+ }}
471
+ >
472
+ Save
473
+ </Button>
474
+ </Box>
475
+ </DialogContent>
476
+ </Dialog>
477
+ );
478
+ };
479
+
480
+ 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) => {