diff --git a/package.json b/package.json
index a74c3db..3855313 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,12 @@
"@mui/icons-material": "^5.15.6",
"@mui/material": "^5.15.6",
"@mui/material-nextjs": "^5.15.6",
+ "@mui/x-date-pickers": "^6.19.5",
+ "date-fns": "^3.3.1",
+ "date-fns-jalali": "^2.13.0-0",
+ "dayjs": "^1.11.10",
"formik": "^2.4.5",
+ "jalali-moment": "^3.3.11",
"material-react-table": "^2.11.2",
"next": "14.1.0",
"react": "^18",
diff --git a/src/components/BetweenDateFilter.jsx b/src/components/BetweenDateFilter.jsx
new file mode 100644
index 0000000..e8e8c18
--- /dev/null
+++ b/src/components/BetweenDateFilter.jsx
@@ -0,0 +1,31 @@
+import {Grid} from '@mui/material';
+import MuiDatePicker from "@/components/MuiDatePicker";
+import {useEffect, useState} from "react";
+
+const BetWeenDateFilter = ({formik, id , defaultValue}) => {
+ useEffect(() => {
+ formik.setFieldValue(`${id}[0]`, defaultValue?.[0] || '');
+ formik.setFieldValue(`${id}[1]`, defaultValue?.[1] || '');
+ }, []);
+
+ const [dateRange, setDateRange] = useState(defaultValue);
+
+ const handleDateChange = (index, value) => {
+ const newRange = [...dateRange];
+ newRange[index] = value;
+ setDateRange(newRange);
+ };
+
+ return (
+
+
+ handleDateChange(0, value)}/>
+
+
+ handleDateChange(1, value)}/>
+
+
+ );
+};
+
+export default BetWeenDateFilter;
diff --git a/src/components/BetweenNumberFilter.jsx b/src/components/BetweenNumberFilter.jsx
new file mode 100644
index 0000000..0b3512a
--- /dev/null
+++ b/src/components/BetweenNumberFilter.jsx
@@ -0,0 +1,58 @@
+import {Grid, TextField} from '@mui/material';
+import {useEffect} from "react";
+
+const BetweenNumberFilter = ({formik, id , defaultValue}) => {
+
+ useEffect(() => {
+ formik.setFieldValue(`${id}[0]`, defaultValue?.[0] || '');
+ formik.setFieldValue(`${id}[1]`, defaultValue?.[1] || '');
+ }, []);
+ const handleBlur = (index) => () => {
+ const minValue = formik.values[id][0];
+ const maxValue = formik.values[id][1];
+
+ if (index === 0 && minValue !== '' && maxValue !== '' && parseInt(minValue) > parseInt(maxValue)) {
+ formik.setFieldValue(`${id}[0]`, maxValue);
+ }
+
+ if (index === 1 && minValue !== '' && maxValue !== '' && parseInt(maxValue) < parseInt(minValue)) {
+ formik.setFieldValue(`${id}[1]`, minValue);
+ }
+ };
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+export default BetweenNumberFilter;
diff --git a/src/components/CustomToolbar.jsx b/src/components/CustomToolbar.jsx
new file mode 100644
index 0000000..344b5da
--- /dev/null
+++ b/src/components/CustomToolbar.jsx
@@ -0,0 +1,20 @@
+import FilterColumn from "@/components/FilterColumn";
+
+const CustomToolbar = ({columns}) => {
+ return (
+
+ )
+}
+export default CustomToolbar
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/DataTableMain.jsx b/src/components/DataTableMain.jsx
index 76f4b28..a6e8c91 100644
--- a/src/components/DataTableMain.jsx
+++ b/src/components/DataTableMain.jsx
@@ -1,9 +1,9 @@
import DataTableTest from "@/components/DataTableTest";
import FilterColumn from "@/components/FilterColumn";
import {useMemo} from "react";
+import {Button} from "@mui/material";
function DataTableMain() {
-
const columns = useMemo(
() => [
{
@@ -81,13 +81,58 @@ function DataTableMain() {
"contains"
],
},
+ {
+ accessorKey: 'state',
+ header: 'محدوده',
+ size: 150,
+ id: "betweenNum",
+ text:'محدوده',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "betweenNum",
+ columnFilterModeOptions: [],
+ },
+ {
+ accessorKey: 'state',
+ header: 'تاریخ',
+ size: 150,
+ id: "betweenDate",
+ text:'تاریخ',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "betweenDate",
+ columnFilterModeOptions: [],
+ },
+ {
+ accessorKey: 'state',
+ header: 'شلکت',
+ size: 150,
+ id: "select2",
+ text:'تاریخ',
+ type: 'select2',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "equals",
+ columnFilterModeOptions: [],
+ },
+ {
+ accessorKey: 'state',
+ header: 'سلکت',
+ size: 150,
+ id: "select",
+ text:'تنتنت',
+ type: 'select',
+ enableColumnFilter: true,
+ datatype: "text",
+ filterFn: "equals",
+ columnFilterModeOptions: [],
+ },
],
[],
);
return (
<>
-
>
);
diff --git a/src/components/DataTableTest.jsx b/src/components/DataTableTest.jsx
index 7b988ae..bf610fb 100644
--- a/src/components/DataTableTest.jsx
+++ b/src/components/DataTableTest.jsx
@@ -2,7 +2,8 @@
import {useMemo, useState} from 'react';
import {MaterialReactTable} from 'material-react-table';
import useTableSetting from "@/lib/hooks/useTableSetting";
-import {Typography} from "@mui/material";
+import {IconButton, Tooltip, Typography} from "@mui/material";
+import CustomToolbar from "@/components/CustomToolbar";
const data = [
@@ -53,13 +54,16 @@ const data = [
},
];
+function RefreshIcon() {
+ return null;
+}
+
const TestDataTable = ({columns}) => {
const [userId, setUserId] = useState(2);
const [pageName, setPageName] = useState('loan');
const [tableName, setTableName] = useState('loan_table');
const {settingStore, hideAction, filterAction, sortAction,} = useTableSetting();
-
const onColumnVisibilityChange = (event) => {
const settingValue = event();
hideAction(userId, pageName, tableName, settingValue, columns);
@@ -76,6 +80,11 @@ const TestDataTable = ({columns}) => {
columns={columns}
data={data}
manualSorting={true}
+ renderTopToolbarCustomActions={({table}) => (<>
+
+ >)}
initialState={{
columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []
diff --git a/src/components/FilterBox.jsx b/src/components/FilterBox.jsx
new file mode 100644
index 0000000..f887918
--- /dev/null
+++ b/src/components/FilterBox.jsx
@@ -0,0 +1,30 @@
+import { Popper, Paper, List, ListItem, ListItemText, ClickAwayListener } from '@mui/material';
+
+function FilterBox({anchorEl , open = false, handleClose = () => {} , columnFilterModeOptions , onSelectFilter , defaultFilter}) {
+
+ const id = open ? 'simple-popper' : undefined;
+ const handleSelectFilter = (filterFn) => {
+ onSelectFilter(filterFn);
+ handleClose();
+ };
+
+ return (
+
+
+
+
+
+ {columnFilterModeOptions.map((option, index) => (
+ handleSelectFilter(option)} selected={option === defaultFilter} sx={{ cursor: 'pointer' }}>
+
+
+ ))}
+
+
+
+
+
+ );
+}
+
+export default FilterBox;
diff --git a/src/components/FilterColumn.jsx b/src/components/FilterColumn.jsx
index 28cbf0a..46e156b 100644
--- a/src/components/FilterColumn.jsx
+++ b/src/components/FilterColumn.jsx
@@ -1,83 +1,203 @@
"use client"
import {
- Button,
- Dialog, DialogActions,
- DialogContent,
- DialogTitle,
- FormControl
+ Box,
+ Button, Divider, Drawer,
+ FormControl, IconButton, styled, Typography
} from '@mui/material';
import {useFormik} from 'formik';
import TextFieldWithFnBox from "@/components/TextFieldWithFnBox";
import {useEffect, useState} from "react";
import useTableSetting from "@/lib/hooks/useTableSetting";
+import BetweenNumberFilter from "@/components/BetweenNumberFilter";
+import BetWeenDateFilter from "@/components/BetweenDateFilter";
+import SelectBoxMultiple from "@/components/SelectBoxMultiple";
+import SelectBox from "@/components/SelectBox";
+import CloseIcon from '@mui/icons-material/Close';
+import { useTheme } from '@mui/material/styles';
-function FilterColumn({ onSubmit , column }) {
+function FilterColumn({ columns }) {
+ const [open, setOpen] = useState(false);
const [selectedFilters, setSelectedFilters] = useState([]);
const [userId, setUserId] = useState(2);
const [pageName, setPageName] = useState('loan');
const [tableName, setTableName] = useState('loan_table');
const {settingStore, hideAction, filterAction, sortAction,} = useTableSetting();
-
+ const DrawerHeader = styled('div')(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ padding: theme.spacing(0, 1),
+ justifyContent: 'space-between',
+ }));
+ const toggleDrawer = (newOpen) => () => {
+ setOpen(newOpen);
+ };
+ const handleDrawerClose = () => {
+ setOpen(false);
+ };
useEffect(() => {
-
const settingValue = settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || [];
setSelectedFilters(settingValue.length > 0 ? settingValue : []);
- console.log(selectedFilters)
-
}, [settingStore]);
const handleSelectFilter = (fieldName, filterFn) => {
- const updatedFilters = [...selectedFilters];
- const filterIndex = updatedFilters.findIndex(filter => filter.id === fieldName);
+ const filterIndex = selectedFilters.findIndex(filter => filter.id === fieldName);
+
if (filterIndex !== -1) {
+ const updatedFilters = [...selectedFilters];
updatedFilters[filterIndex].fn = filterFn;
+ setSelectedFilters(updatedFilters);
} else {
- updatedFilters.push({ id: fieldName, fn: filterFn });
+ const newFilter = { id: fieldName, fn: filterFn };
+ const updatedFilters = [...selectedFilters, newFilter];
+ setSelectedFilters(updatedFilters);
}
- setSelectedFilters(updatedFilters);
};
- const formik = useFormik({
- initialValues: column.reduce((acc, column) => {
- if (column.enableColumnFilter) {
- acc[column.field] = '';
+ const initialValues = columns.reduce((acc, column) => {
+ if (column.enableColumnFilter) {
+ const filter = settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'].find((filter) => filter.id === column.id);
+ if (column.filterFn === 'betweenNum' || column.filterFn === 'betweenDate') {
+ acc[column.id] = filter?.value || ["", ""];
+ }
+ else if ( column.type === 'select2' ) {
+ acc[column.id] = filter?.value || [];
+ }
+ else {
+ acc[column.id] = filter?.value || '';
}
- return acc;
- }, {}),
- onSubmit: (values) => {
- // const selectedFilters = [
- // { "id": "name", "value": values.name1, "fn": values.cityFn, "datatype": values.nameDatatype },
- // { "id": "navgan_id", "value": values.name2, "fn": values.nameFn, "datatype": values.nameDatatype }
- // ];
-
-
}
+ return acc;
+ }, {});
+
+ const formik = useFormik({
+ initialValues,
+ onSubmit: (values) => {
+ console.log(values)
+ const updatedFilters = selectedFilters.map(filter => {
+ const value = values[filter.id];
+ if (value !== undefined) {
+ return { ...filter, value };
+ }
+ return filter;
+ });
+
+ Object.entries(values).forEach(([fieldName, value]) => {
+ const filterIndex = updatedFilters.findIndex(filter => filter.id === fieldName);
+ if (filterIndex === -1 && value !== '' && value !== null && value !== undefined) {
+ const column = columns.find(col => col.id === fieldName);
+ updatedFilters.push({ id: fieldName, value, fn: column.filterFn, datatype: column.datatype });
+ }
+ });
+
+ const newArrayFilter = updatedFilters.filter((filter) => {
+ const value = values[filter.id];
+
+ return (
+ value !== '' &&
+ value !== null &&
+ value !== undefined &&
+ !(Array.isArray(value) && (value.length === 0 || (value.length === 2 && value.every((v) => v === ''))))
+ );
+ });
+
+ setSelectedFilters(newArrayFilter);
+ filterAction(userId, pageName, tableName, newArrayFilter, columns);
+ }
+
});
-
return (
-
+ <>
+
+
+
+ فیلتر
+
+
+
+
+
+
+ {columns.map((column, index) => (
+ column.enableColumnFilter &&
+ (column.filterFn === 'betweenDate' ?
+ handleSelectFilter(column.id, filterFn)}
+ defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ''}
+ />
+ : column.filterFn === 'betweenNum' ?
+ handleSelectFilter(column.id, filterFn)}
+ defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ["", ""]}
+ />
+ : column.type === 'select2' ?
+ handleSelectFilter(column.id, filterFn)}
+ defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || []}
+ />
+ : column.type === 'select' ?
+ handleSelectFilter(column.id, filterFn)}
+ defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ''}
+ />
+ :
+ filter.id === column.id)?.fn || column.filterFn}
+ columnFilterModeOptions={column.columnFilterModeOptions}
+ onSelectFilter={(filterFn) => handleSelectFilter(column.id, filterFn)}
+ defaultValue={formik.values[column.id] || (settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ''}
+ />
+ )
+ ))}
+
+
+
+
+
+
+
+
+
+ >
);
}
diff --git a/src/components/MuiDatePicker.jsx b/src/components/MuiDatePicker.jsx
new file mode 100644
index 0000000..0929c61
--- /dev/null
+++ b/src/components/MuiDatePicker.jsx
@@ -0,0 +1,63 @@
+import { LocalizationProvider, MobileDateTimePicker } from "@mui/x-date-pickers";
+import { AdapterDateFnsJalali } from "@mui/x-date-pickers/AdapterDateFnsJalali";
+import moment from "jalali-moment";
+import { faIR } from "@mui/x-date-pickers/locales";
+import { Box, IconButton } from "@mui/material";
+import ClearIcon from "@mui/icons-material/Clear";
+import {useState} from "react";
+
+export default function MuiDatePicker({label, name, formik, defaultValue, minDate, maxDate, onDateChange }) {
+ const [pickerValue, setPickerValue] = useState(defaultValue ? new Date(defaultValue) : null);
+ return (
+
+
+
+ {
+ const date = new Date(newValue);
+ const formattedDate = moment(date)
+ .locale("en")
+ .format("YYYY-MM-DD HH:mm");
+ formik.setFieldValue(name, formattedDate);
+ onDateChange(formattedDate);
+ setPickerValue(newValue);
+ }}
+ slotProps={{
+ textField: {
+ placeholder: "تاریخ خود را وارد کنید",
+ helperText: label,
+ },
+ }}
+ value={pickerValue}
+ minDate={minDate ? new Date(minDate) : null}
+ maxDate={maxDate ? new Date(maxDate) : null}
+ />
+
+
+ {
+ formik.setFieldValue(name, "");
+ setPickerValue(null);
+ }}
+ sx={{
+ color: "#bfbfbf",
+ position: 'absolute',
+ right: '4px',
+ top: '35%',
+ transform: 'translateY(-50%)',
+ }}
+ >
+
+
+
+ );
+}
diff --git a/src/components/SelectBox.jsx b/src/components/SelectBox.jsx
new file mode 100644
index 0000000..88cce6e
--- /dev/null
+++ b/src/components/SelectBox.jsx
@@ -0,0 +1,53 @@
+import InputLabel from '@mui/material/InputLabel';
+import MenuItem from '@mui/material/MenuItem';
+import FormControl from '@mui/material/FormControl';
+import Select from '@mui/material/Select';
+import {useEffect, useState} from "react";
+const names = [
+ 'Oliver Hansen',
+ 'Van Henry',
+ 'April Tucker',
+ 'Ralph Hubbard',
+ 'Omar Alexander',
+ 'Carlos Abbott',
+ 'Miriam Wagner',
+ 'Bradley Wilkerson',
+ 'Virginia Andrews',
+ 'Kelly Snyder',
+];
+const SelectBox = ({ formik, id, defaultValue }) => {
+
+ useEffect(() => {
+ formik.setFieldValue(id, defaultValue || '');
+ }, []);
+
+ const [selectedValue, setSelectedValue] = useState(defaultValue || '');
+
+ const handleChange = (event) => {
+ const { value } = event.target;
+ setSelectedValue(value);
+ formik.setFieldValue(id, value);
+ };
+
+ return (
+ <>
+
+ انتخاب
+
+
+ >
+ );
+};
+
+export default SelectBox;
diff --git a/src/components/SelectBoxMultiple.jsx b/src/components/SelectBoxMultiple.jsx
new file mode 100644
index 0000000..b3b943d
--- /dev/null
+++ b/src/components/SelectBoxMultiple.jsx
@@ -0,0 +1,113 @@
+import { useTheme } from '@mui/material/styles';
+import Box from '@mui/material/Box';
+import OutlinedInput from '@mui/material/OutlinedInput';
+import InputLabel from '@mui/material/InputLabel';
+import MenuItem from '@mui/material/MenuItem';
+import FormControl from '@mui/material/FormControl';
+import Select from '@mui/material/Select';
+import Chip from '@mui/material/Chip';
+import {useEffect, useState} from "react";
+
+const ITEM_HEIGHT = 48;
+const ITEM_PADDING_TOP = 8;
+const MenuProps = {
+ PaperProps: {
+ style: {
+ maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
+ width: 250,
+ },
+ },
+};
+
+const names = [
+ 'Oliver Hansen',
+ 'Van Henry',
+ 'April Tucker',
+ 'Ralph Hubbard',
+ 'Omar Alexander',
+ 'Carlos Abbott',
+ 'Miriam Wagner',
+ 'Bradley Wilkerson',
+ 'Virginia Andrews',
+ 'Kelly Snyder',
+];
+
+function getStyles(name, personName, theme) {
+ return {
+ fontWeight:
+ personName.indexOf(name) === -1
+ ? theme.typography.fontWeightRegular
+ : theme.typography.fontWeightMedium,
+ };
+}
+
+const SelectBoxMultiple = ({formik, id , defaultValue}) => {
+ const theme = useTheme();
+ const [personName, setPersonName] = useState(defaultValue || []);
+
+ useEffect(() => {
+ defaultValue.forEach((value, index) => {
+ formik.setFieldValue(`${id}[${index}]`, value || '');
+ });
+
+ }, []);
+
+ const handleChange = (event) => {
+ const {
+ target: { value },
+ } = event;
+ console.log(value)
+ const selectedValues = typeof value === 'string' ? value.split(',') : value;
+ if (selectedValues.length === 0) {
+ setPersonName([]);
+ formik.setFieldValue(id, []);
+ } else {
+ setPersonName(selectedValues);
+ selectedValues.forEach((selectedValue, index) => {
+ formik.setFieldValue(`${id}[${index}]`, selectedValue);
+ });
+ }
+ };
+
+ return (
+ <>
+
+ انتخاب
+ }
+ renderValue={(selected) => (
+
+ {selected.map((value) => (
+
+ ))}
+
+ )}
+ MenuProps={MenuProps}
+ sx={{
+ "&.MuiSelect-select": {
+ minWidth: 0,
+ maxWidth: '100%',
+ overflow: 'hidden',
+ },
+ }}
+ >
+ {names.map((name) => (
+
+ ))}
+
+
+ >
+ );
+};
+
+export default SelectBoxMultiple;
diff --git a/src/components/TextFieldWithFnBox.jsx b/src/components/TextFieldWithFnBox.jsx
index a3c277d..6ba443b 100644
--- a/src/components/TextFieldWithFnBox.jsx
+++ b/src/components/TextFieldWithFnBox.jsx
@@ -1,12 +1,17 @@
import {IconButton, Paper, TextField} from '@mui/material';
-import AlignHorizontalCenterIcon from '@mui/icons-material/AlignHorizontalCenter';
-import {useState} from "react";
-// import FilterBox from "@/components/FiterBox";
+import ArtTrackIcon from '@mui/icons-material/ArtTrack';
+import {useEffect, useState} from "react";
+import FilterBox from "@/components/FilterBox";
+
+const TextFieldWithFnBox = ({formik, id , header, defaultValue, defaultFilter , columnFilterModeOptions ,onSelectFilter}) => {
-const TextFieldWithFnBox = ({formik, id , defaultFilter , columnFilterModeOptions ,onSelectFilter}) => {
const [anchorEl, setAnchorEl] = useState(null);
const [open, setOpen] = useState(false);
+ useEffect(() => {
+ formik.setFieldValue(id, defaultValue || '');
+ }, []);
+
const handleClick = (event) => {
setOpen((prevOpen) => !prevOpen);
setAnchorEl(anchorEl ? null : event.currentTarget);
@@ -21,16 +26,16 @@ const TextFieldWithFnBox = ({formik, id , defaultFilter , columnFilterModeOptio
<>
-
+
)
}}
@@ -38,7 +43,7 @@ const TextFieldWithFnBox = ({formik, id , defaultFilter , columnFilterModeOptio
position: 'relative'
}}
/>
- {/**/}
+
>
);
};
diff --git a/src/lib/contexts/tableSetting.jsx b/src/lib/contexts/tableSetting.jsx
index 8ade22b..d787d87 100644
--- a/src/lib/contexts/tableSetting.jsx
+++ b/src/lib/contexts/tableSetting.jsx
@@ -19,8 +19,8 @@ export const TableSettingProvider = ({children}) => {
const sortAction = useCallback((user_id, page_name, table_name, settingValue, columns) => {
updateSettingStorage(user_id, page_name, table_name, "sorts", settingValue, columns);
}, [])
- const filterAction = (user_id, page_name, table_name, settingValue) => {
- updateSettingStorage(user_id, page_name, table_name, "filters", settingValue);
+ const filterAction = (user_id, page_name, table_name, settingValue, columns) => {
+ updateSettingStorage(user_id, page_name, table_name, "filters", settingValue, columns);
}
const updateSettingStorage = (user_id, page_name, table_name, settingType, settingValue, columns) => {
const prevLocalStorage = JSON.parse(localStorage.getItem("_setting-app")) || {};
@@ -28,7 +28,7 @@ export const TableSettingProvider = ({children}) => {
const pageSettings = userSettings[page_name] || {};
const tableSettings = pageSettings[table_name] || {};
let newSettings;
- if (settingType === "sorts") {
+ if (settingType === "sorts" || settingType === "filters") {
newSettings = [...settingValue];
} else {
newSettings = {...tableSettings[settingType] || {}, ...settingValue};