Merge branch 'feature/mohammad_refactor_filter' into 'develop'

Feature/mohammad refactor filter

See merge request witel-front-end/rms!8
This commit is contained in:
AmirHossein Mahmoodi
2024-07-11 07:23:57 +00:00
14 changed files with 242 additions and 195 deletions

View File

@@ -41,4 +41,4 @@
"eslint-config-prettier": "^9.1.0",
"prettier": "^3.3.2"
}
}
}

View File

@@ -5,5 +5,4 @@
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;
}

View File

@@ -11,5 +11,5 @@ const Layout = ({ children }) => {
</Box>
</Stack>
);
}
};
export default Layout;

View File

@@ -76,65 +76,43 @@ const data = [
];
const DataTable_Main = (props) => {
const { filterData } = useDataTable();
const { filterData, sortData, setSortData } = useDataTable();
const { table_url, user_id, page_name, table_name, columns, initialStateProps } = props;
const flatColumns = flattenArrayOfObjects(columns, "columns");
const { settingStore, hideAction, sortAction } = useTableSetting();
const { settingStore, hideAction } = 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;
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);
setSortData(event);
};
const fetchUrl = useMemo(() => {
const params = new URLSearchParams();
params.set("start", `${pagination.pageIndex * pagination.pageSize}`);
params.set("size", pagination.pageSize);
params.set("filters", JSON.stringify(filterData ?? []));
params.set("sorting", JSON.stringify(sortData));
return `${table_url}?${params}`;
}, [table_url, filterData, pagination, 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"] || [],
sorting: sortData,
...initialStateProps,
},
state: {
columnVisibility: settingStore?.[user_id]?.[page_name]?.[table_name]?.["hides"],
sorting: settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || [],
sorting: sortData,
pagination,
},
muiTableHeadProps: {
@@ -162,10 +140,15 @@ const DataTable_Main = (props) => {
},
manualFiltering: true,
manualPagination: true,
onPaginationChange: setPagination,
manualSorting: true,
onPaginationChange: setPagination,
onColumnVisibilityChange: onColumnVisibilityChange,
onSortingChange: onSortingChange,
renderTopToolbarCustomActions: ({ table }) => (
<>
<Toolbar columns={flatColumns} />
</>
),
...props,
});

View File

@@ -3,12 +3,10 @@
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 { useEffect, 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)({
@@ -29,46 +27,13 @@ const ScrollBox = styled(Box)({
});
function FilterBody({ columns, drawerState, setDrawerState, user_id, page_name, table_name }) {
const { settingStore, filterAction } = useTableSetting();
const { settingStore } = 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);
setInitialValues(filterData);
}, [columns, settingStore, user_id, page_name, table_name, filterData]);
useEffect(() => {
@@ -107,49 +72,16 @@ function FilterBody({ columns, drawerState, setDrawerState, user_id, page_name,
}, [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%",
}}
sx={{ overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%" }}
>
<FilterHeader setDrawerState={setDrawerState} />
{Object.keys(initialValues).length > 0 && (

View File

@@ -32,12 +32,7 @@ function FilterOptionList({
key={index}
onClick={(event) => handleChangeItem(event, index)}
selected={option === selectedFilter}
sx={{
cursor: "pointer",
width: "100px",
display: "flex",
justifyContent: "center",
}}
sx={{ cursor: "pointer", width: "100px", display: "flex", justifyContent: "center" }}
>
{columnFilterModeOptionFa[option]}
</ListItem>

View File

@@ -7,7 +7,6 @@ import ClearIcon from "@mui/icons-material/Clear";
import moment from "jalali-moment";
function MuiDatePicker({ value, setFieldValue, name, minDate, maxDate, helperText, placeholder, error }) {
return (
<Box sx={{ display: "flex", flexDirection: "column", my: 1 }}>
<LocalizationProvider

View File

@@ -3,7 +3,12 @@ import DataTableProvider from "@/lib/contexts/DataTable";
const DataTable = (props) => {
return (
<DataTableProvider>
<DataTableProvider
user_id={props.user_id}
page_name={props.page_name}
table_name={props.table_name}
columns={props.columns}
>
<DataTable_Main {...props} />
</DataTableProvider>
);

View File

@@ -1,61 +0,0 @@
"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;

View File

@@ -0,0 +1,78 @@
"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";
import useTableSetting from "@/lib/hooks/useTableSetting";
function AskForKeepData({ filterData, sortData, user_id, page_name, table_name, columns }) {
const { filterAction, sortAction } = useTableSetting();
const onSaveFilter = () => {
const filteredItems = 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);
filterAction(user_id, page_name, table_name, filteredItems, columns);
sortAction(user_id, page_name, table_name, sortData, columns);
toast.dismiss({ containerId: "datatable" });
};
const handleDismiss = () => {
toast.dismiss({ containerId: "datatable" });
};
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 AskForKeepData;

View File

@@ -0,0 +1,110 @@
import { createContext, useEffect, useState } from "react";
import { toast } from "react-toastify";
import AskForKeepData from "@/core/components/NotificationDesign/AskForKeepData";
import useTableSetting from "@/lib/hooks/useTableSetting";
import { flattenArrayOfObjects } from "@/core/utils/flattenArrayOfObjects";
export const DataTableContext = createContext();
const DataTableProvider = ({ children, user_id, page_name, table_name, columns }) => {
const { settingStore } = useTableSetting();
const [isInitStates, setInitStates] = useState(false);
const flatColumns = flattenArrayOfObjects(columns, "columns");
const [initFilter, setInitFilter] = useState({});
const [initSort, setInitSort] = useState([]);
const [filterData, setFilterData] = useState({});
const [sortData, setSortData] = useState([]);
useEffect(() => {
if (!settingStore) return;
const values = flatColumns.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?.filterFn || column._filterFn,
datatype: column.datatype,
};
} else if (column._filterFn === "between") {
acc[column.id] = {
id: column.id,
value: filter?.value || ["", ""],
filterFn: filter?.filterFn || column._filterFn,
datatype: column.datatype,
};
} else {
acc[column.id] = {
id: column.id,
value: filter?.value || "",
filterFn: filter?.filterFn || column._filterFn,
datatype: column.datatype,
};
}
return acc;
}, {});
setFilterData(values);
setSortData(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || []);
setInitFilter(values);
setInitSort(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || []);
setInitStates(true);
}, [settingStore, isInitStates]);
useEffect(() => {
if (
JSON.stringify(initFilter) !== JSON.stringify(filterData) ||
JSON.stringify(initSort) !== JSON.stringify(sortData)
) {
if (!toast.isActive("keep_data", "datatable")) {
toast(
<AskForKeepData
filterData={filterData}
sortData={sortData}
user_id={user_id}
page_name={page_name}
table_name={table_name}
columns={flatColumns}
/>,
{
containerId: "datatable",
toastId: "keep_data",
className: "filter-toast",
position: "bottom-left",
draggable: true,
autoClose: false,
}
);
} else {
toast.update("keep_data", {
containerId: "datatable",
toastId: "keep_data",
render: (
<AskForKeepData
filterData={filterData}
sortData={sortData}
user_id={user_id}
page_name={page_name}
table_name={table_name}
columns={flatColumns}
/>
),
});
}
} else {
toast.dismiss({ containerId: "datatable" });
}
}, [filterData, sortData]);
if (!isInitStates) return null;
return (
<DataTableContext.Provider value={{ filterData, setFilterData, sortData, setSortData }}>
{children}
</DataTableContext.Provider>
);
};
export default DataTableProvider;

View File

@@ -96,10 +96,7 @@ export const TableSettingProvider = ({ children }) => {
}
if (values.hides) {
const hidesArray = Object.entries(values.hides).map((e) => ({
id: e[0],
value: e[1],
}));
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) {
@@ -136,15 +133,7 @@ export const TableSettingProvider = ({ children }) => {
};
return (
<TableSettingContext.Provider
value={{
settingStore,
hideAction,
sortAction,
filterAction,
refactorAction,
}}
>
<TableSettingContext.Provider value={{ settingStore, hideAction, sortAction, filterAction, refactorAction }}>
{children}
</TableSettingContext.Provider>
);

View File

@@ -0,0 +1,9 @@
import { useContext } from "react";
import { DataTableContext } from "@/lib/contexts/DataTable";
const useTableSetting = () => {
const { filterData, setFilterData, sortData, setSortData } = useContext(DataTableContext);
return { filterData, setFilterData, sortData, setSortData };
};
export default useTableSetting;

View File

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