diff --git a/package.json b/package.json index 4a1bf6b..7bec569 100644 --- a/package.json +++ b/package.json @@ -41,4 +41,4 @@ "eslint-config-prettier": "^9.1.0", "prettier": "^3.3.2" } -} \ No newline at end of file +} diff --git a/public/global.scss b/public/global.scss index 1af3ae3..d1d10e3 100644 --- a/public/global.scss +++ b/public/global.scss @@ -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; } diff --git a/src/app/(withAuth)/dashboard/layout.js b/src/app/(withAuth)/dashboard/layout.js index cb48375..f00cb95 100644 --- a/src/app/(withAuth)/dashboard/layout.js +++ b/src/app/(withAuth)/dashboard/layout.js @@ -11,5 +11,5 @@ const Layout = ({ children }) => { ); -} +}; export default Layout; \ No newline at end of file diff --git a/src/core/components/DataTable/Main.js b/src/core/components/DataTable/Main.js index db8aebd..3ea2e65 100644 --- a/src/core/components/DataTable/Main.js +++ b/src/core/components/DataTable/Main.js @@ -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 }) => ( - <> - - - ), 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 }) => ( + <> + + + ), ...props, }); diff --git a/src/core/components/DataTable/filter/FilterBody.jsx b/src/core/components/DataTable/filter/FilterBody.jsx index 9dafb03..1f8513c 100644 --- a/src/core/components/DataTable/filter/FilterBody.jsx +++ b/src/core/components/DataTable/filter/FilterBody.jsx @@ -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(, { - 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 ( setDrawerState(false)} - sx={{ - overflowY: "hidden", - display: "flex", - flexDirection: "column", - height: "100%", - }} + sx={{ overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%" }} > {Object.keys(initialValues).length > 0 && ( diff --git a/src/core/components/DataTable/filter/FilterOptionList.jsx b/src/core/components/DataTable/filter/FilterOptionList.jsx index e088832..86a4745 100644 --- a/src/core/components/DataTable/filter/FilterOptionList.jsx +++ b/src/core/components/DataTable/filter/FilterOptionList.jsx @@ -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]} diff --git a/src/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker.jsx b/src/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker.jsx index b4da7f5..22eb5f9 100644 --- a/src/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker.jsx +++ b/src/core/components/DataTable/filter/fieldsType/CustomDate/MuiDatePicker.jsx @@ -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 ( { return ( - + ); diff --git a/src/core/components/NotificationDesign/AskForKeep.jsx b/src/core/components/NotificationDesign/AskForKeep.jsx index d4721bb..e69de29 100644 --- a/src/core/components/NotificationDesign/AskForKeep.jsx +++ b/src/core/components/NotificationDesign/AskForKeep.jsx @@ -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 ( - - - - ذخیره سازی اطلاعات - - - - - آیا مایل به ذخیره فیلتر های اعمال شده برای دفعات بعد هستید؟ - - - - - - - ); -} - -export default AskForKeep; diff --git a/src/core/components/NotificationDesign/AskForKeepData.jsx b/src/core/components/NotificationDesign/AskForKeepData.jsx new file mode 100644 index 0000000..552aee1 --- /dev/null +++ b/src/core/components/NotificationDesign/AskForKeepData.jsx @@ -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 ( + + + + ذخیره سازی تغییرات + + + + + آیا مایل به ذخیره تغییر های اعمال شده برای دفعات بعد هستید؟ + + + + + + + ); +} + +export default AskForKeepData; \ No newline at end of file diff --git a/src/lib/contexts/DataTable.jsx b/src/lib/contexts/DataTable.jsx new file mode 100644 index 0000000..32b2a7c --- /dev/null +++ b/src/lib/contexts/DataTable.jsx @@ -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( + , + { + 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: ( + + ), + }); + } + } else { + toast.dismiss({ containerId: "datatable" }); + } + }, [filterData, sortData]); + + if (!isInitStates) return null; + + return ( + + {children} + + ); +}; + +export default DataTableProvider; diff --git a/src/lib/contexts/tableSetting.js b/src/lib/contexts/tableSetting.js index 3611df9..8b05828 100644 --- a/src/lib/contexts/tableSetting.js +++ b/src/lib/contexts/tableSetting.js @@ -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 ( - + {children} ); diff --git a/src/lib/hooks/useDataTable.jsx b/src/lib/hooks/useDataTable.jsx new file mode 100644 index 0000000..e2feecb --- /dev/null +++ b/src/lib/hooks/useDataTable.jsx @@ -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; diff --git a/src/lib/hooks/useTableSetting.jsx b/src/lib/hooks/useTableSetting.jsx new file mode 100644 index 0000000..6588446 --- /dev/null +++ b/src/lib/hooks/useTableSetting.jsx @@ -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;