Merge branch 'feature/mohammad_refactor_filter' into 'develop'
complete toast and notification for keeping data in local storage for... See merge request witel-front-end/rms!7
This commit is contained in:
@@ -22,12 +22,15 @@
|
||||
"formik": "^2.4.5",
|
||||
"jalali-moment": "^3.3.11",
|
||||
"material-react-table": "^2.11.2",
|
||||
"next": "14.1.0",
|
||||
"next": "^14.2.3",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-toastify": "^10.0.5",
|
||||
"sass": "^1.75.0",
|
||||
"stylis": "^4.3.1",
|
||||
"stylis-plugin-rtl": "^2.1.1"
|
||||
"stylis-plugin-rtl": "^2.1.1",
|
||||
"swr": "^2.2.5",
|
||||
"yup": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8",
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
@import "fontiran";
|
||||
@import "fontiran";
|
||||
|
||||
.filter-toast {
|
||||
box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px;
|
||||
background-color: #d0dfe8;
|
||||
border: 1px dashed #095b8c;
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
const Layout = ({children}) => {
|
||||
return (
|
||||
<>{children}</>
|
||||
<>
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {Box, Stack} from "@mui/material";
|
||||
import HeaderWithLogo from "@/components/layouts/dashboard/headerWithLogo";
|
||||
import HeaderWithSidebar from "@/components/layouts/dashboard/headerWithSidebar";
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
const Template = ({children}) => {
|
||||
return (<Stack>
|
||||
|
||||
@@ -2,7 +2,9 @@ import cacheProviderRtl from "@/core/utils/cacheRtl";
|
||||
import {CssBaseline, ThemeProvider} from "@mui/material";
|
||||
import theme from "@/core/utils/theme";
|
||||
import {AppRouterCacheProvider} from "@mui/material-nextjs/v14-appRouter";
|
||||
import "^/global.scss"
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
import "^/global.scss";
|
||||
import {ToastContainer} from "react-toastify";
|
||||
|
||||
const Template = ({children}) => {
|
||||
return (
|
||||
@@ -10,6 +12,7 @@ const Template = ({children}) => {
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline/>
|
||||
{children}
|
||||
<ToastContainer rtl containerId="filtering" closeButton={false}/>
|
||||
</ThemeProvider>
|
||||
</AppRouterCacheProvider>
|
||||
)
|
||||
|
||||
@@ -2,7 +2,9 @@ import {Button} from "@mui/material";
|
||||
|
||||
const Toolbar = ({columns}) => {
|
||||
return (
|
||||
<Button>add something</Button>
|
||||
<>
|
||||
<Button>add something</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Toolbar
|
||||
|
||||
@@ -17,7 +17,6 @@ function HomeComponent() {
|
||||
enableColumnFilter: true,
|
||||
datatype: "text",
|
||||
filterFn: "contains",
|
||||
inputType: "textField",
|
||||
enableClickToCopy: true,
|
||||
columnFilterModeOptions: [
|
||||
"equals",
|
||||
@@ -32,7 +31,6 @@ function HomeComponent() {
|
||||
enableColumnFilter: true,
|
||||
datatype: "text",
|
||||
filterFn: "equals",
|
||||
inputType: "textField",
|
||||
columnFilterModeOptions: [
|
||||
"equals",
|
||||
"notEquals",
|
||||
@@ -48,7 +46,6 @@ function HomeComponent() {
|
||||
enableColumnFilter: true,
|
||||
datatype: "text",
|
||||
filterFn: "notEquals",
|
||||
inputType: "textField",
|
||||
columnFilterModeOptions: [
|
||||
"equals",
|
||||
"notEquals",
|
||||
@@ -62,7 +59,6 @@ function HomeComponent() {
|
||||
enableColumnFilter: true,
|
||||
datatype: "text",
|
||||
filterFn: "equals",
|
||||
inputType: "textField",
|
||||
columnFilterModeOptions: [
|
||||
"equals",
|
||||
"notEquals",
|
||||
@@ -71,7 +67,6 @@ function HomeComponent() {
|
||||
"greaterThan",
|
||||
"fuzzy"
|
||||
],
|
||||
|
||||
},
|
||||
{
|
||||
accessorKey: 'state',
|
||||
@@ -80,7 +75,6 @@ function HomeComponent() {
|
||||
enableColumnFilter: true,
|
||||
datatype: "text",
|
||||
filterFn: "contains",
|
||||
inputType: "textField",
|
||||
columnFilterModeOptions: [
|
||||
"equals",
|
||||
"notEquals",
|
||||
@@ -92,9 +86,20 @@ function HomeComponent() {
|
||||
header: 'محدوده',
|
||||
id: "price",
|
||||
enableColumnFilter: true,
|
||||
datatype: "nemeric",
|
||||
datatype: "numeric",
|
||||
filterFn: "equals",
|
||||
columnFilterModeOptions: [
|
||||
"equals",
|
||||
"notEquals",
|
||||
],
|
||||
},
|
||||
{
|
||||
accessorKey: 'created_at',
|
||||
header: 'تاریخ ساخت',
|
||||
id: "created_at",
|
||||
enableColumnFilter: true,
|
||||
datatype: "date",
|
||||
filterFn: "between",
|
||||
inputType: "betweenNum",
|
||||
},
|
||||
{
|
||||
accessorKey: 'updated_at',
|
||||
@@ -102,26 +107,33 @@ function HomeComponent() {
|
||||
id: "updated_at",
|
||||
enableColumnFilter: true,
|
||||
datatype: "date",
|
||||
filterFn: "between",
|
||||
inputType: "betweenDate",
|
||||
filterFn: "equals",
|
||||
},
|
||||
{
|
||||
accessorKey: 'company',
|
||||
header: 'نام شرکت',
|
||||
id: "company",
|
||||
enableColumnFilter: true,
|
||||
datatype: "text",
|
||||
datatype: "numeric",
|
||||
filterFn: "equals",
|
||||
inputType: "select2",
|
||||
columnSelectOption: [
|
||||
{value: 1, label: "وایتل"},
|
||||
{value: 2, label: "اسنپ"},
|
||||
{value: 3, label: "تپسی"}
|
||||
],
|
||||
},
|
||||
{
|
||||
accessorKey: 'area',
|
||||
header: 'منطقه',
|
||||
id: "area",
|
||||
enableColumnFilter: true,
|
||||
datatype: "text",
|
||||
datatype: "array",
|
||||
filterFn: "equals",
|
||||
inputType: "select",
|
||||
columnSelectOption: [
|
||||
{value: 1, label: "وایتل"},
|
||||
{value: 2, label: "اسنپ"},
|
||||
{value: 3, label: "تپسی"}
|
||||
],
|
||||
},
|
||||
],
|
||||
[],
|
||||
@@ -129,7 +141,7 @@ function HomeComponent() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataTable columns={columns} userId={0} pageName={'testPage'} tableName={'testTable'}/>
|
||||
<DataTable columns={columns} table_url={"api"} user_id={0} page_name={'testPage'} table_name={'testTable'}/>
|
||||
</>
|
||||
|
||||
);
|
||||
|
||||
@@ -1,19 +1,174 @@
|
||||
import {useMaterialReactTable} from "material-react-table";
|
||||
import DataTable_Paper from "./table/Paper";
|
||||
import {useMaterialReactTable} from "material-react-table";
|
||||
import {FA_DATATABLE_LOCALIZATION} from "./localization/fa/datatable";
|
||||
import useTableSetting from "@/lib/hooks/useTableSetting";
|
||||
import {flattenArrayOfObjects} from "@/core/utils/flattenArrayOfObjects";
|
||||
import Toolbar from "@/components/home/Toolbar";
|
||||
import useDataTable from "@/lib/hooks/useDataTable";
|
||||
import {useMemo, useState} from "react";
|
||||
|
||||
const isTableInstanceProp = (props) => props.table !== undefined;
|
||||
const data = [
|
||||
{
|
||||
name: {
|
||||
firstName: 'امین',
|
||||
lastName: 'قاسمپور',
|
||||
},
|
||||
address: 'فلان جا',
|
||||
city: 'تهران',
|
||||
state: 'بررسی شده',
|
||||
price: '20',
|
||||
updated_at: '2024/05/02',
|
||||
company: 'همراه اول',
|
||||
area: 'منطقه 5',
|
||||
},
|
||||
{
|
||||
name: {
|
||||
firstName: 'امیر حسین',
|
||||
lastName: 'محمودی',
|
||||
},
|
||||
address: 'فلان جا',
|
||||
city: 'تهران',
|
||||
state: 'تکمیل شده',
|
||||
price: '27',
|
||||
updated_at: '2024/05/02',
|
||||
company: 'وایتل',
|
||||
area: 'منطقه 27',
|
||||
},
|
||||
{
|
||||
name: {
|
||||
firstName: 'یاسمن',
|
||||
lastName: 'علی اکبری',
|
||||
},
|
||||
address: 'فلان جا',
|
||||
city: 'شیراز',
|
||||
state: 'بررسی شده',
|
||||
price: '32',
|
||||
updated_at: '2024/05/02',
|
||||
company: 'ایرانسل',
|
||||
area: 'منطقه 23',
|
||||
},
|
||||
{
|
||||
name: {
|
||||
firstName: 'حمیدرضا',
|
||||
lastName: 'رنجبرپور',
|
||||
},
|
||||
address: 'فلان جا',
|
||||
city: 'اصفهان',
|
||||
state: 'بررسی شده',
|
||||
price: '45',
|
||||
updated_at: '2024/05/02',
|
||||
company: 'ایرانول',
|
||||
area: 'منطقه 21',
|
||||
},
|
||||
{
|
||||
name: {
|
||||
firstName: 'محمد',
|
||||
lastName: 'جلالی',
|
||||
},
|
||||
address: 'فلان جا',
|
||||
city: 'رشت',
|
||||
state: 'درحال بررسی',
|
||||
price: '8',
|
||||
updated_at: '2024/05/02',
|
||||
company: 'فیلیمو',
|
||||
area: 'منطقه 8',
|
||||
},
|
||||
];
|
||||
|
||||
const DataTable_Main = (props) => {
|
||||
let table;
|
||||
const {filterData} = useDataTable();
|
||||
const {table_url, user_id, page_name, table_name, columns, initialStateProps} = props
|
||||
const flatColumns = flattenArrayOfObjects(columns, 'columns')
|
||||
const {settingStore, hideAction, sortAction} = useTableSetting();
|
||||
const [pagination, setPagination] = useState({pageIndex: 0, pageSize: 10});
|
||||
const filters = Object.keys(filterData)
|
||||
.map(key => {
|
||||
const value = filterData[key].value;
|
||||
if (value !== "" &&
|
||||
!(Array.isArray(value) && (value.length === 0 || (value.length === 2 && value[0] === "" && value[1] === "")))) {
|
||||
return filterData[key];
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
const storedFilters = settingStore?.[user_id]?.[page_name]?.[table_name]?.filters;
|
||||
const finalFilters = storedFilters || filters;
|
||||
|
||||
if (isTableInstanceProp(props)) {
|
||||
table = props.table;
|
||||
} else {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
table = useMaterialReactTable(props);
|
||||
const fetchUrl = useMemo(() => {
|
||||
const params = new URLSearchParams();
|
||||
params.set("start", `${pagination.pageIndex * pagination.pageSize}`);
|
||||
params.set("size", pagination.pageSize);
|
||||
params.set("filters", JSON.stringify(finalFilters ?? []));
|
||||
params.set("sorting", JSON.stringify(settingStore?.[user_id]?.[page_name]?.[table_name]?.['sorts'] ?? []));
|
||||
return `${table_url}?${params}`;
|
||||
}, [table_url, finalFilters, pagination, columns]);
|
||||
|
||||
const onColumnVisibilityChange = (event) => {
|
||||
const settingValue = event();
|
||||
hideAction(user_id, page_name, table_name, settingValue, flatColumns);
|
||||
};
|
||||
const onSortingChange = (event) => {
|
||||
const settingValue = event(settingStore?.[user_id]?.[page_name]?.[table_name]?.['sorts'] || []);
|
||||
sortAction(user_id, page_name, table_name, settingValue, flatColumns);
|
||||
}
|
||||
|
||||
return <DataTable_Paper table={table} columns={props.columns}/>;
|
||||
const table = useMaterialReactTable({
|
||||
localization: FA_DATATABLE_LOCALIZATION,
|
||||
columns,
|
||||
data: data,
|
||||
// manualSorting: true,
|
||||
renderTopToolbarCustomActions: ({table}) => (<>
|
||||
<Toolbar
|
||||
columns={flatColumns}
|
||||
/>
|
||||
</>),
|
||||
initialState: {
|
||||
density: 'compact',
|
||||
columnVisibility: settingStore?.[user_id]?.[page_name]?.[table_name]?.['hides'],
|
||||
sorting: settingStore?.[user_id]?.[page_name]?.[table_name]?.['sorts'] || [],
|
||||
...initialStateProps
|
||||
},
|
||||
state: {
|
||||
columnVisibility: settingStore?.[user_id]?.[page_name]?.[table_name]?.['hides'],
|
||||
sorting: settingStore?.[user_id]?.[page_name]?.[table_name]?.['sorts'] || [],
|
||||
pagination
|
||||
},
|
||||
muiTableHeadProps: {
|
||||
sx: {
|
||||
borderTop: "1px solid #e1e1e1",
|
||||
borderBottom: "2px solid #e1e1e1",
|
||||
}
|
||||
},
|
||||
muiTableHeadCellProps: {
|
||||
sx: {
|
||||
color: "primary.main",
|
||||
borderLeft: "1px solid #e1e1e1",
|
||||
"&:first-of-type": {
|
||||
borderLeft: "unset"
|
||||
}
|
||||
},
|
||||
},
|
||||
muiTableBodyCellProps: {
|
||||
sx: {
|
||||
borderLeft: "1px solid #e1e1e1",
|
||||
"&:first-of-type": {
|
||||
borderLeft: "unset"
|
||||
}
|
||||
},
|
||||
},
|
||||
manualFiltering: true,
|
||||
manualPagination: true,
|
||||
onPaginationChange: setPagination,
|
||||
manualSorting: true,
|
||||
onColumnVisibilityChange: onColumnVisibilityChange,
|
||||
onSortingChange: onSortingChange,
|
||||
...props
|
||||
})
|
||||
|
||||
return <DataTable_Paper table={table} columns={flatColumns} user_id={user_id} page_name={page_name}
|
||||
table_name={table_name}/>;
|
||||
};
|
||||
|
||||
export default DataTable_Main;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
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 (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<TextField
|
||||
name={`${id}[0]`}
|
||||
label="کمترین رقم"
|
||||
id={`${id}_min`}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={handleBlur(0)}
|
||||
fullWidth
|
||||
value={formik.values[id][0] || ''}
|
||||
margin="normal"
|
||||
sx={{
|
||||
position: 'relative'
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<TextField
|
||||
name={`${id}[1]`}
|
||||
label="بیشترین رقم"
|
||||
id={`${id}_max`}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={handleBlur(1)}
|
||||
fullWidth
|
||||
value={formik.values[id][1] || ''}
|
||||
margin="normal"
|
||||
sx={{
|
||||
position: 'relative'
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default BetweenNumberFilter;
|
||||
@@ -1,35 +0,0 @@
|
||||
import {Grid} from '@mui/material';
|
||||
import MuiDatePicker from "@/core/components/DataTable/filter/DatePicker/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 (
|
||||
<Grid container spacing={1} mt={1} mb={2}>
|
||||
<Grid item xs={6}>
|
||||
<MuiDatePicker label="تاریخ شروع " name={`${id}[0]`} formik={formik} defaultValue={defaultValue?.[0]}
|
||||
minDate={null} maxDate={dateRange[1]}
|
||||
onDateChange={(value) => handleDateChange(0, value)}/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<MuiDatePicker label="تاریخ پایان" name={`${id}[1]`} formik={formik} defaultValue={defaultValue?.[1]}
|
||||
minDate={dateRange[0]} maxDate={null}
|
||||
onDateChange={(value) => handleDateChange(1, value)}/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default BetWeenDateFilter;
|
||||
@@ -1,67 +0,0 @@
|
||||
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, InputAdornment, TextField } 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 (
|
||||
<Box sx={{ position: 'relative' }}>
|
||||
<LocalizationProvider
|
||||
dateAdapter={AdapterDateFnsJalali}
|
||||
localeText={
|
||||
faIR.components.MuiLocalizationProvider.defaultProps.localeText
|
||||
}
|
||||
>
|
||||
<MobileDateTimePicker
|
||||
key={formik.values[name]}
|
||||
ampm={false}
|
||||
onChange={(newValue) => {
|
||||
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,
|
||||
InputProps: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
formik.setFieldValue(name, "");
|
||||
setPickerValue(null);
|
||||
}}
|
||||
sx={{
|
||||
color: "#bfbfbf",
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(189, 189, 189, 0.1)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ClearIcon />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
},
|
||||
}}
|
||||
value={pickerValue}
|
||||
minDate={minDate ? new Date(minDate) : null}
|
||||
maxDate={maxDate ? new Date(maxDate) : null}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
207
src/core/components/DataTable/filter/FilterBody.jsx
Normal file
207
src/core/components/DataTable/filter/FilterBody.jsx
Normal file
@@ -0,0 +1,207 @@
|
||||
'use client'
|
||||
|
||||
import {Box, Button, Drawer, styled} from "@mui/material";
|
||||
import {Form, Formik} from "formik";
|
||||
import useTableSetting from "@/lib/hooks/useTableSetting";
|
||||
import {useEffect, useRef, useState} from "react";
|
||||
import FilterHeader from "@/core/components/DataTable/filter/FilterHeader";
|
||||
import * as Yup from "yup";
|
||||
import FilterBodyField from "@/core/components/DataTable/filter/FilterBodyField";
|
||||
import {toast} from "react-toastify";
|
||||
import AskForKeep from "@/core/components/NotificationDesign/AskForKeep";
|
||||
import useDataTable from "@/lib/hooks/useDataTable";
|
||||
|
||||
const ScrollBox = styled(Box)({
|
||||
flexGrow: 1,
|
||||
overflowY: 'scroll',
|
||||
maxWidth: "450px",
|
||||
'::-webkit-scrollbar': {
|
||||
width: '5px',
|
||||
},
|
||||
'::-webkit-scrollbar-track': {
|
||||
boxShadow: 'inset 0 0 5px #fff',
|
||||
borderRadius: '5px',
|
||||
},
|
||||
'::-webkit-scrollbar-thumb': {
|
||||
background: '#155175',
|
||||
borderRadius: '0px',
|
||||
}
|
||||
});
|
||||
|
||||
function FilterBody({columns, drawerState, setDrawerState, user_id, page_name, table_name}) {
|
||||
const {settingStore, filterAction} = useTableSetting();
|
||||
const [initialValues, setInitialValues] = useState({});
|
||||
const [validationSchema, setValidationSchema] = useState({});
|
||||
const {filterData, setFilterData} = useDataTable();
|
||||
const filterDataRef = useRef(filterData);
|
||||
useEffect(() => {
|
||||
filterDataRef.current = filterData;
|
||||
}, [filterData]);
|
||||
|
||||
useEffect(() => {
|
||||
const values = columns.reduce((acc, column) => {
|
||||
if (!column.enableColumnFilter) return acc;
|
||||
const filter = settingStore?.[user_id]?.[page_name]?.[table_name]?.['filters']?.find((filter) => filter.id === column.id);
|
||||
if (column.datatype === 'array') {
|
||||
acc[column.id] = {
|
||||
id: column.id,
|
||||
value: filter?.value || [],
|
||||
filterFn: filter?.fn || column._filterFn,
|
||||
datatype: column.datatype
|
||||
};
|
||||
} else if (column._filterFn === "between") {
|
||||
acc[column.id] = {
|
||||
id: column.id,
|
||||
value: filter?.value || ["", ""],
|
||||
filterFn: filter?.fn || column._filterFn,
|
||||
datatype: column.datatype
|
||||
};
|
||||
} else {
|
||||
acc[column.id] = {
|
||||
id: column.id,
|
||||
value: filter?.value || '',
|
||||
filterFn: filter?.fn || column._filterFn,
|
||||
datatype: column.datatype
|
||||
};
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
setInitialValues(Object.keys(filterData).length !== 0 ? filterData : values);
|
||||
}, [columns, settingStore, user_id, page_name, table_name, filterData]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const generatedSchema = Yup.object().shape(
|
||||
Object.keys(initialValues).reduce((acc, key) => {
|
||||
const initialValue = initialValues[key];
|
||||
if (initialValue.filterFn === "between") {
|
||||
acc[key] = Yup.object().shape({
|
||||
value: Yup.array()
|
||||
.of(Yup.string().nullable())
|
||||
.test({
|
||||
test(value, ctx) {
|
||||
const [start, end] = value || ["", ""];
|
||||
if (initialValue.datatype === "numeric" && parseInt(end, 10) <= parseInt(start, 10)) {
|
||||
return ctx.createError({
|
||||
message: `مقدار وارده باید بیشتر از (${start}) باشد`,
|
||||
});
|
||||
} else if ((start && !end) || (!start && end)) {
|
||||
return ctx.createError({
|
||||
message: "این بخش را تکمیل نمایید",
|
||||
});
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
|
||||
setValidationSchema(generatedSchema);
|
||||
}, [initialValues]);
|
||||
|
||||
|
||||
const handleSubmit = (values, {setSubmitting}) => {
|
||||
toast.dismiss({containerId: "filtering"});
|
||||
setDrawerState(false);
|
||||
setFilterData(values);
|
||||
toast(<AskForKeep onSaveFilter={onSaveFilter}/>, {
|
||||
containerId: "filtering",
|
||||
className: "filter-toast",
|
||||
position: "bottom-left",
|
||||
draggable: true,
|
||||
autoClose: false,
|
||||
});
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
const onSaveFilter = () => {
|
||||
const currentFilterData = filterDataRef.current;
|
||||
const arrayOfObjects = Object.keys(currentFilterData).map(key => {
|
||||
const value = currentFilterData[key].value;
|
||||
if (value !== "" &&
|
||||
!(Array.isArray(value) && (value.length === 0 || (value.length === 2 && value[0] === "" && value[1] === "")))) {
|
||||
return currentFilterData[key];
|
||||
}
|
||||
}).filter(Boolean);
|
||||
filterAction(user_id, page_name, table_name, arrayOfObjects, columns)
|
||||
toast.dismiss({containerId: "filtering"});
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer open={drawerState} onClose={() => setDrawerState(false)}
|
||||
sx={{overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%"}}>
|
||||
<FilterHeader setDrawerState={setDrawerState}/>
|
||||
{Object.keys(initialValues).length > 0 && (
|
||||
<ScrollBox>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleSubmit}
|
||||
validationSchema={validationSchema}
|
||||
>
|
||||
{
|
||||
({
|
||||
values,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
setFieldValue,
|
||||
errors,
|
||||
touched,
|
||||
isSubmitting,
|
||||
isValid,
|
||||
dirty,
|
||||
resetForm
|
||||
}) => (
|
||||
<Form>
|
||||
<Box sx={{px: 1, py: 2}}>
|
||||
{
|
||||
columns.map((column) => (
|
||||
column.enableColumnFilter && (
|
||||
<FilterBodyField
|
||||
key={column.id}
|
||||
column={column}
|
||||
filterParameters={values[column.id]}
|
||||
handleChange={handleChange}
|
||||
handleBlur={handleBlur}
|
||||
setFieldValue={setFieldValue}
|
||||
resetForm={resetForm}
|
||||
errors={errors}
|
||||
touched={touched}
|
||||
/>
|
||||
)
|
||||
))
|
||||
}
|
||||
</Box>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
pb: 2
|
||||
}}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
size="large"
|
||||
disabled={isSubmitting || !isValid || !dirty}
|
||||
sx={{
|
||||
px: 8,
|
||||
boxShadow: "rgba(0, 0, 0, 0.23) 0px 6px 6px, rgba(0, 0, 0, 0.19) 10px 0px 20px",
|
||||
backgroundColor: "#155175", ':hover': {
|
||||
backgroundColor: "#155175",
|
||||
}
|
||||
}}>
|
||||
اعمال فیلتر
|
||||
</Button>
|
||||
</Box>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</ScrollBox>
|
||||
)}
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterBody;
|
||||
111
src/core/components/DataTable/filter/FilterBodyField.jsx
Normal file
111
src/core/components/DataTable/filter/FilterBodyField.jsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import {useState} from "react";
|
||||
import FilterOptionList from "@/core/components/DataTable/filter/FilterOptionList";
|
||||
import CustomSelect from "@/core/components/DataTable/filter/fieldsType/CustomSelect";
|
||||
import CustomTextField from "@/core/components/DataTable/filter/fieldsType/CustomTextField";
|
||||
import CustomTextFieldRange from "@/core/components/DataTable/filter/fieldsType/CustomTextFieldRange";
|
||||
import CustomDatePickerRange from "@/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePickerRange";
|
||||
import CustomDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePicker";
|
||||
import CustomSelectMultiple from "@/core/components/DataTable/filter/fieldsType/CustomSelectMultiple";
|
||||
|
||||
const columnFilterModeOptionFa = {
|
||||
equals: "برابر",
|
||||
notEquals: "نابرابر",
|
||||
contains: "شامل",
|
||||
lessThan: "کوچکتر",
|
||||
greaterThan: "بزرگتر",
|
||||
fuzzy: "فازی",
|
||||
between: "مابین"
|
||||
};
|
||||
|
||||
function FilterBodyField({
|
||||
column,
|
||||
filterParameters,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
setFieldValue,
|
||||
errors,
|
||||
touched,
|
||||
resetForm
|
||||
}) {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const defaultFilterTranslation = columnFilterModeOptionFa[filterParameters.filterFn] || filterParameters.filterFn;
|
||||
|
||||
const handleOpenFilterBox = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{filterParameters.datatype === "numeric" && filterParameters.filterFn === "between" ? (
|
||||
<CustomTextFieldRange
|
||||
column={column}
|
||||
filterParameters={filterParameters}
|
||||
defaultFilterTranslation={defaultFilterTranslation}
|
||||
handleOpenFilterBox={handleOpenFilterBox}
|
||||
handleBlur={handleBlur}
|
||||
handleChange={handleChange}
|
||||
errors={errors}
|
||||
touched={touched}
|
||||
/>
|
||||
) : filterParameters.datatype === "numeric" && filterParameters.filterFn === "equals" && Array.isArray(column.columnSelectOption) ? (
|
||||
<CustomSelect
|
||||
column={column}
|
||||
filterParameters={filterParameters}
|
||||
setFieldValue={setFieldValue}
|
||||
defaultFilterTranslation={defaultFilterTranslation}
|
||||
handleOpenFilterBox={handleOpenFilterBox}
|
||||
/>
|
||||
) : filterParameters.datatype === "date" && filterParameters.filterFn === "equals" ? (
|
||||
<CustomDatePicker
|
||||
column={column}
|
||||
filterParameters={filterParameters}
|
||||
setFieldValue={setFieldValue}
|
||||
defaultFilterTranslation={defaultFilterTranslation}
|
||||
handleOpenFilterBox={handleOpenFilterBox}
|
||||
/>
|
||||
) : filterParameters.datatype === "date" && filterParameters.filterFn === "between" ? (
|
||||
<CustomDatePickerRange
|
||||
column={column}
|
||||
filterParameters={filterParameters}
|
||||
setFieldValue={setFieldValue}
|
||||
defaultFilterTranslation={defaultFilterTranslation}
|
||||
handleOpenFilterBox={handleOpenFilterBox}
|
||||
errors={errors}
|
||||
touched={touched}
|
||||
/>
|
||||
) : filterParameters.datatype === "array" && filterParameters.filterFn === "equals" ? (
|
||||
<CustomSelectMultiple
|
||||
column={column}
|
||||
filterParameters={filterParameters}
|
||||
setFieldValue={setFieldValue}
|
||||
defaultFilterTranslation={defaultFilterTranslation}
|
||||
handleOpenFilterBox={handleOpenFilterBox}
|
||||
/>
|
||||
) : (
|
||||
<CustomTextField
|
||||
column={column}
|
||||
filterParameters={filterParameters}
|
||||
defaultFilterTranslation={defaultFilterTranslation}
|
||||
handleOpenFilterBox={handleOpenFilterBox}
|
||||
handleBlur={handleBlur}
|
||||
handleChange={(e) => setFieldValue(`${column.id}.value`, e.target.value)}
|
||||
/>
|
||||
)}
|
||||
{Array.isArray(column.columnFilterModeOptions) && (
|
||||
<FilterOptionList
|
||||
column={column}
|
||||
anchorEl={anchorEl}
|
||||
setAnchorEl={setAnchorEl}
|
||||
filterParameters={filterParameters}
|
||||
filterType={filterParameters.filterFn}
|
||||
setFieldValue={setFieldValue}
|
||||
resetForm={resetForm}
|
||||
filterOption={column.columnFilterModeOptions}
|
||||
columnFilterModeOptionFa={columnFilterModeOptionFa}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterBodyField;
|
||||
17
src/core/components/DataTable/filter/FilterButton.jsx
Normal file
17
src/core/components/DataTable/filter/FilterButton.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
'use client'
|
||||
import FilterAltIcon from '@mui/icons-material/FilterAlt';
|
||||
import {IconButton, Tooltip} from "@mui/material";
|
||||
|
||||
function FilterButton({drawerState, setDrawerState}) {
|
||||
return (
|
||||
<Tooltip title="فیلتر">
|
||||
<IconButton onClick={() => {
|
||||
setDrawerState(!drawerState)
|
||||
}} aria-label="filter table">
|
||||
<FilterAltIcon/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterButton;
|
||||
@@ -1,201 +0,0 @@
|
||||
"use client"
|
||||
import {Box, Button, Divider, Drawer, FormControl, IconButton, styled, Tooltip, Typography} from '@mui/material';
|
||||
import {useFormik} from 'formik';
|
||||
import {useEffect, useState} from "react";
|
||||
import useTableSetting from "@/lib/hooks/useTableSetting";
|
||||
import BetweenNumberFilter from "@/core/components/DataTable/filter/BetweenNumberFilter";
|
||||
import BetWeenDateFilter from "@/core/components/DataTable/filter/DatePicker/BetweenDateFilter";
|
||||
import SelectBoxMultiple from "@/core/components/DataTable/filter/SelectBoxMultiple";
|
||||
import SelectBox from "@/core/components/DataTable/filter/SelectBox";
|
||||
import FilterAltIcon from '@mui/icons-material/FilterAlt';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import TextFieldWithFnBox from "@/core/components/DataTable/filter/TextFieldWithFnBox/TextFieldWithFnBox";
|
||||
|
||||
function FilterColumn({columns}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [selectedFilters, setSelectedFilters] = useState([]);
|
||||
const [userId, setUserId] = useState(0);
|
||||
const [pageName, setPageName] = useState('testPage');
|
||||
const [tableName, setTableName] = useState('testTable');
|
||||
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 : []);
|
||||
if (!settingValue.length > 0) {
|
||||
formik.resetForm();
|
||||
}
|
||||
}, [settingStore]);
|
||||
|
||||
const handleSelectFilter = (fieldName, filterFn) => {
|
||||
const filterIndex = selectedFilters.findIndex(filter => filter.id === fieldName);
|
||||
if (filterIndex !== -1) {
|
||||
const updatedFilters = [...selectedFilters];
|
||||
updatedFilters[filterIndex].fn = filterFn;
|
||||
setSelectedFilters(updatedFilters);
|
||||
} else {
|
||||
const newFilter = {id: fieldName, fn: filterFn};
|
||||
const updatedFilters = [...selectedFilters, newFilter];
|
||||
setSelectedFilters(updatedFilters);
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
}, {});
|
||||
|
||||
const formik = useFormik({
|
||||
initialValues,
|
||||
onSubmit: (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);
|
||||
}
|
||||
|
||||
});
|
||||
console.log(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'])
|
||||
return (
|
||||
<>
|
||||
<Tooltip title="فیلتر">
|
||||
<IconButton onClick={toggleDrawer(true)} aria-label="filter table">
|
||||
<FilterAltIcon/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Drawer open={open} onClose={toggleDrawer(false)}
|
||||
sx={{
|
||||
"& .MuiDrawer-paper": {
|
||||
padding: '16px',
|
||||
overflowY: 'auto',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
scrollbarWidth: 'thin',
|
||||
width: 450,
|
||||
"&::-webkit-scrollbar": {
|
||||
width: '8px',
|
||||
},
|
||||
"&::-webkit-scrollbar-thumb": {
|
||||
backgroundColor: '#ccc',
|
||||
borderRadius: '4px',
|
||||
},
|
||||
"@media (max-width: 600px)": {
|
||||
width: '100%'
|
||||
},
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DrawerHeader>
|
||||
<Typography variant="h6" mb={2}>فیلتر</Typography>
|
||||
<IconButton onClick={handleDrawerClose}>
|
||||
<CloseIcon/>
|
||||
</IconButton>
|
||||
</DrawerHeader>
|
||||
<Box sx={{display: 'flex', flexDirection: 'column'}}>
|
||||
<FormControl fullWidth>
|
||||
{columns.map((column, index) => (
|
||||
column.enableColumnFilter &&
|
||||
(column.inputType === 'betweenDate' ?
|
||||
<BetWeenDateFilter
|
||||
key={index}
|
||||
id={column.id}
|
||||
formik={formik}
|
||||
defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ''}
|
||||
/>
|
||||
: column.inputType === 'betweenNum' ?
|
||||
<BetweenNumberFilter
|
||||
key={index}
|
||||
id={column.id}
|
||||
formik={formik}
|
||||
defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ["", ""]}
|
||||
/>
|
||||
: column.inputType === 'select2' ?
|
||||
<SelectBoxMultiple
|
||||
key={index}
|
||||
id={column.id}
|
||||
formik={formik}
|
||||
defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || []}
|
||||
/>
|
||||
: column.inputType === 'select' ?
|
||||
<SelectBox
|
||||
key={index}
|
||||
id={column.id}
|
||||
formik={formik}
|
||||
defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ''}
|
||||
/>
|
||||
:
|
||||
<TextFieldWithFnBox
|
||||
key={index}
|
||||
id={column.id}
|
||||
header={column.header}
|
||||
formik={formik}
|
||||
defaultFilter={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => 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 || ''}
|
||||
/>
|
||||
)
|
||||
))}
|
||||
</FormControl>
|
||||
</Box>
|
||||
<Box sx={{mt: 2}}>
|
||||
<Divider/>
|
||||
<Box sx={{display: 'flex', justifyContent: 'flex-end', alignItems: 'center'}}>
|
||||
<Button variant="contained" size="medium" onClick={formik.handleSubmit}
|
||||
sx={{mt: 1}}>ثبت</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterColumn;
|
||||
|
||||
|
||||
32
src/core/components/DataTable/filter/FilterHeader.jsx
Normal file
32
src/core/components/DataTable/filter/FilterHeader.jsx
Normal file
@@ -0,0 +1,32 @@
|
||||
'use client'
|
||||
|
||||
import {Box, IconButton, Typography} from "@mui/material";
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import FilterAltIcon from '@mui/icons-material/FilterAlt';
|
||||
|
||||
function FilterHeader({setDrawerState}) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
px: 2,
|
||||
py: 1,
|
||||
backgroundColor: "#155175",
|
||||
boxShadow: "rgba(0, 0, 0, 0.19) 0px 10px 20px, rgba(0, 0, 0, 0.23) 0px 6px 6px",
|
||||
maxWidth: "450px"
|
||||
}}
|
||||
>
|
||||
<Box sx={{display: "flex", alignItems: "center"}}>
|
||||
<FilterAltIcon sx={{color: "#fff"}}/>
|
||||
<Typography variant="h6" sx={{color: "#fff"}}>فیلتر</Typography>
|
||||
</Box>
|
||||
<IconButton sx={{color: "#fff"}} onClick={() => setDrawerState(false)}>
|
||||
<CancelIcon sx={{fontSize: "1em"}}/>
|
||||
</IconButton>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterHeader;
|
||||
48
src/core/components/DataTable/filter/FilterOptionList.jsx
Normal file
48
src/core/components/DataTable/filter/FilterOptionList.jsx
Normal file
@@ -0,0 +1,48 @@
|
||||
'use client'
|
||||
|
||||
import {ListItem, Menu} from "@mui/material";
|
||||
import {useState} from "react";
|
||||
|
||||
function FilterOptionList({
|
||||
column,
|
||||
filterType,
|
||||
filterOption,
|
||||
anchorEl,
|
||||
columnFilterModeOptionFa,
|
||||
setAnchorEl,
|
||||
setFieldValue
|
||||
}) {
|
||||
const [selectedFilter, setSelectedFilter] = useState(filterType);
|
||||
|
||||
const handleChangeItem = (event, index) => {
|
||||
setFieldValue(`${column.id}.value`, filterOption[index] === "between" ? ["", ""] : "");
|
||||
setFieldValue(`${column.id}.filterFn`, filterOption[index]);
|
||||
setSelectedFilter(filterOption[index]);
|
||||
setAnchorEl(null);
|
||||
}
|
||||
|
||||
const handleCloseFilterBox = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<Menu
|
||||
id="simple-menu"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleCloseFilterBox}
|
||||
>
|
||||
{filterOption.map((option, index) => (
|
||||
<ListItem key={index}
|
||||
onClick={(event) => handleChangeItem(event, index)}
|
||||
selected={option === selectedFilter}
|
||||
sx={{cursor: 'pointer', width: "100px", display: "flex", justifyContent: "center"}}>
|
||||
{columnFilterModeOptionFa[option]}
|
||||
</ListItem>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterOptionList;
|
||||
@@ -1,53 +0,0 @@
|
||||
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 (
|
||||
<>
|
||||
<FormControl sx={{ mt: 3 }}>
|
||||
<InputLabel>انتخاب</InputLabel>
|
||||
<Select
|
||||
id={id}
|
||||
value={selectedValue}
|
||||
onChange={handleChange}
|
||||
label="انتخاب"
|
||||
>
|
||||
{names.map((name) => (
|
||||
<MenuItem key={name} value={name}>
|
||||
{name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectBox;
|
||||
@@ -1,116 +0,0 @@
|
||||
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;
|
||||
|
||||
const selectedValues = typeof value === 'string' ? value.split(',').filter(item => item.trim() !== '') : value;
|
||||
|
||||
formik.setFieldValue(id, []);
|
||||
|
||||
if (selectedValues.length === 0) {
|
||||
setPersonName([]);
|
||||
formik.setFieldValue(id, []);
|
||||
} else {
|
||||
setPersonName(selectedValues);
|
||||
selectedValues.forEach((selectedValue, index) => {
|
||||
formik.setFieldValue(`${id}[${index}]`, selectedValue);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormControl sx={{width: '100%'}}>
|
||||
<InputLabel id={id}>انتخاب</InputLabel>
|
||||
<Select
|
||||
id={id}
|
||||
multiple
|
||||
value={personName}
|
||||
onChange={handleChange}
|
||||
input={<OutlinedInput label="Chip"/>}
|
||||
renderValue={(selected) => (
|
||||
<Box sx={{display: 'flex', flexWrap: 'wrap', gap: 0.5}}>
|
||||
{selected.map((value) => (
|
||||
<Chip key={value} label={value}/>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
MenuProps={MenuProps}
|
||||
sx={{
|
||||
"&.MuiSelect-select": {
|
||||
minWidth: 0,
|
||||
maxWidth: '100%',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{names.map((name) => (
|
||||
<MenuItem
|
||||
key={name}
|
||||
value={name}
|
||||
style={getStyles(name, personName, theme)}
|
||||
>
|
||||
{name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectBoxMultiple;
|
||||
@@ -1,46 +0,0 @@
|
||||
import {ClickAwayListener, List, ListItem, ListItemText, Paper, Popper} from '@mui/material';
|
||||
import {useState} from "react";
|
||||
|
||||
const columnFilterModeOptionFa = {
|
||||
equals: "برابر",
|
||||
notEquals: "نابرابر",
|
||||
contains: "شامل",
|
||||
lessThan: "کوچکتر",
|
||||
greaterThan: "بزرگتر",
|
||||
fuzzy: "فازی",
|
||||
}
|
||||
|
||||
function FilterBox({
|
||||
anchorEl, open = false, handleClose = () => {
|
||||
}, columnFilterModeOptions, onSelectFilter, defaultFilter
|
||||
}) {
|
||||
const [selectedFilter, setSelectedFilter] = useState(defaultFilter);
|
||||
|
||||
const id = open ? 'simple-popper' : undefined;
|
||||
const handleSelectFilter = (filterFn) => {
|
||||
setSelectedFilter(filterFn);
|
||||
onSelectFilter(filterFn);
|
||||
handleClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Popper id={id} open={open} anchorEl={anchorEl} placement="bottom" style={{zIndex: 1301}}>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<Paper>
|
||||
<List>
|
||||
{columnFilterModeOptions.map((option, index) => (
|
||||
<ListItem key={index} onClick={() => handleSelectFilter(option)}
|
||||
selected={option === selectedFilter} sx={{cursor: 'pointer'}}>
|
||||
<ListItemText primary={columnFilterModeOptionFa[option]}/>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Paper>
|
||||
</ClickAwayListener>
|
||||
</Popper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterBox;
|
||||
@@ -1,61 +0,0 @@
|
||||
import {IconButton, TextField} from '@mui/material';
|
||||
import ArtTrackIcon from '@mui/icons-material/ArtTrack';
|
||||
import {useEffect, useState} from "react";
|
||||
import FilterBox from "@/core/components/DataTable/filter/TextFieldWithFnBox/FilterBox";
|
||||
|
||||
const TextFieldWithFnBox = ({
|
||||
formik,
|
||||
id,
|
||||
header,
|
||||
defaultValue,
|
||||
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);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextField
|
||||
name={id}
|
||||
label={header}
|
||||
id={id}
|
||||
onChange={formik.handleChange}
|
||||
fullWidth
|
||||
value={formik.values[id]}
|
||||
margin="normal"
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<IconButton onClick={handleClick}>
|
||||
<ArtTrackIcon/>
|
||||
</IconButton>
|
||||
)
|
||||
}}
|
||||
sx={{
|
||||
position: 'relative'
|
||||
}}
|
||||
/>
|
||||
<FilterBox anchorEl={anchorEl} open={open} handleClose={handleClose}
|
||||
columnFilterModeOptions={columnFilterModeOptions} onSelectFilter={onSelectFilter}
|
||||
defaultFilter={defaultFilter}/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextFieldWithFnBox;
|
||||
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker";
|
||||
import {Typography} from "@mui/material";
|
||||
|
||||
function CustomDatePicker({column, filterParameters, defaultFilterTranslation, setFieldValue}) {
|
||||
return (
|
||||
<MuiDatePicker
|
||||
name={`${column.id}.value`}
|
||||
value={filterParameters.value}
|
||||
setFieldValue={setFieldValue}
|
||||
placeholder={column.header}
|
||||
helperText={
|
||||
<Typography variant="button" sx={{color: "#155175"}}>
|
||||
نوع فیلتر: {defaultFilterTranslation} (تاریخ)
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomDatePicker;
|
||||
@@ -0,0 +1,35 @@
|
||||
'use client'
|
||||
|
||||
import React from "react";
|
||||
import {Box, Typography} from "@mui/material";
|
||||
import MuiDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker";
|
||||
|
||||
function CustomDatePickerRange({column, filterParameters, defaultFilterTranslation, setFieldValue, errors, touched}) {
|
||||
return (
|
||||
<Box sx={{display: "flex", gap: 1}}>
|
||||
<MuiDatePicker
|
||||
name={`${column.id}.value[0]`}
|
||||
value={filterParameters.value[0]}
|
||||
setFieldValue={setFieldValue}
|
||||
maxDate={filterParameters.value[1]}
|
||||
placeholder={`از تاریخ`}
|
||||
helperText={
|
||||
<Typography variant="button" sx={{color: "#155175"}}>
|
||||
نوع فیلتر: {defaultFilterTranslation} (تاریخ)
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
<MuiDatePicker
|
||||
name={`${column.id}.value[1]`}
|
||||
value={filterParameters.value[1]}
|
||||
setFieldValue={setFieldValue}
|
||||
minDate={filterParameters.value[0]}
|
||||
placeholder={`تا تاریخ`}
|
||||
helperText={errors?.[`${column.id}`]?.value ? errors?.[`${column.id}`]?.value : null}
|
||||
error={Boolean(errors?.[`${column.id}`]?.value)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomDatePickerRange;
|
||||
@@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import {LocalizationProvider, MobileDatePicker} from "@mui/x-date-pickers";
|
||||
import {AdapterDateFnsJalali} from "@mui/x-date-pickers/AdapterDateFnsJalali";
|
||||
import {faIR} from "@mui/x-date-pickers/locales";
|
||||
import {Box, FormHelperText, IconButton, InputAdornment} from "@mui/material";
|
||||
import ClearIcon from "@mui/icons-material/Clear";
|
||||
import moment from "jalali-moment";
|
||||
|
||||
function MuiDatePicker({value, setFieldValue, name, minDate, maxDate, helperText, placeholder, error}) {
|
||||
|
||||
// console.log("error", error);
|
||||
//
|
||||
// console.log("helperText", helperText)
|
||||
|
||||
return (
|
||||
<Box sx={{display: "flex", flexDirection: "column", my: 1}}>
|
||||
<LocalizationProvider dateAdapter={AdapterDateFnsJalali}
|
||||
localeText={faIR.components.MuiLocalizationProvider.defaultProps.localeText}
|
||||
>
|
||||
<MobileDatePicker
|
||||
value={value ? new Date(value) : null}
|
||||
sx={{width: "100%"}}
|
||||
id={name}
|
||||
name={name}
|
||||
aria-describedby="component-helper-text"
|
||||
onChange={(value) => {
|
||||
const date = new Date(value);
|
||||
const formattedDate = moment(date)
|
||||
.locale("en")
|
||||
.format("YYYY-MM-DD");
|
||||
setFieldValue(name, formattedDate);
|
||||
}}
|
||||
minDate={minDate ? new Date(minDate) : null}
|
||||
maxDate={maxDate ? new Date(maxDate) : null}
|
||||
slotProps={{
|
||||
textField: {
|
||||
error: error,
|
||||
placeholder: placeholder,
|
||||
InputProps: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
setFieldValue(name, "");
|
||||
}}
|
||||
sx={{
|
||||
color: "#bfbfbf",
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(189, 189, 189, 0.1)',
|
||||
color: "#363434"
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ClearIcon/>
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<FormHelperText id="component-helper-text" sx={{ml: 2, color: error ? '#d32f2f' : "unset"}}>
|
||||
{helperText ? helperText : ""}
|
||||
</FormHelperText>
|
||||
</LocalizationProvider>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default MuiDatePicker;
|
||||
@@ -0,0 +1,40 @@
|
||||
'use client'
|
||||
|
||||
import {FormControl, FormHelperText, InputLabel, MenuItem, OutlinedInput, Select, Typography} from "@mui/material";
|
||||
|
||||
function CustomSelect({column, filterParameters, defaultFilterTranslation, setFieldValue}) {
|
||||
const columnSelectOption = column.columnSelectOption;
|
||||
|
||||
return (
|
||||
<FormControl fullWidth sx={{marginY: 1}}>
|
||||
<InputLabel id={`label${column.id}`}>{column.header}</InputLabel>
|
||||
<Select
|
||||
labelId={`label${column.id}`}
|
||||
id={column.id}
|
||||
name={`${column.id}.value`}
|
||||
value={filterParameters.value}
|
||||
input={<OutlinedInput label={column.header}/>}
|
||||
onChange={(e) => {
|
||||
setFieldValue(`${column.id}.value`, e.target.value)
|
||||
}}
|
||||
>
|
||||
<MenuItem value="">{column.header}</MenuItem>
|
||||
{columnSelectOption.map((option) => (
|
||||
<MenuItem
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
<FormHelperText>
|
||||
<Typography variant="button" sx={{color: "#155175"}}>
|
||||
نوع فیلتر: {defaultFilterTranslation}
|
||||
</Typography>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomSelect;
|
||||
@@ -0,0 +1,62 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
Box,
|
||||
Chip,
|
||||
FormControl,
|
||||
FormHelperText,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
OutlinedInput,
|
||||
Select,
|
||||
Typography
|
||||
} from "@mui/material";
|
||||
|
||||
function CustomSelectMultiple({column, filterParameters, defaultFilterTranslation, setFieldValue}) {
|
||||
const columnSelectOption = column.columnSelectOption;
|
||||
|
||||
const getLabelForValue = (value) => {
|
||||
const option = columnSelectOption.find(opt => opt.value === value);
|
||||
return option ? option.label : value;
|
||||
};
|
||||
|
||||
return (
|
||||
<FormControl fullWidth sx={{marginY: 1}}>
|
||||
<InputLabel htmlFor="uncontrolled-native"
|
||||
id={`label${column.id}`}>{column.header}</InputLabel>
|
||||
<Select
|
||||
labelId={`label${column.id}`}
|
||||
id={column.id}
|
||||
value={filterParameters.value}
|
||||
multiple
|
||||
onChange={(e) => {
|
||||
setFieldValue(`${column.id}.value`, e.target.value)
|
||||
}}
|
||||
renderValue={(selected) => (
|
||||
<Box sx={{display: 'flex', flexWrap: 'wrap', gap: 0.5}}>
|
||||
{selected.map((value) => (
|
||||
<Chip key={value} label={getLabelForValue(value)}/>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
input={<OutlinedInput label={column.header}/>}
|
||||
>
|
||||
{columnSelectOption.map((option) => (
|
||||
<MenuItem
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
<FormHelperText>
|
||||
<Typography variant="button" sx={{color: "#155175"}}>
|
||||
نوع فیلتر: {defaultFilterTranslation}
|
||||
</Typography>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomSelectMultiple;
|
||||
@@ -0,0 +1,39 @@
|
||||
import {InputAdornment, TextField, Typography} from "@mui/material";
|
||||
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||
|
||||
function CustomTextField({
|
||||
column,
|
||||
filterParameters,
|
||||
defaultFilterTranslation,
|
||||
handleOpenFilterBox,
|
||||
handleChange,
|
||||
handleBlur
|
||||
}) {
|
||||
|
||||
return (
|
||||
<TextField
|
||||
id={column.id}
|
||||
name={column.id}
|
||||
label={column.header}
|
||||
value={filterParameters.value}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
fullWidth
|
||||
helperText={<Typography variant="button" sx={{color: "#155175"}}>
|
||||
نوع فیلتر: {defaultFilterTranslation}
|
||||
</Typography>}
|
||||
variant="outlined"
|
||||
size="normal"
|
||||
sx={{marginY: 1}}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end" sx={{cursor: "pointer"}} onClick={handleOpenFilterBox}>
|
||||
<FilterListIcon/>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomTextField;
|
||||
@@ -0,0 +1,56 @@
|
||||
import {Box, InputAdornment, TextField, Typography} from "@mui/material";
|
||||
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||
|
||||
function CustomTextFieldRange({
|
||||
column,
|
||||
filterParameters,
|
||||
defaultFilterTranslation,
|
||||
handleOpenFilterBox,
|
||||
handleChange,
|
||||
handleBlur,
|
||||
errors,
|
||||
touched
|
||||
}) {
|
||||
return (
|
||||
<Box sx={{display: "flex"}}>
|
||||
<TextField
|
||||
id={`${column.id}.value[0]`}
|
||||
name={`${column.id}.value[0]`}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
label={<Typography>از {column.header}</Typography>}
|
||||
value={filterParameters.value[0]}
|
||||
fullWidth
|
||||
error={touched?.[`${column.id}`]?.value && Boolean(errors?.[`${column.id}`]?.value)}
|
||||
helperText={<Typography variant="button" sx={{color: "#155175"}}>نوع
|
||||
فیلتر: {defaultFilterTranslation}</Typography>}
|
||||
variant="outlined"
|
||||
size="normal"
|
||||
sx={{marginY: 1, marginRight: 1}}
|
||||
/>
|
||||
<TextField
|
||||
id={`${column.id}.value[1]`}
|
||||
name={`${column.id}.value[1]`}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
error={touched?.[`${column.id}`]?.value && Boolean(errors?.[`${column.id}`]?.value)}
|
||||
helperText={touched?.[`${column.id}`]?.value ? errors?.[`${column.id}`]?.value : null}
|
||||
label={<Typography>تا {column.header}</Typography>}
|
||||
value={filterParameters.value[1]}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
size="normal"
|
||||
sx={{marginY: 1}}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end" sx={{cursor: "pointer"}} onClick={handleOpenFilterBox}>
|
||||
<FilterListIcon/>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CustomTextFieldRange;
|
||||
19
src/core/components/DataTable/filter/index.jsx
Normal file
19
src/core/components/DataTable/filter/index.jsx
Normal file
@@ -0,0 +1,19 @@
|
||||
'use client'
|
||||
|
||||
import {useState} from "react";
|
||||
import FilterButton from "@/core/components/DataTable/filter/FilterButton";
|
||||
import FilterBody from "@/core/components/DataTable/filter/FilterBody";
|
||||
|
||||
function FilterColumn({columns, user_id, page_name, table_name}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FilterButton drawerState={open} setDrawerState={setOpen}/>
|
||||
<FilterBody columns={columns} drawerState={open} setDrawerState={setOpen} user_id={user_id}
|
||||
page_name={page_name} table_name={table_name}/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default FilterColumn;
|
||||
@@ -1,142 +1,12 @@
|
||||
import {useMaterialReactTable} from "material-react-table";
|
||||
import DataTable_Main from "./Main";
|
||||
import {FA_DATATABLE_LOCALIZATION} from "./localization/fa/datatable";
|
||||
import useTableSetting from "@/lib/hooks/useTableSetting";
|
||||
import {flattenArrayOfObjects} from "@/core/utils/flattenArrayOfObjects";
|
||||
import Toolbar from "@/components/home/Toolbar";
|
||||
|
||||
const data = [
|
||||
{
|
||||
name: {
|
||||
firstName: 'امین',
|
||||
lastName: 'قاسمپور',
|
||||
},
|
||||
address: 'فلان جا',
|
||||
city: 'تهران',
|
||||
state: 'بررسی شده',
|
||||
price: '20',
|
||||
updated_at: '2024/05/02',
|
||||
company: 'همراه اول',
|
||||
area: 'منطقه 5',
|
||||
},
|
||||
{
|
||||
name: {
|
||||
firstName: 'امیر حسین',
|
||||
lastName: 'محمودی',
|
||||
},
|
||||
address: 'فلان جا',
|
||||
city: 'تهران',
|
||||
state: 'تکمیل شده',
|
||||
price: '27',
|
||||
updated_at: '2024/05/02',
|
||||
company: 'وایتل',
|
||||
area: 'منطقه 27',
|
||||
},
|
||||
{
|
||||
name: {
|
||||
firstName: 'یاسمن',
|
||||
lastName: 'علی اکبری',
|
||||
},
|
||||
address: 'فلان جا',
|
||||
city: 'شیراز',
|
||||
state: 'بررسی شده',
|
||||
price: '32',
|
||||
updated_at: '2024/05/02',
|
||||
company: 'ایرانسل',
|
||||
area: 'منطقه 23',
|
||||
},
|
||||
{
|
||||
name: {
|
||||
firstName: 'حمیدرضا',
|
||||
lastName: 'رنجبرپور',
|
||||
},
|
||||
address: 'فلان جا',
|
||||
city: 'اصفهان',
|
||||
state: 'بررسی شده',
|
||||
price: '45',
|
||||
updated_at: '2024/05/02',
|
||||
company: 'ایرانول',
|
||||
area: 'منطقه 21',
|
||||
},
|
||||
{
|
||||
name: {
|
||||
firstName: 'محمد',
|
||||
lastName: 'جلالی',
|
||||
},
|
||||
address: 'فلان جا',
|
||||
city: 'رشت',
|
||||
state: 'درحال بررسی',
|
||||
price: '8',
|
||||
updated_at: '2024/05/02',
|
||||
company: 'فیلیمو',
|
||||
area: 'منطقه 8',
|
||||
},
|
||||
];
|
||||
import DataTableProvider from "@/lib/contexts/DataTable";
|
||||
|
||||
const DataTable = (props) => {
|
||||
const {userId, pageName, tableName, columns, initialStateProps} = props
|
||||
const {settingStore, hideAction, sortAction} = useTableSetting();
|
||||
const flatColumns = flattenArrayOfObjects(columns, 'columns')
|
||||
|
||||
const onColumnVisibilityChange = (event) => {
|
||||
const settingValue = event();
|
||||
hideAction(userId, pageName, tableName, settingValue, flatColumns);
|
||||
};
|
||||
const onSortingChange = (event) => {
|
||||
const settingValue = event(settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []);
|
||||
sortAction(userId, pageName, tableName, settingValue, flatColumns);
|
||||
}
|
||||
|
||||
|
||||
const table = useMaterialReactTable({
|
||||
localization: FA_DATATABLE_LOCALIZATION,
|
||||
columns,
|
||||
data: data,
|
||||
// manualSorting: true,
|
||||
renderTopToolbarCustomActions: ({table}) => (<>
|
||||
<Toolbar
|
||||
columns={flatColumns}
|
||||
/>
|
||||
</>),
|
||||
initialState: {
|
||||
density: 'compact',
|
||||
columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
|
||||
sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || [],
|
||||
...initialStateProps
|
||||
},
|
||||
state: {
|
||||
columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
|
||||
sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []
|
||||
},
|
||||
muiTableHeadProps: {
|
||||
sx: {
|
||||
borderTop: "1px solid #e1e1e1",
|
||||
borderBottom: "2px solid #e1e1e1",
|
||||
}
|
||||
},
|
||||
muiTableHeadCellProps: {
|
||||
sx: {
|
||||
color: "primary.main",
|
||||
borderLeft: "1px solid #e1e1e1",
|
||||
"&:first-of-type": {
|
||||
borderLeft: "unset"
|
||||
}
|
||||
},
|
||||
},
|
||||
muiTableBodyCellProps: {
|
||||
sx: {
|
||||
borderLeft: "1px solid #e1e1e1",
|
||||
"&:first-of-type": {
|
||||
borderLeft: "unset"
|
||||
}
|
||||
},
|
||||
},
|
||||
onColumnVisibilityChange: onColumnVisibilityChange,
|
||||
onSortingChange: onSortingChange,
|
||||
...props
|
||||
})
|
||||
|
||||
return <DataTable_Main table={table} columns={flatColumns}/>;
|
||||
return (
|
||||
<DataTableProvider>
|
||||
<DataTable_Main {...props}/>
|
||||
</DataTableProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default DataTable;
|
||||
export default DataTable;
|
||||
@@ -6,9 +6,9 @@ import {useState} from "react";
|
||||
|
||||
function RefactorTable() {
|
||||
const {refactorAction} = useTableSetting();
|
||||
const [userId, setUserId] = useState(2);
|
||||
const [pageName, setPageName] = useState('loan');
|
||||
const [tableName, setTableName] = useState('loan_table');
|
||||
const [userId, setUserId] = useState(0);
|
||||
const [pageName, setPageName] = useState('testPage');
|
||||
const [tableName, setTableName] = useState('testTable');
|
||||
|
||||
const refactor = () => {
|
||||
refactorAction(userId, pageName, tableName);
|
||||
|
||||
@@ -65,7 +65,10 @@ const DataTable_Paper = ({table, ...rest}) => {
|
||||
>
|
||||
{enableTopToolbar &&
|
||||
(parseFromValuesOrFunc(renderTopToolbar, {table}) ?? (
|
||||
<DataTable_TopToolbar table={table} columns={paperProps.columns}/>
|
||||
<DataTable_TopToolbar table={table} columns={paperProps.columns} table_url={paperProps.table_url}
|
||||
user_id={paperProps.user_id}
|
||||
page_name={paperProps.page_name}
|
||||
table_name={paperProps.table_name}/>
|
||||
))}
|
||||
<DataTable_TableContainer table={table}/>
|
||||
{enableBottomToolbar &&
|
||||
|
||||
@@ -3,10 +3,10 @@ import {Box, useMediaQuery} from "@mui/material";
|
||||
import DataTable_LinearProgressBar from "./LinearProgressBar";
|
||||
import DataTable_TablePagination from "./TablePagination";
|
||||
import DataTable_ToolbarInternalButtons from "./ToolbarInternalButtons";
|
||||
import FilterColumn from "@/core/components/DataTable/filter/FilterColumn";
|
||||
import FilterColumn from "@/core/components/DataTable/filter";
|
||||
import RefactorTable from "@/core/components/DataTable/refactor/RefactorTable";
|
||||
|
||||
const DataTable_TopToolbar = ({table, columns}) => {
|
||||
const DataTable_TopToolbar = ({table, columns, table_url, user_id, page_name, table_name}) => {
|
||||
const {
|
||||
getState,
|
||||
options: {
|
||||
@@ -73,7 +73,8 @@ const DataTable_TopToolbar = ({table, columns}) => {
|
||||
}}
|
||||
>
|
||||
<RefactorTable/>
|
||||
<FilterColumn columns={columns}/>
|
||||
<FilterColumn columns={columns} table_url={table_url} user_id={user_id} page_name={page_name}
|
||||
table_name={table_name}/>
|
||||
<DataTable_ToolbarInternalButtons table={table}/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
47
src/core/components/NotificationDesign/AskForKeep.jsx
Normal file
47
src/core/components/NotificationDesign/AskForKeep.jsx
Normal file
@@ -0,0 +1,47 @@
|
||||
'use client'
|
||||
|
||||
import SaveIcon from '@mui/icons-material/Save';
|
||||
import {Box, Button, Typography} from "@mui/material";
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
import {toast} from "react-toastify";
|
||||
|
||||
function AskForKeep({onSaveFilter}) {
|
||||
|
||||
const handleDismiss = () => {
|
||||
toast.dismiss({containerId: "filtering"});
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{width: "100%"}}>
|
||||
<Box sx={{display: "flex", alignItems: "center", justifyContent: "space-between", mb: 1}}>
|
||||
<Typography color="#155175" variant="subtitle1" sx={{fontWeight: "500"}}>
|
||||
ذخیره سازی اطلاعات
|
||||
</Typography>
|
||||
<SaveIcon sx={{color: "#155175"}}/>
|
||||
</Box>
|
||||
<Box sx={{display: "flex", flexDirection: "column"}}>
|
||||
<Typography variant="caption">
|
||||
آیا مایل به ذخیره فیلتر های اعمال شده برای دفعات بعد هستید؟
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{display: "flex", gap: 1, mt: 1}}>
|
||||
<Button variant="contained" onClick={handleDismiss} sx={{
|
||||
backgroundColor: "#792626",
|
||||
':hover': {backgroundColor: "#792626"},
|
||||
boxShadow: "rgba(0, 0, 0, 0.23) 0px 6px 6px, rgba(0, 0, 0, 0.19) 10px 0px 20px",
|
||||
}}>رد
|
||||
کردن</Button>
|
||||
<Button variant="contained" component="label" endIcon={<DownloadIcon/>}
|
||||
onClick={onSaveFilter}
|
||||
sx={{
|
||||
backgroundColor: "#155175",
|
||||
':hover': {backgroundColor: "#155175"},
|
||||
boxShadow: "rgba(0, 0, 0, 0.23) 0px 6px 6px, rgba(0, 0, 0, 0.19) 10px 0px 20px",
|
||||
}}>ذخیره
|
||||
فیلتر</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default AskForKeep;
|
||||
@@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import { createTheme } from "@mui/material";
|
||||
import {createTheme} from "@mui/material";
|
||||
|
||||
const theme = createTheme({
|
||||
direction: 'rtl',
|
||||
|
||||
15
src/lib/contexts/DataTable.jsx
Normal file
15
src/lib/contexts/DataTable.jsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import {createContext, useState} from "react";
|
||||
|
||||
export const DataTableContext = createContext();
|
||||
|
||||
const DataTableProvider = ({children}) => {
|
||||
const [filterData, setFilterData] = useState({});
|
||||
|
||||
return (
|
||||
<DataTableContext.Provider value={{filterData, setFilterData}}>
|
||||
{children}
|
||||
</DataTableContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default DataTableProvider;
|
||||
9
src/lib/hooks/useDataTable.jsx
Normal file
9
src/lib/hooks/useDataTable.jsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import {useContext} from "react";
|
||||
import {DataTableContext} from "@/lib/contexts/DataTable";
|
||||
|
||||
const useTableSetting = () => {
|
||||
const {filterData, setFilterData} = useContext(DataTableContext);
|
||||
return {filterData, setFilterData};
|
||||
};
|
||||
|
||||
export default useTableSetting;
|
||||
Reference in New Issue
Block a user