working on datepickerrange problems

This commit is contained in:
2024-06-09 16:43:53 +03:30
parent 474e6f3e8d
commit 3f896ef5e9
13 changed files with 264 additions and 132 deletions

View File

@@ -22,12 +22,13 @@
"formik": "^2.4.5", "formik": "^2.4.5",
"jalali-moment": "^3.3.11", "jalali-moment": "^3.3.11",
"material-react-table": "^2.11.2", "material-react-table": "^2.11.2",
"next": "14.1.0", "next": "^14.2.3",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
"sass": "^1.75.0", "sass": "^1.75.0",
"stylis": "^4.3.1", "stylis": "^4.3.1",
"stylis-plugin-rtl": "^2.1.1" "stylis-plugin-rtl": "^2.1.1",
"yup": "^1.4.0"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^8", "eslint": "^8",

View File

@@ -94,13 +94,21 @@ function HomeComponent() {
"between" "between"
], ],
}, },
{
accessorKey: 'created_at',
header: 'تاریخ ساخت',
id: "created_at",
enableColumnFilter: true,
datatype: "date",
filterFn: "between",
},
{ {
accessorKey: 'updated_at', accessorKey: 'updated_at',
header: 'تاریخ بروزرسانی', header: 'تاریخ بروزرسانی',
id: "updated_at", id: "updated_at",
enableColumnFilter: true, enableColumnFilter: true,
datatype: "date", datatype: "date",
filterFn: "between", filterFn: "equals",
}, },
{ {
accessorKey: 'company', accessorKey: 'company',
@@ -120,7 +128,7 @@ function HomeComponent() {
header: 'منطقه', header: 'منطقه',
id: "area", id: "area",
enableColumnFilter: true, enableColumnFilter: true,
datatype: "numeric", datatype: "array",
filterFn: "equals", filterFn: "equals",
columnSelectOption: [ columnSelectOption: [
{value: 1, label: "وایتل"}, {value: 1, label: "وایتل"},

View File

@@ -6,10 +6,10 @@ import useTableSetting from "@/lib/hooks/useTableSetting";
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import FilterBodyField from "@/core/components/DataTable/filter/FilterBodyField"; import FilterBodyField from "@/core/components/DataTable/filter/FilterBodyField";
import FilterHeader from "@/core/components/DataTable/filter/FilterHeader"; import FilterHeader from "@/core/components/DataTable/filter/FilterHeader";
import * as Yup from "yup";
const ScrollBox = styled(Box)({ const ScrollBox = styled(Box)({
flexGrow: 1, flexGrow: 1,
padding: '16px',
overflowY: 'scroll', overflowY: 'scroll',
maxWidth: "450px", maxWidth: "450px",
'::-webkit-scrollbar': { '::-webkit-scrollbar': {
@@ -40,8 +40,10 @@ function FilterBody({columns, drawerState, setDrawerState}) {
const values = columns.reduce((acc, column) => { const values = columns.reduce((acc, column) => {
if (!column.enableColumnFilter) return acc; if (!column.enableColumnFilter) return acc;
const filter = settingStore?.[userId]?.[pageName]?.[tableName]?.['filters']?.find((filter) => filter.id === column.id); const filter = settingStore?.[userId]?.[pageName]?.[tableName]?.['filters']?.find((filter) => filter.id === column.id);
if (column.datatype === 'select2') { if (column.datatype === 'array') {
acc[column.id] = filter?.value || []; acc[column.id] = filter?.value || [];
} else if (column._filterFn === "between") {
acc[column.id] = filter?.value || ["", ""];
} else { } else {
acc[column.id] = filter?.value || ''; acc[column.id] = filter?.value || '';
} }
@@ -50,6 +52,33 @@ function FilterBody({columns, drawerState, setDrawerState}) {
setInitialValues(values); setInitialValues(values);
}, [columns, settingStore, userId, pageName, tableName]); }, [columns, settingStore, userId, pageName, tableName]);
console.log("initialValues", initialValues)
const validationSchema = Yup.object().shape(
columns.reduce((acc, column) => {
if (column._filterFn === "between") {
acc[column.id] = Yup.array().of(Yup.string()).test({
test(value, ctx) {
const [start, end] = value || ["", ""];
const startInt = parseInt(start, 10);
const endInt = parseInt(end, 10);
if (start && end && endInt <= startInt) {
return ctx.createError({
message: `مقدار وارده باید بیشتر از (${start}) باشد`
});
} else if (start && !end) {
return ctx.createError({
message: "این بخش را پر نمایید"
});
}
return true;
}
});
}
return acc;
}, {})
);
const handleSubmit = (values) => { const handleSubmit = (values) => {
console.log("Form values:", values); console.log("Form values:", values);
}; };
@@ -58,44 +87,50 @@ function FilterBody({columns, drawerState, setDrawerState}) {
<Drawer open={drawerState} onClose={() => setDrawerState(false)} <Drawer open={drawerState} onClose={() => setDrawerState(false)}
sx={{overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%"}}> sx={{overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%"}}>
<FilterHeader setDrawerState={setDrawerState}/> <FilterHeader setDrawerState={setDrawerState}/>
<ScrollBox> {Object.keys(initialValues).length > 0 && (
{Object.keys(initialValues).length > 0 && ( <ScrollBox>
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
onSubmit={handleSubmit} onSubmit={handleSubmit}
validationSchema={validationSchema}
> >
{({values, handleChange, handleBlur}) => ( {({values, handleChange, handleBlur, setFieldValue, errors, touched}) => (
<Form> <Form>
{columns.map((column) => ( <Box sx={{px: 1, py: 2}}>
column.enableColumnFilter && ( {columns.map((column) => (
<FilterBodyField column.enableColumnFilter && (
key={column.id} <FilterBodyField
column={column} key={column.id}
value={values[column.id]} column={column}
onChange={handleChange} value={values[column.id]}
onBlur={handleBlur} handleChange={handleChange}
/> handleBlur={handleBlur}
) setFieldValue={setFieldValue}
))} errors={errors}
touched={touched}
/>
)
))}
</Box>
<Box sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
pb: 2
}}>
<Button variant="contained" size="large" type="submit"
sx={{
boxShadow: "rgba(0, 0, 0, 0.23) 0px 6px 6px, rgba(0, 0, 0, 0.19) 10px 0px 20px",
backgroundColor: "#16534a", ':hover': {
backgroundColor: "#16534a",
}, width: "50%"
}}>اعمال فیلتر</Button>
</Box>
</Form> </Form>
)} )}
</Formik> </Formik>
)} </ScrollBox>
</ScrollBox> )}
<Box sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
boxShadow: "rgba(0, 0, 0, 0.23) 0px 6px 6px, rgba(0, 0, 0, 0.19) 10px 0px 20px",
py: 1,
}}>
<Button variant="contained" size="large" onClick={handleSubmit}
sx={{
backgroundColor: "#16534a", ':hover': {
backgroundColor: "#16534a",
}, width: "50%"
}}>اعمال فیلتر</Button>
</Box>
</Drawer> </Drawer>
); );
} }

View File

@@ -6,9 +6,9 @@ import useTableSetting from "@/lib/hooks/useTableSetting";
import CustomSelect from "@/core/components/DataTable/filter/fieldsType/CustomSelect"; import CustomSelect from "@/core/components/DataTable/filter/fieldsType/CustomSelect";
import CustomTextField from "@/core/components/DataTable/filter/fieldsType/CustomTextField"; import CustomTextField from "@/core/components/DataTable/filter/fieldsType/CustomTextField";
import CustomTextFieldRange from "@/core/components/DataTable/filter/fieldsType/CustomTextFieldRange"; import CustomTextFieldRange from "@/core/components/DataTable/filter/fieldsType/CustomTextFieldRange";
import CustomDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDatePicker"; import CustomDatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePicker";
import CustomSelectMultiple from "@/core/components/DataTable/filter/fieldsType/CustomSelectMultiple"; import CustomSelectMultiple from "@/core/components/DataTable/filter/fieldsType/CustomSelectMultiple";
import CustomDatePickerRange from "@/core/components/DataTable/filter/fieldsType/CustomDatePickerRange"; import CustomDatePickerRange from "@/core/components/DataTable/filter/fieldsType/CustomDate/CustomDatePickerRange";
const columnFilterModeOptionFa = { const columnFilterModeOptionFa = {
equals: "برابر", equals: "برابر",
@@ -20,7 +20,7 @@ const columnFilterModeOptionFa = {
between: "مابین" between: "مابین"
} }
function FilterBodyField({column, value}) { function FilterBodyField({column, value, handleChange, handleBlur, setFieldValue, errors, touched}) {
const [anchorEl, setAnchorEl] = useState(null); const [anchorEl, setAnchorEl] = useState(null);
const [userId, setUserId] = useState(0); const [userId, setUserId] = useState(0);
const [pageName, setPageName] = useState('testPage'); const [pageName, setPageName] = useState('testPage');
@@ -28,7 +28,6 @@ function FilterBodyField({column, value}) {
const {settingStore} = useTableSetting(); const {settingStore} = useTableSetting();
const defaultFilter = (settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.fn || column._filterFn; const defaultFilter = (settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.fn || column._filterFn;
const defaultFilterTranslation = columnFilterModeOptionFa[defaultFilter] || defaultFilter; const defaultFilterTranslation = columnFilterModeOptionFa[defaultFilter] || defaultFilter;
const handleOpenFilterBox = (event) => { const handleOpenFilterBox = (event) => {
setAnchorEl(event.currentTarget); setAnchorEl(event.currentTarget);
}; };
@@ -43,11 +42,16 @@ function FilterBodyField({column, value}) {
value={value} value={value}
defaultFilterTranslation={defaultFilterTranslation} defaultFilterTranslation={defaultFilterTranslation}
handleOpenFilterBox={handleOpenFilterBox} handleOpenFilterBox={handleOpenFilterBox}
handleBlur={handleBlur}
handleChange={handleChange}
errors={errors}
touched={touched}
/> />
) : column.datatype === "numeric" && column._filterFn === "equals" && Array.isArray(column.columnSelectOption) ? ( ) : column.datatype === "numeric" && column._filterFn === "equals" && Array.isArray(column.columnSelectOption) ? (
<CustomSelect <CustomSelect
column={column} column={column}
value={value} value={value}
setFieldValue={setFieldValue}
defaultFilterTranslation={defaultFilterTranslation} defaultFilterTranslation={defaultFilterTranslation}
handleOpenFilterBox={handleOpenFilterBox} handleOpenFilterBox={handleOpenFilterBox}
/> />
@@ -55,6 +59,7 @@ function FilterBodyField({column, value}) {
<CustomDatePicker <CustomDatePicker
column={column} column={column}
value={value} value={value}
setFieldValue={setFieldValue}
defaultFilterTranslation={defaultFilterTranslation} defaultFilterTranslation={defaultFilterTranslation}
handleOpenFilterBox={handleOpenFilterBox} handleOpenFilterBox={handleOpenFilterBox}
/> />
@@ -69,6 +74,7 @@ function FilterBodyField({column, value}) {
<CustomSelectMultiple <CustomSelectMultiple
column={column} column={column}
value={value} value={value}
setFieldValue={setFieldValue}
defaultFilterTranslation={defaultFilterTranslation} defaultFilterTranslation={defaultFilterTranslation}
handleOpenFilterBox={handleOpenFilterBox} handleOpenFilterBox={handleOpenFilterBox}
/> />
@@ -78,6 +84,8 @@ function FilterBodyField({column, value}) {
value={value} value={value}
defaultFilterTranslation={defaultFilterTranslation} defaultFilterTranslation={defaultFilterTranslation}
handleOpenFilterBox={handleOpenFilterBox} handleOpenFilterBox={handleOpenFilterBox}
handleBlur={handleBlur}
handleChange={handleChange}
/> />
)} )}
{Array.isArray(column.columnFilterModeOptions) && anchorEl !== null && ( {Array.isArray(column.columnFilterModeOptions) && anchorEl !== null && (

View File

@@ -0,0 +1,15 @@
import React from 'react';
import DatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/DatePicker";
function CustomDatePicker({column, value, defaultFilterTranslation, setFieldValue}) {
return (
<DatePicker
column={column}
value={value}
defaultFilterTranslation={defaultFilterTranslation}
setFieldValue={setFieldValue}
/>
);
}
export default CustomDatePicker;

View File

@@ -0,0 +1,25 @@
'use client'
import DatePicker from "@/core/components/DataTable/filter/fieldsType/CustomDate/DatePicker";
import React from "react";
function CustomDatePickerRange({column, value, defaultFilterTranslation, setFieldValue}) {
return (
<>
<DatePicker
column={column}
value={value}
defaultFilterTranslation={defaultFilterTranslation}
setFieldValue={setFieldValue}
/>
<DatePicker
column={column}
value={value}
defaultFilterTranslation={defaultFilterTranslation}
setFieldValue={setFieldValue}
/>
</>
);
}
export default CustomDatePickerRange;

View File

@@ -0,0 +1,66 @@
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 {IconButton, InputAdornment, Typography} from "@mui/material";
import ClearIcon from "@mui/icons-material/Clear";
import moment from "jalali-moment";
function DatePicker({column, value, defaultFilterTranslation, setFieldValue}) {
return (
<LocalizationProvider
dateAdapter={AdapterDateFnsJalali}
localeText={
faIR.components.MuiLocalizationProvider.defaultProps.localeText
}
>
<MobileDatePicker
value={value ? new Date(value) : null}
sx={{my: 1, width: "100%"}}
id={column.id}
name={column.id}
onChange={(value) => {
const date = new Date(value);
const formattedDate = moment(date)
.locale("en")
.format("YYYY-MM-DD");
setFieldValue(column.id, formattedDate);
}}
slotProps={{
textField: {
placeholder: "تاریخ خود را وارد کنید",
helperText: (
<Typography variant="button" sx={{color: "#16534a"}}>
نوع فیلتر: {defaultFilterTranslation} (تاریخ)
</Typography>
),
InputProps: {
endAdornment: (
<InputAdornment position="end">
<IconButton
size="small"
onClick={(event) => {
event.stopPropagation();
setFieldValue(column.id, "");
}}
sx={{
color: "#bfbfbf",
'&:hover': {
backgroundColor: 'rgba(189, 189, 189, 0.1)',
color: "#363434"
},
}}
>
<ClearIcon/>
</IconButton>
</InputAdornment>
),
},
},
}}
/>
</LocalizationProvider>
);
}
export default DatePicker;

View File

@@ -1,30 +0,0 @@
'use client'
import {InputAdornment, TextField, Typography} from "@mui/material";
import FilterListIcon from '@mui/icons-material/FilterList';
function CustomDatePicker({column, value, defaultFilterTranslation, handleOpenFilterBox}) {
return (
<TextField
id={column.id}
label={column.header}
value={value}
fullWidth
helperText={<Typography variant="button" sx={{color: "#16534a"}}>
نوع فیلتر: {defaultFilterTranslation}
</Typography>}
variant="outlined"
size="normal"
sx={{marginY: 1}}
InputProps={{
endAdornment: (
<InputAdornment position="end" sx={{cursor: "pointer"}} onClick={handleOpenFilterBox}>
<FilterListIcon/>
</InputAdornment>
),
}}
/>
);
}
export default CustomDatePicker;

View File

@@ -1,30 +0,0 @@
'use client'
import {InputAdornment, TextField, Typography} from "@mui/material";
import FilterListIcon from '@mui/icons-material/FilterList';
function CustomDatePickerRange({column, value, defaultFilterTranslation, handleOpenFilterBox}) {
return (
<TextField
id={column.id}
label={column.header}
value={value}
fullWidth
helperText={<Typography variant="button" sx={{color: "#16534a"}}>
نوع فیلتر: {defaultFilterTranslation}
</Typography>}
variant="outlined"
size="normal"
sx={{marginY: 1}}
InputProps={{
endAdornment: (
<InputAdornment position="end" sx={{cursor: "pointer"}} onClick={handleOpenFilterBox}>
<FilterListIcon/>
</InputAdornment>
),
}}
/>
);
}
export default CustomDatePickerRange;

View File

@@ -1,27 +1,22 @@
'use client' 'use client'
import {FormControl, FormHelperText, InputLabel, MenuItem, OutlinedInput, Select, Typography} from "@mui/material"; import {FormControl, FormHelperText, InputLabel, MenuItem, OutlinedInput, Select, Typography} from "@mui/material";
import {useState} from "react";
function CustomSelect({column, value, defaultFilterTranslation}) { function CustomSelect({column, value, defaultFilterTranslation, setFieldValue}) {
const [fieldValue, setFieldValue] = useState(value);
const columnSelectOption = column.columnSelectOption; const columnSelectOption = column.columnSelectOption;
const handleChange = (event) => {
setFieldValue(event.target.value);
};
return ( return (
<FormControl fullWidth sx={{marginY: 1}}> <FormControl fullWidth sx={{marginY: 1}}>
<InputLabel htmlFor="uncontrolled-native" <InputLabel id={`label${column.id}`}>{column.header}</InputLabel>
id={`label${column.id}`}>{column.header}</InputLabel>
<Select <Select
labelId={`label${column.id}`} labelId={`label${column.id}`}
id={column.id} id={column.id}
name={column.header} name={column.header}
value={fieldValue} value={value}
input={<OutlinedInput label={column.header}/>} input={<OutlinedInput label={column.header}/>}
onChange={handleChange} onChange={(e) => {
setFieldValue(column.id, e.target.value)
}}
> >
{columnSelectOption.map((option) => ( {columnSelectOption.map((option) => (
<MenuItem <MenuItem

View File

@@ -1,9 +1,25 @@
'use client' 'use client'
import {FormControl, FormHelperText, InputLabel, MenuItem, OutlinedInput, Select, Typography} from "@mui/material"; import {
Box,
Chip,
FormControl,
FormHelperText,
InputLabel,
MenuItem,
OutlinedInput,
Select,
Typography
} from "@mui/material";
function CustomSelectMultiple({column, value, defaultFilterTranslation}) { function CustomSelectMultiple({column, value, defaultFilterTranslation, setFieldValue}) {
const columnSelectOption = column.columnSelectOption; const columnSelectOption = column.columnSelectOption;
const getLabelForValue = (value) => {
const option = columnSelectOption.find(opt => opt.value === value);
return option ? option.label : value;
};
return ( return (
<FormControl fullWidth sx={{marginY: 1}}> <FormControl fullWidth sx={{marginY: 1}}>
<InputLabel htmlFor="uncontrolled-native" <InputLabel htmlFor="uncontrolled-native"
@@ -12,8 +28,18 @@ function CustomSelectMultiple({column, value, defaultFilterTranslation}) {
labelId={`label${column.id}`} labelId={`label${column.id}`}
id={column.id} id={column.id}
value={value} value={value}
multiple
onChange={(e) => {
setFieldValue(column.id, 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}/>} input={<OutlinedInput label={column.header}/>}
// onChange={handleChange}
> >
{columnSelectOption.map((option) => ( {columnSelectOption.map((option) => (
<MenuItem <MenuItem

View File

@@ -3,12 +3,15 @@
import {InputAdornment, TextField, Typography} from "@mui/material"; import {InputAdornment, TextField, Typography} from "@mui/material";
import FilterListIcon from '@mui/icons-material/FilterList'; import FilterListIcon from '@mui/icons-material/FilterList';
function CustomTextField({column, value, defaultFilterTranslation, handleOpenFilterBox}) { function CustomTextField({column, value, defaultFilterTranslation, handleOpenFilterBox, handleChange, handleBlur}) {
return ( return (
<TextField <TextField
id={column.id} id={column.id}
name={column.id}
label={column.header} label={column.header}
value={value} value={value}
onChange={handleChange}
onBlur={handleBlur}
fullWidth fullWidth
helperText={<Typography variant="button" sx={{color: "#16534a"}}> helperText={<Typography variant="button" sx={{color: "#16534a"}}>
نوع فیلتر: {defaultFilterTranslation} نوع فیلتر: {defaultFilterTranslation}

View File

@@ -1,31 +1,41 @@
'use client'
import {Box, InputAdornment, TextField, Typography} from "@mui/material"; import {Box, InputAdornment, TextField, Typography} from "@mui/material";
import FilterListIcon from '@mui/icons-material/FilterList'; import FilterListIcon from '@mui/icons-material/FilterList';
function CustomTextFieldRange({column, value, defaultFilterTranslation, handleOpenFilterBox}) { function CustomTextFieldRange({
column,
value,
defaultFilterTranslation,
handleOpenFilterBox,
handleChange,
handleBlur,
errors,
touched
}) {
return ( return (
<Box sx={{display: "flex"}}> <Box sx={{display: "flex"}}>
<TextField <TextField
id={column.id} id={`${column.id}[0]`}
label={<Typography> name={`${column.id}[0]`}
از {column.header} onChange={handleChange}
</Typography>} onBlur={handleBlur}
value={value} label={<Typography>از {column.header}</Typography>}
value={value[0]}
fullWidth fullWidth
helperText={<Typography variant="button" sx={{color: "#16534a"}}> helperText={<Typography variant="button" sx={{color: "#16534a"}}>نوع
نوع فیلتر: {defaultFilterTranslation} فیلتر: {defaultFilterTranslation}</Typography>}
</Typography>}
variant="outlined" variant="outlined"
size="normal" size="normal"
sx={{marginY: 1, marginRight: 1}} sx={{marginY: 1, marginRight: 1}}
/> />
<TextField <TextField
id={column.id} id={`${column.id}[1]`}
label={<Typography> name={`${column.id}[1]`}
تا {column.header} onChange={handleChange}
</Typography>} onBlur={handleBlur}
value={value} error={touched?.[column.id] && Boolean(errors?.[column.id])}
helperText={touched?.[column.id] ? errors?.[column.id] : null}
label={<Typography>تا {column.header}</Typography>}
value={value[1]}
fullWidth fullWidth
variant="outlined" variant="outlined"
size="normal" size="normal"