Merge branch 'feature/mohammad_table_test' into 'develop'

Feature/mohammad table test

See merge request witel-front-end/rms!1
This commit is contained in:
AmirHossein Mahmoodi
2024-04-13 09:31:19 +00:00
21 changed files with 1378 additions and 6 deletions

View File

@@ -15,6 +15,13 @@
"@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",
"react-dom": "^18",

View File

@@ -1,11 +1,17 @@
import {TableSettingProvider} from "@/lib/contexts/tableSetting";
export const metadata = {
title: 'سامانه جامع راهداری',
}
export default function RootLayout({children}) {
return (<html lang="fa" dir={'rtl'}>
return (
<html lang="fa" dir={'rtl'}>
<body style={{height: '100vh'}}>
{children}
<TableSettingProvider>
{children}
</TableSettingProvider>
</body>
</html>)
</html>
)
}

View File

@@ -1,5 +1,13 @@
const Page = () => {
return (<></>)
import DataTableMain from "@/components/DataTableMain";
function page() {
return (
<>
<DataTableMain />
</>
);
}
export default Page
export default page;

View File

@@ -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 (
<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;

View File

@@ -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 (
<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;

View File

@@ -0,0 +1,20 @@
import FilterColumn from "@/components/FilterColumn";
const CustomToolbar = ({columns}) => {
return (
<FilterColumn columns={columns}/>
)
}
export default CustomToolbar

View File

@@ -0,0 +1,141 @@
import DataTableTest from "@/components/DataTableTest";
import FilterColumn from "@/components/FilterColumn";
import {useMemo} from "react";
import {Button} from "@mui/material";
function DataTableMain() {
const columns = useMemo(
() => [
{
accessorKey: 'name.firstName',
header: 'نام',
size: 150,
id: "firstName",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains"
],
},
{
accessorKey: 'name.lastName',
header: 'نام خانوادگی',
size: 150,
id: "lastName",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains"
],
},
{
accessorKey: 'address',
header: 'آدرس',
size: 200,
id: "address",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains"
],
},
{
accessorKey: 'city',
header: 'شهر',
size: 150,
id: "city",
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"contains",
"lessThan",
"greaterThan",
"between",
],
},
{
accessorKey: 'state',
header: 'وضعیت',
size: 150,
id: "state",
text:'نام',
enableColumnFilter: true,
datatype: "text",
filterFn: "contains",
columnFilterModeOptions: [
"equals",
"notEquals",
"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 (
<>
<DataTableTest columns={columns}/>
</>
);
}
export default DataTableMain;

View File

@@ -0,0 +1,104 @@
"use client"
import {useMemo, useState} from 'react';
import {MaterialReactTable} from 'material-react-table';
import useTableSetting from "@/lib/hooks/useTableSetting";
import {IconButton, Tooltip, Typography} from "@mui/material";
import CustomToolbar from "@/components/CustomToolbar";
const data = [
{
name: {
firstName: 'John',
lastName: 'Doe',
},
address: '261 Erdman Ford',
city: 'East Daphne',
state: 'Kentucky',
},
{
name: {
firstName: 'Jane',
lastName: 'Doe',
},
address: '769 Dominic Grove',
city: 'Columbus',
state: 'Ohio',
},
{
name: {
firstName: 'Joe',
lastName: 'Doe',
},
address: '566 Brakus Inlet',
city: 'South Linda',
state: 'West Virginia',
},
{
name: {
firstName: 'Kevin',
lastName: 'Vandy',
},
address: '722 Emie Stream',
city: 'Lincoln',
state: 'Nebraska',
},
{
name: {
firstName: 'Joshua',
lastName: 'Rolluffs',
},
address: '32188 Larkin Turnpike',
city: 'Charleston',
state: 'South Carolina',
},
];
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);
};
const onSortingChange = (event) => {
const settingValue = event(settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []);
sortAction(userId, pageName, tableName, settingValue, columns);
}
return (
<>
<Typography>{settingStore?.[userId]?.[pageName]?.[tableName]?.['summary']}</Typography>
<MaterialReactTable
columns={columns}
data={data}
manualSorting={true}
renderTopToolbarCustomActions={({table}) => (<>
<CustomToolbar
columns={columns}
/>
</>)}
initialState={{
columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []
}}
state={{
columnVisibility: settingStore?.[userId]?.[pageName]?.[tableName]?.['hides'],
sorting: settingStore?.[userId]?.[pageName]?.[tableName]?.['sorts'] || []
}}
onColumnVisibilityChange={onColumnVisibilityChange}
onSortingChange={onSortingChange}
/>
</>
);
};
export default TestDataTable;

View File

@@ -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 (
<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 === defaultFilter} sx={{ cursor: 'pointer' }}>
<ListItemText primary={option} />
</ListItem>
))}
</List>
</Paper>
</ClickAwayListener>
</Popper>
</div>
);
}
export default FilterBox;

View File

@@ -0,0 +1,206 @@
"use client"
import {
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({ 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 : []);
}, [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) => {
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 (
<>
<Button onClick={toggleDrawer(true)}>Open drawer</Button>
<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.filterFn === 'betweenDate' ?
<BetWeenDateFilter
key={index}
id={column.id}
formik={formik}
onSelectFilter={(filterFn) => handleSelectFilter(column.id, filterFn)}
defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ''}
/>
: column.filterFn === 'betweenNum' ?
<BetweenNumberFilter
key={index}
id={column.id}
formik={formik}
onSelectFilter={(filterFn) => handleSelectFilter(column.id, filterFn)}
defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || ["", ""]}
/>
: column.type === 'select2' ?
<SelectBoxMultiple
key={index}
id={column.id}
formik={formik}
onSelectFilter={(filterFn) => handleSelectFilter(column.id, filterFn)}
defaultValue={(settingStore?.[userId]?.[pageName]?.[tableName]?.['filters'] || []).find(filter => filter.id === column.id)?.value || []}
/>
: column.type === 'select' ?
<SelectBox
key={index}
id={column.id}
formik={formik}
onSelectFilter={(filterFn) => handleSelectFilter(column.id, filterFn)}
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;

View File

@@ -0,0 +1,41 @@
"use client"
import {Typography} from "@mui/material";
const data = [
{
id : "hello",
Subitems : [
{id : "amin",firstName : "amin", lastName:"ali",Subitems : [{id : "shahrokh",shahrokh : "shahrokh"}], hasSubitems : true, showSubitems : false},
{id : "amir",firstName : "amir", lastName:"akbar", hasSubitems : false, showSubitems : false},
],
hasSubitems : true,
showSubitems : false
},
{
id : "hi",
Subitems : [
{id : "ali",firstName : "ali", lastName:"ali", hasSubitems : false, showSubitems : false},
{id : "akbar",firstName : "akbar", lastName:"akbar", hasSubitems : false, showSubitems : false},
],
hasSubitems : true,
showSubitems : false
}
]
const MapTest = () => {
const Amin = data.map((item) => {
return item.hasSubitems ? {...item, Subitems: item.Subitems.map((subitem) => true ? {
...subitem, showSubitems: !item.showSubitems
} : subitem)} : item
})
console.log(Amin)
return (
<>
<Typography>hello</Typography>
</>
)
}
export default MapTest

View File

@@ -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 (
<Box sx={{ padding: '4px', position: 'relative' }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<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,
},
}}
value={pickerValue}
minDate={minDate ? new Date(minDate) : null}
maxDate={maxDate ? new Date(maxDate) : null}
/>
</LocalizationProvider>
</Box>
<IconButton
size="small"
onClick={() => {
formik.setFieldValue(name, "");
setPickerValue(null);
}}
sx={{
color: "#bfbfbf",
position: 'absolute',
right: '4px',
top: '35%',
transform: 'translateY(-50%)',
}}
>
<ClearIcon />
</IconButton>
</Box>
);
}

View File

@@ -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 (
<>
<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;

View File

@@ -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 (
<>
<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;

View File

@@ -0,0 +1,51 @@
import {IconButton, Paper, TextField} from '@mui/material';
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 [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;

View File

@@ -4,6 +4,7 @@ import {Box, Drawer, IconButton, Paper, styled, Toolbar, Typography} from "@mui/
import MuiAppBar from '@mui/material/AppBar';
import MenuIcon from '@mui/icons-material/Menu';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
import SidebarMenu from "@/core/components/SidebarMenu";
const drawerWidth = 260;
@@ -82,6 +83,7 @@ const HeaderWithSidebar = ({children}) => {
<ChevronRightIcon/>
</IconButton>
</DrawerHeader>
<SidebarMenu />
</Drawer>
<Main elevation={0} open={open}>
<DrawerHeader/>

View File

@@ -0,0 +1,46 @@
import {Collapse, ListItem, ListItemButton, ListItemIcon, ListItemText} from "@mui/material";
import { ExpandLess, ExpandMore } from '@mui/icons-material';
import WifiIcon from '@mui/icons-material/Wifi';
import SidebarSubitems from "@/core/components/SidebarSubitems";
const SidebarListItems = ({menuItem, dispatch}) => {
return(
<>
<ListItem disablePadding sx={{p : 0}}>
<ListItemButton sx={{
'&.Mui-selected': {
backgroundColor: 'rgba(0, 0, 0, 0.2)',
},
}} selected={menuItem.selected} onClick={() => {
dispatch({type: "COLLAPSE_MENU", id: menuItem.id})
}}>
<ListItemIcon
sx={{
minWidth: 0,
justifyContent: "center",
color: "primary.main",
width: 40,
height: 24,
pr: 2,
}}
>
{menuItem.icon}
</ListItemIcon>
<ListItemText primary={menuItem.id} />
{menuItem.hasSubitems ? (menuItem.showSubitems ? <ExpandLess /> : <ExpandMore />) : null }
</ListItemButton>
</ListItem>
<Collapse in={menuItem.showSubitems} timeout="auto" mountOnEnter={true} unmountOnExit={true}>
{
menuItem.hasSubitems ? (menuItem.Subitems.map((subitem,index)=>{
return(
<SidebarSubitems dispatch={dispatch} key={index} subitem={subitem}/>
)
})) : null
}
</Collapse>
</>
)
}
export default SidebarListItems

View File

@@ -0,0 +1,201 @@
"use client"
import {List} from "@mui/material";
import SidebarListItems from "@/core/components/SidebarListItems";
import {useEffect, useReducer, useState} from "react";
import {usePathname} from 'next/navigation'
import SecurityIcon from '@mui/icons-material/Security';
import SpaceDashboardIcon from "@mui/icons-material/SpaceDashboard";
import AssessmentIcon from '@mui/icons-material/Assessment';
import AirlineSeatReclineNormalIcon from "@mui/icons-material/AirlineSeatReclineNormal";
import RepartitionIcon from '@mui/icons-material/Repartition';
const data = [
{
id: "hello",
type: "menu",
route: "/hello",
icon: <SecurityIcon sx={{width: 'inherit', height: 'inherit'}}/>,
selected: false,
Subitems: [
{
id: "amin",
route: "/s",
icon: <SpaceDashboardIcon sx={{width: 'inherit', height: 'inherit'}}/>,
firstName: "amin",
lastName: "ali",
Subitems: [
{
id: "shahrokh", shahrokh: "shahrokh", type: "page", selected: false, route: "/dashbssoard", icon: <AssessmentIcon sx={{width: 'inherit', height: 'inherit'}}/>
},
{
id: "shasdasdsahrokh", shahrokh: "shahroasdasdkh", type: "page", selected: false, route: "/dashboardsd", icon: <AirlineSeatReclineNormalIcon sx={{width: 'inherit', height: 'inherit'}}/>
}
],
hasSubitems: true,
showSubitems: false,
type: "menu",
selected: false,
},
{
id: "amir",
route: "/dashboarsad",
icon: <RepartitionIcon sx={{width: 'inherit', height: 'inherit'}}/>,
firstName: "amir",
lastName: "akbar",
hasSubitems: false,
showSubitems: false,
selected: false,
type: "page",
},
],
hasSubitems: true,
showSubitems: false
},
{
id: "hi",
type: "menu",
icon: <SecurityIcon sx={{width: 'inherit', height: 'inherit'}}/>,
route: "/hi",
selected: false,
Subitems: [
{
id: "ali",
route: "/dashboardss",
firstName: "ali",
lastName: "ali",
icon: <SecurityIcon sx={{width: 'inherit', height: 'inherit'}}/>,
hasSubitems: false,
showSubitems: false,
type: "page",
selected: false,
},
{
id: "akbar",
route: "/akbar",
firstName: "akbar",
icon: <SecurityIcon sx={{width: 'inherit', height: 'inherit'}}/>,
lastName: "akbar",
hasSubitems: false,
showSubitems: false,
type: "page",
selected: false,
},
],
hasSubitems: true,
showSubitems: false
},
{
id: "hoo",
type: "page",
route: "/dashboard",
icon: <SecurityIcon sx={{width: 'inherit', height: 'inherit'}}/>,
selected: false,
hasSubitems: false,
showSubitems: false
}
]
function reducer(state, action) {
switch (action.type) {
case "COLLAPSE_MENU":
return state.map((item) => action.id == item.id ? {
...item, showSubitems: !item.showSubitems
} : item)
case "COLLAPSE_SUB_ITEMS" :
return state.map((item) => {
return item.hasSubitems ? {
...item, Subitems: item.Subitems.map((subitem) => action.id === subitem.id ? {
...subitem, showSubitems: !subitem.showSubitems
} : subitem)
} : item
})
case "COLLAPSE_SUB_SECOND_ITEMS" :
return state.map((item) => {
return item.hasSubitems ? {
...item, Subitems: item.Subitems.map((subitem) => {
return subitem.hasSubitems ? {
...subitem,
Subitems: subitem.Subitems.map((secondSubitem) => action.id === secondSubitem.id ? {
...secondSubitem, showSubitems: !secondSubitem.showSubitems
} : secondSubitem)
} : subitem
})
} : item
})
case "SELECTED":
return state.map((item) => {
return item.type === "page" ? {
...item, selected: action.route === item.route,showSubitems: item.route === action.route
} : item.Subitems && Array.isArray(item.Subitems) ? {
...item, Subitems: item.Subitems.map((subitem) => {
return subitem.type === "page" ? {
...subitem, selected: action.route === subitem.route,showSubitems: subitem.route === action.route
} : subitem.Subitems && Array.isArray(subitem.Subitems) ? {
...subitem, Subitems: subitem.Subitems.map((secondSubItem) => ({
...secondSubItem, selected: secondSubItem.route === action.route
})),showSubitems: subitem.Subitems.some((secondSubItem) => secondSubItem.route === action.route),
} : subitem
}),showSubitems: item.Subitems.some((subitem) => subitem.showSubitems),
} : item
});
// case "SELECTED":
// return state.map((item) => {
// return item.type === "page" ? {
// ...item,
// selected: action.route === item.route
// } : item.Subitems && Array.isArray(item.Subitems) ? {
// ...item,
// Subitems: item.Subitems.map((subitem) => {
// const updatedSubitem = subitem.type === "page" ? {
// ...subitem,
// selected: action.route === subitem.route
// } : subitem.Subitems && Array.isArray(subitem.Subitems) ? {
// ...subitem,
// Subitems: subitem.Subitems.map((secondSubItem) => ({
// ...secondSubItem,
// selected: secondSubItem.route === action.route
// })),
// showSubitems: subitem.Subitems.some((secondSubItem) => secondSubItem.route === action.route),
// } : subitem;
//
// console.log("Updated subitem:", updatedSubitem); // Log the updated subitem
// return updatedSubitem;
// }),
// showSubitems: item.Subitems.some((subitem) => subitem.showSubitems),
// } : item
// });
default:
throw new Error();
}
}
const SidebarMenu = () => {
const [menuItems, dispatch] = useReducer(reducer, data);
const pathname = usePathname()
const [selectedKey, setSelectedKey] = useState(null);
useEffect(() => {
dispatch({type: "SELECTED", route: pathname});
setSelectedKey(pathname);
}, [pathname]);
useEffect(() => {
selectedKey && document.querySelector(`[data-route="${selectedKey}"]`)?.scrollIntoView({
behavior: "smooth", block: "center"
});
}, [selectedKey]);
return (
<List disablePadding>
{
menuItems.map((menuItem) => {
return (
<SidebarListItems dispatch={dispatch} key={menuItem.id} menuItem={menuItem}/>
)
})
}
</List>
)
}
export default SidebarMenu

View File

@@ -0,0 +1,74 @@
import {Collapse, List, ListItem, ListItemButton, ListItemIcon, ListItemText} from "@mui/material";
import {ExpandLess, ExpandMore} from "@mui/icons-material";
const SidebarSubitems = ({subitem, dispatch}) => {
return (
<>
<List disablePadding>
<ListItem disablePadding>
<ListItemButton sx={{
'&.Mui-selected': {
backgroundColor: 'rgba(0, 0, 0, 0.1)',
},
}} selected={subitem.selected} onClick={() => {
dispatch({type: "COLLAPSE_SUB_ITEMS", id: subitem.id})
}}>
<ListItemIcon
sx={{
minWidth: 0,
justifyContent: "center",
color: "primary.main",
width: 40,
height: 24,
pr: 2,
}}
>
{subitem.icon}
</ListItemIcon>
<ListItemText primary={subitem.firstName}/>
{subitem.hasSubitems ? (subitem.showSubitems ? <ExpandLess/> : <ExpandMore/>) : null
}
</ListItemButton>
</ListItem>
</List>
{
subitem.hasSubitems ? (subitem.Subitems.map((subSubitem, index) => {
return (
<Collapse key={index} in={subitem.showSubitems} timeout="auto" mountOnEnter={true} unmountOnExit={true}>
<List disablePadding>
<ListItem disablePadding>
<ListItemButton
onClick={() => {
dispatch({type: "COLLAPSE_SUB_SECOND_ITEMS", id: subSubitem.id})
}}
selected={subSubitem.selected}
sx={{
'&.Mui-selected': {
backgroundColor: 'rgba(0, 0, 0, 0.05)',
},
}}
>
<ListItemIcon
sx={{
minWidth: 0,
justifyContent: "center",
color: "primary.main",
width: 40,
height: 24,
pr: 2,
}}
>
{subSubitem.icon}
</ListItemIcon>
<ListItemText primary={subSubitem.shahrokh}/>
</ListItemButton>
</ListItem>
</List>
</Collapse>
)
})) : null
}
</>
)
}
export default SidebarSubitems

View File

@@ -0,0 +1,108 @@
'use client'
import {createContext, useCallback, useEffect, useState} from "react";
export const TableSettingContext = createContext();
export const TableSettingProvider = ({children}) => {
const [settingStore, setSettingStore] = useState({});
useEffect(() => {
const _localStorage = localStorage.getItem("_setting-app")
if (_localStorage) {
setSettingStore(JSON.parse(_localStorage))
}
}, []);
const hideAction = useCallback((user_id, page_name, table_name, settingValue, columns) => {
updateSettingStorage(user_id, page_name, table_name, "hides", settingValue, columns);
}, [])
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, 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")) || {};
const userSettings = prevLocalStorage[user_id] || {};
const pageSettings = userSettings[page_name] || {};
const tableSettings = pageSettings[table_name] || {};
let newSettings;
if (settingType === "sorts" || settingType === "filters") {
newSettings = [...settingValue];
} else {
newSettings = {...tableSettings[settingType] || {}, ...settingValue};
}
const updatedLocalStorage = {
...prevLocalStorage,
[user_id]: {
...userSettings,
[page_name]: {
...pageSettings,
[table_name]: {
...tableSettings,
[settingType]: Array.isArray(settingValue) ? [...newSettings] : {...newSettings},
summary: SummaryTextMessage({
...tableSettings,
[settingType]: Array.isArray(settingValue) ? [...newSettings] : {...newSettings},
}, columns)
},
},
},
};
localStorage.setItem("_setting-app", JSON.stringify(updatedLocalStorage));
setSettingStore(updatedLocalStorage);
}
const SummaryTextMessage = (values, columns) => {
let SummaryText = "";
let HideText = "";
let FilterText;
let SortText = "";
if (values.sorts && values.sorts.length > 0) {
const _list = []
for (const sort of values.sorts) {
const _column = columns.find(column => column.id == sort.id)
_list.push(`${_column.header}` + `(${sort.desc ? "نزولی" : "صعودی"})`)
}
if (_list.length > 0)
SortText = " مرتب شده بر اساس " + _list.join(", ")
}
if (values.hides) {
const hidesArray = Object.entries(values.hides).map((e) => ({id: e[0], value: e[1]}))
if (hidesArray.length > 0) {
const _list = []
for (const hide of hidesArray) {
if (hide.value) continue
const _column = columns.find(c => c.id == hide.id)
_list.push(_column.header)
}
if (_list.length > 0)
HideText = "ستون های مخفی شده : " + _list.join(", ");
}
}
if (HideText || SortText) {
if (SortText) {
SummaryText += SortText;
if (HideText) {
SummaryText += " ; ";
}
}
if (HideText) {
SummaryText += HideText;
}
}
return SummaryText;
};
return (
<TableSettingContext.Provider
value={{settingStore, hideAction, sortAction, filterAction}}>
{children}
</TableSettingContext.Provider>
);
};

View File

@@ -0,0 +1,9 @@
import {useContext} from "react";
import {TableSettingContext} from "../contexts/tableSetting";
const useUser = () => {
const {settingStore, hideAction, sortAction, filterAction} = useContext(TableSettingContext);
return {settingStore, hideAction, sortAction, filterAction};
};
export default useUser;