From bd6ea0aeaeb68fd7f50c0bd0808fdafabd9edcb0 Mon Sep 17 00:00:00 2001 From: Mohammad Jalali Date: Sun, 21 Jul 2024 09:31:26 +0330 Subject: [PATCH 1/3] solve filter of datatable bug and some indentation changes --- .../dashboard/inquiryPrivacyFencing/index.jsx | 103 +----------------- src/core/components/DataTable/Main.js | 12 +- .../DataTable/filter/FilterBodyField.jsx | 22 ++-- .../DataTable/filter/FilterOptionList.jsx | 17 +-- src/lib/contexts/DataTable.js | 4 +- 5 files changed, 34 insertions(+), 124 deletions(-) diff --git a/src/components/dashboard/inquiryPrivacyFencing/index.jsx b/src/components/dashboard/inquiryPrivacyFencing/index.jsx index 832cd3b..c7f3404 100644 --- a/src/components/dashboard/inquiryPrivacyFencing/index.jsx +++ b/src/components/dashboard/inquiryPrivacyFencing/index.jsx @@ -3,7 +3,6 @@ import { useMemo } from "react"; import Toolbar from "@/components/dashboard/inquiryPrivacyFencing/Toolbar"; import DataTable from "@/core/components/DataTable"; -import { Box } from "@mui/material"; import { GET_INQUIRY_PRIVACY_FENCING_ROUTE } from "@/core/utils/routes"; const InquiryPrivacyFencingPage = () => { @@ -32,259 +31,174 @@ const InquiryPrivacyFencingPage = () => { header: "متقاضی", id: "motaghazi_is_legal", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "nameh_date", header: "تاریخ نامه درخواست", id: "nameh_date", enableColumnFilter: false, - datatype: "date", - filterFn: "equals", }, { accessorKey: "motaghazi_type", header: "نوع متقاضی", id: "motaghazi_type", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "motaghazi_firstname", header: "نام متقاضی", id: "motaghazi_firstname", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "motaghazi_lastname", header: "نام خانوادگی متقاضی", id: "motaghazi_lastname", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "national_id", header: "کد ملی/شناسه ملی", id: "national_id", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "tel_number", header: "تلفن", id: "tel_number", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "mobile_number", header: "تلفن همراه", id: "mobile_number", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "address", header: "آدرس", id: "address", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "edare_kol", header: "اداره كل راهداری", id: "edare_kol", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "edare_shahri", header: "اداره شهرستان", id: "edare_shahri", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "rah_type", header: "نوع راه", id: "rah_type", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "name_mehvar_fa", header: "نام محور", id: "name_mehvar_fa", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "mizan_harim", header: "میزان حریم", id: "mizan_harim", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "arze_navar", header: "عرض نوار تاسیساتی زیربنایی", id: "arze_navar", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "samt", header: "سمت", id: "samt", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "kilometr", header: "كيلومتر", id: "kilometr", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "zone", header: "zone", id: "zone", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "karbari_type", header: "نوع کاربری", id: "karbari_type", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "tarh_title", header: "عنوان طرح", id: "tarh_title", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "masahat_zirbana", header: "مساحت زیربنا", id: "masahat_zirbana", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "ehdasat_type", header: "نوع احداثات", id: "ehdasat_type", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "divarkeshi_distance", header: "فاصله دیوارکشی از آکس محور", id: "divarkeshi_distance", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "mahale_ejra", header: "محل اجرا", id: "mahale_ejra", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "traffic", header: "میزان ترافیک", id: "traffic", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "vaziat_eghtesadi", header: "وضعیت اقتصادی", id: "vaziat_eghtesadi", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "has_access", header: "آیا راه دسترسی از قبل وجود دارد؟", id: "has_access", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "shomare_estelam_harim", header: "شماره استعلام دفتر ایمنی و حریم", id: "shomare_estelam_harim", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "max_month", header: "ماه", id: "max_month", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { @@ -292,48 +206,33 @@ const InquiryPrivacyFencingPage = () => { header: "روز", id: "max_day", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "shomare_tahaodname", header: "شماره تعهدنامه", id: "shomare_tahaodname", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "shomare_daftarkhaneh", header: "شماره دفترخانه", id: "shomare_daftarkhaneh", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "description", header: "توضیحات", id: "description", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, { accessorKey: "ronevesht", header: "رونوشت", id: "ronevesht", enableColumnFilter: false, - datatype: "text", - filterFn: "contains", - columnFilterModeOptions: ["equals", "notEquals", "contains"], }, ], - [] + [], ); return ( diff --git a/src/core/components/DataTable/Main.js b/src/core/components/DataTable/Main.js index db3a6f1..f18084c 100644 --- a/src/core/components/DataTable/Main.js +++ b/src/core/components/DataTable/Main.js @@ -26,10 +26,20 @@ const DataTable_Main = (props) => { }; const fetchUrl = useMemo(() => { + const isValueEmpty = (value) => { + if (Array.isArray(value)) { + return value.length === 0 || value.every(v => v === ""); + } + return value === "" || value === null || value === undefined; + }; + const filteredFilterData = Object.values(filterData).filter( + (filter) => !isValueEmpty(filter.value), + ); + const params = new URLSearchParams(); params.set("start", `${pagination.pageIndex * pagination.pageSize}`); params.set("size", pagination.pageSize); - params.set("filters", JSON.stringify(isEmptyObject(filterData) ? [] : [filterData])); + params.set("filters", JSON.stringify(filteredFilterData.length === 0 ? [] : filteredFilterData)); params.set("sorting", JSON.stringify(sortData)); return `${table_url}?${params}`; }, [table_url, filterData, pagination, columns, sortData]); diff --git a/src/core/components/DataTable/filter/FilterBodyField.jsx b/src/core/components/DataTable/filter/FilterBodyField.jsx index a9bef12..7f0337e 100644 --- a/src/core/components/DataTable/filter/FilterBodyField.jsx +++ b/src/core/components/DataTable/filter/FilterBodyField.jsx @@ -18,15 +18,15 @@ const columnFilterModeOptionFa = { }; function FilterBodyField({ - column, - filterParameters, - handleChange, - handleBlur, - setFieldValue, - errors, - touched, - resetForm, -}) { + column, + filterParameters, + handleChange, + handleBlur, + setFieldValue, + errors, + touched, + resetForm, + }) { const [anchorEl, setAnchorEl] = useState(null); const defaultFilterTranslation = columnFilterModeOptionFa[filterParameters.filterFn] || filterParameters.filterFn; @@ -48,8 +48,8 @@ function FilterBodyField({ touched={touched} /> ) : filterParameters.datatype === "numeric" && - filterParameters.filterFn === "equals" && - Array.isArray(column.columnSelectOption) ? ( + filterParameters.filterFn === "equals" && + Array.isArray(column.columnSelectOption) ? ( { diff --git a/src/lib/contexts/DataTable.js b/src/lib/contexts/DataTable.js index 3ada093..85f521f 100644 --- a/src/lib/contexts/DataTable.js +++ b/src/lib/contexts/DataTable.js @@ -22,7 +22,7 @@ const DataTableProvider = ({ children, user_id, page_name, table_name, columns } 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 + (filter) => filter.id === column.id, ); if (column.datatype === "array") { acc[column.id] = { @@ -81,7 +81,7 @@ const DataTableProvider = ({ children, user_id, page_name, table_name, columns } position: "bottom-left", draggable: true, autoClose: false, - } + }, ); } else { toast.update("keep_data", { From d3bc855f396352a8d7c09575f619581ed58627b7 Mon Sep 17 00:00:00 2001 From: Mohammad Jalali Date: Sun, 21 Jul 2024 17:50:17 +0330 Subject: [PATCH 2/3] make hide action of datatable own --- .../dashboard/inquiryPrivacyFencing/index.jsx | 35 ++++ src/core/components/DataTable/Main.js | 9 - .../buttons/ShowHideColumnsButton.js | 30 ---- .../DataTable/filter/FilterHeader.jsx | 2 +- .../components/DataTable/hide/HideBody.jsx | 46 ++++++ .../DataTable/hide/HideBodyField.jsx | 38 +++++ .../components/DataTable/hide/HideButton.jsx | 21 +++ .../components/DataTable/hide/HideHeader.jsx | 34 ++++ src/core/components/DataTable/hide/index.jsx | 24 +++ .../DataTable/menus/ShowHideColumnsMenu.js | 116 ------------- .../menus/ShowHideColumnsMenuItems.js | 154 ------------------ .../toolbar/ToolbarInternalButtons.js | 44 ----- .../DataTable/toolbar/TopToolbar.js | 8 +- src/core/utils/flattenArrayOfObjects.js | 6 +- src/lib/contexts/DataTable.js | 33 ++-- 15 files changed, 229 insertions(+), 371 deletions(-) delete mode 100644 src/core/components/DataTable/buttons/ShowHideColumnsButton.js create mode 100644 src/core/components/DataTable/hide/HideBody.jsx create mode 100644 src/core/components/DataTable/hide/HideBodyField.jsx create mode 100644 src/core/components/DataTable/hide/HideButton.jsx create mode 100644 src/core/components/DataTable/hide/HideHeader.jsx create mode 100644 src/core/components/DataTable/hide/index.jsx delete mode 100644 src/core/components/DataTable/menus/ShowHideColumnsMenu.js delete mode 100644 src/core/components/DataTable/menus/ShowHideColumnsMenuItems.js delete mode 100644 src/core/components/DataTable/toolbar/ToolbarInternalButtons.js diff --git a/src/components/dashboard/inquiryPrivacyFencing/index.jsx b/src/components/dashboard/inquiryPrivacyFencing/index.jsx index c7f3404..3ee9a28 100644 --- a/src/components/dashboard/inquiryPrivacyFencing/index.jsx +++ b/src/components/dashboard/inquiryPrivacyFencing/index.jsx @@ -8,6 +8,41 @@ import { GET_INQUIRY_PRIVACY_FENCING_ROUTE } from "@/core/utils/routes"; const InquiryPrivacyFencingPage = () => { const columns = useMemo( () => [ + { + accessorKey: "fullname", + header: "fullname", + id: "fullname", + columns: [ + { + accessorKey: "first_name", + header: "first_name", + id: "first_name", + columns: [ + { + accessorKey: "first_name_first", + header: "first_name_first", + id: "first_name_first", + enableColumnFilter: false, + datatype: "text", + }, + { + accessorKey: "first_name_last", + header: "first_name_last", + id: "first_name_last", + enableColumnFilter: false, + datatype: "text", + }, + ], + }, + { + accessorKey: "last_name", + header: "last_name", + id: "last_name", + enableColumnFilter: false, + datatype: "text", + }, + ], + }, { accessorKey: "dabirkhaneh_number", header: "شماره دبیرخانه درخواست", diff --git a/src/core/components/DataTable/Main.js b/src/core/components/DataTable/Main.js index f18084c..4a3908b 100644 --- a/src/core/components/DataTable/Main.js +++ b/src/core/components/DataTable/Main.js @@ -1,7 +1,6 @@ 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 useDataTable from "@/lib/hooks/useDataTable"; import { useMemo, useState } from "react"; @@ -10,17 +9,10 @@ import useSWR from "swr"; const DataTable_Main = (props) => { const request = useRequest(); - const isEmptyObject = (obj) => obj && Object.keys(obj).length === 0 && obj.constructor === Object; const { filterData, sortData, setSortData, hideData, setHideData } = useDataTable(); const { need_filter, table_url, user_id, page_name, table_name, columns, initialStateProps, TableToolbar } = props; const flatColumns = flattenArrayOfObjects(columns, "columns"); - const { settingStore, hideAction } = useTableSetting(); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); - - const onColumnVisibilityChange = (event) => { - setHideData(event); - }; - const onSortingChange = (event) => { setSortData(event); }; @@ -103,7 +95,6 @@ const DataTable_Main = (props) => { manualPagination: true, manualSorting: true, onPaginationChange: setPagination, - onColumnVisibilityChange: onColumnVisibilityChange, onSortingChange: onSortingChange, renderTopToolbarCustomActions: ({ table }) => ( <> diff --git a/src/core/components/DataTable/buttons/ShowHideColumnsButton.js b/src/core/components/DataTable/buttons/ShowHideColumnsButton.js deleted file mode 100644 index cd6c8ac..0000000 --- a/src/core/components/DataTable/buttons/ShowHideColumnsButton.js +++ /dev/null @@ -1,30 +0,0 @@ -import { IconButton, Tooltip } from "@mui/material"; -import { useState } from "react"; -import DataTable_ShowHideColumnsMenu from "../menus/ShowHideColumnsMenu"; - -const DataTable_ShowHideColumnsButton = ({ table, ...rest }) => { - const { - options: { - icons: { ViewColumnIcon }, - localization, - }, - } = table; - - const [anchorEl, setAnchorEl] = useState(null); - - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - - return ( - <> - - - - - - {anchorEl && } - - ); -}; -export default DataTable_ShowHideColumnsButton; diff --git a/src/core/components/DataTable/filter/FilterHeader.jsx b/src/core/components/DataTable/filter/FilterHeader.jsx index 76980ce..50d00d8 100644 --- a/src/core/components/DataTable/filter/FilterHeader.jsx +++ b/src/core/components/DataTable/filter/FilterHeader.jsx @@ -19,7 +19,7 @@ function FilterHeader({ setDrawerState }) { }} > - + فیلتر diff --git a/src/core/components/DataTable/hide/HideBody.jsx b/src/core/components/DataTable/hide/HideBody.jsx new file mode 100644 index 0000000..adf4c12 --- /dev/null +++ b/src/core/components/DataTable/hide/HideBody.jsx @@ -0,0 +1,46 @@ +"use client"; + +import { Box, Drawer, styled } from "@mui/material"; +import useDataTable from "@/lib/hooks/useDataTable"; +import HideBodyField from "@/core/components/DataTable/hide/HideBodyField"; +import HideHeader from "@/core/components/DataTable/hide/HideHeader"; + +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 HideBody({ columns, drawerState, setDrawerState }) { + const { hideData } = useDataTable(); + + return ( + setDrawerState(false)} + sx={{ overflowY: "hidden", display: "flex", flexDirection: "column", height: "100%" }} + > + + {Object.keys(hideData).length > 0 && ( + + + {columns.map((column) => ())} + + + )} + + ); +} + +export default HideBody; diff --git a/src/core/components/DataTable/hide/HideBodyField.jsx b/src/core/components/DataTable/hide/HideBodyField.jsx new file mode 100644 index 0000000..e8123af --- /dev/null +++ b/src/core/components/DataTable/hide/HideBodyField.jsx @@ -0,0 +1,38 @@ +import { Box, FormControlLabel, Switch, Typography } from "@mui/material"; +import useDataTable from "@/lib/hooks/useDataTable"; + +function HideBodyField({ column, level = 0 }) { + const { hideData, setHideData } = useDataTable(); + const paddingLeft = 2 * level; + + return ( + + { + setHideData(hideData => ({ + ...hideData, + [column.id]: !hideData[column.id], + })); + }} + name={column.id} + /> + } + label={ + {column.header} + } + /> + {column.columns?.map((subColumn) => ( + + ))} + + ); +} + +export default HideBodyField; \ No newline at end of file diff --git a/src/core/components/DataTable/hide/HideButton.jsx b/src/core/components/DataTable/hide/HideButton.jsx new file mode 100644 index 0000000..de919fe --- /dev/null +++ b/src/core/components/DataTable/hide/HideButton.jsx @@ -0,0 +1,21 @@ +"use client"; + +import ViewColumnIcon from "@mui/icons-material/ViewColumn"; +import { IconButton, Tooltip } from "@mui/material"; + +function HideButton({ drawerState, setDrawerState }) { + return ( + + { + setDrawerState(!drawerState); + }} + aria-label="hide table column" + > + + + + ); +} + +export default HideButton; diff --git a/src/core/components/DataTable/hide/HideHeader.jsx b/src/core/components/DataTable/hide/HideHeader.jsx new file mode 100644 index 0000000..5adcf25 --- /dev/null +++ b/src/core/components/DataTable/hide/HideHeader.jsx @@ -0,0 +1,34 @@ +"use client"; + +import { Box, IconButton, Typography } from "@mui/material"; +import CancelIcon from "@mui/icons-material/Cancel"; +import ViewColumnIcon from "@mui/icons-material/ViewColumn"; + +function FilterHeader({ setDrawerState }) { + return ( + + + + + نمایش/مخفی کردن ستون ها + + + setDrawerState(false)}> + + + + ); +} + +export default FilterHeader; diff --git a/src/core/components/DataTable/hide/index.jsx b/src/core/components/DataTable/hide/index.jsx new file mode 100644 index 0000000..b917c56 --- /dev/null +++ b/src/core/components/DataTable/hide/index.jsx @@ -0,0 +1,24 @@ +"use client"; + +import { useState } from "react"; +import HideButton from "@/core/components/DataTable/hide/HideButton"; +import HideBody from "@/core/components/DataTable/hide/HideBody"; + +function HideColumn({ columns, user_id, page_name, table_name }) { + const [open, setOpen] = useState(false); + return ( + <> + + + + ); +} + +export default HideColumn; diff --git a/src/core/components/DataTable/menus/ShowHideColumnsMenu.js b/src/core/components/DataTable/menus/ShowHideColumnsMenu.js deleted file mode 100644 index cb0423c..0000000 --- a/src/core/components/DataTable/menus/ShowHideColumnsMenu.js +++ /dev/null @@ -1,116 +0,0 @@ -import { Box, Button, Divider, Menu } from "@mui/material"; -import { useMemo, useState } from "react"; -import DataTable_ShowHideColumnsMenuItems from "./ShowHideColumnsMenuItems"; - -const DataTable_ShowHideColumnsMenu = ({ anchorEl, setAnchorEl, table, ...rest }) => { - const { - getAllColumns, - getAllLeafColumns, - getCenterLeafColumns, - getIsAllColumnsVisible, - getIsSomeColumnsPinned, - getIsSomeColumnsVisible, - getLeftLeafColumns, - getRightLeafColumns, - getState, - options: { - enableColumnOrdering, - enableColumnPinning, - enableHiding, - localization, - mrtTheme: { menuBackgroundColor }, - }, - } = table; - - const { columnOrder, columnPinning, density } = getState(); - - const handleToggleAllColumns = (value) => { - getAllLeafColumns() - .filter((col) => col.columnDef.enableHiding !== false) - .forEach((col) => col.toggleVisibility(value)); - }; - - const allColumns = useMemo(() => { - const columns = getAllColumns(); - if (columnOrder.length > 0 && !columns.some((col) => col.columnDef.columnDefType === "group")) { - return [ - ...getLeftLeafColumns(), - ...Array.from(new Set(columnOrder)).map((colId) => - getCenterLeafColumns().find((col) => col?.id === colId) - ), - ...getRightLeafColumns(), - ].filter(Boolean); - } - return columns; - }, [ - columnOrder, - columnPinning, - getAllColumns(), - getCenterLeafColumns(), - getLeftLeafColumns(), - getRightLeafColumns(), - ]); - - const isNestedColumns = allColumns.some((col) => col.columnDef.columnDefType === "group"); - - const [hoveredColumn, setHoveredColumn] = useState(null); - - return ( - setAnchorEl(null)} - open={!!anchorEl} - {...rest} - > - - {enableHiding && ( - - )} - {enableColumnOrdering && ( - - )} - {enableColumnPinning && ( - - )} - {enableHiding && ( - - )} - - - {allColumns.map((column, index) => ( - - ))} - - ); -}; -export default DataTable_ShowHideColumnsMenu; diff --git a/src/core/components/DataTable/menus/ShowHideColumnsMenuItems.js b/src/core/components/DataTable/menus/ShowHideColumnsMenuItems.js deleted file mode 100644 index 97c2e5d..0000000 --- a/src/core/components/DataTable/menus/ShowHideColumnsMenuItems.js +++ /dev/null @@ -1,154 +0,0 @@ -import { getCommonTooltipProps, parseFromValuesOrFunc } from "@/core/utils/utils"; -import { Box, FormControlLabel, MenuItem, Switch, Tooltip, Typography } from "@mui/material"; -import { useRef, useState } from "react"; -import DataTable_GrabHandleButton from "../buttons/GrabHandleButton"; - -const DataTable_ShowHideColumnsMenuItems = ({ - allColumns, - column, - hoveredColumn, - isNestedColumns, - setHoveredColumn, - table, - ...rest -}) => { - const { - getState, - options: { - enableColumnOrdering, - enableHiding, - localization, - mrtTheme: { draggingBorderColor }, - }, - setColumnOrder, - } = table; - - const { columnOrder } = getState(); - const { columnDef } = column; - const { columnDefType } = columnDef; - - const switchChecked = column.getIsVisible(); - - const handleToggleColumnHidden = (column) => { - if (columnDefType === "group") { - column?.columns?.forEach?.((childColumn) => { - childColumn.toggleVisibility(!switchChecked); - }); - } else { - column.toggleVisibility(); - } - }; - - const menuItemRef = useRef(null); - - const [isDragging, setIsDragging] = useState(false); - - const handleDragStart = (e) => { - setIsDragging(true); - try { - e.dataTransfer.setDragImage(menuItemRef.current, 0, 0); - } catch (e) { - console.error(e); - } - }; - - const handleDragEnd = (_e) => { - setIsDragging(false); - setHoveredColumn(null); - if (hoveredColumn) { - setColumnOrder(reorderColumn(column, hoveredColumn, columnOrder)); - } - }; - - const handleDragEnter = (_e) => { - if (!isDragging && columnDef.enableColumnOrdering !== false) { - setHoveredColumn(column); - } - }; - - if (!columnDef.header || columnDef.visibleInShowHideMenu === false) { - return null; - } - - return ( - <> - ({ - alignItems: "center", - justifyContent: "flex-start", - my: 0, - opacity: isDragging ? 0.5 : 1, - outline: isDragging - ? `2px dashed ${theme.palette.grey[500]}` - : hoveredColumn?.id === column.id - ? `2px dashed ${draggingBorderColor}` - : "none", - outlineOffset: "-2px", - pl: `${(column.depth + 0.5) * 2}rem`, - py: "6px", - ...parseFromValuesOrFunc(rest?.sx, theme), - })} - > - - {columnDefType !== "group" && - enableColumnOrdering && - !isNestedColumns && - (columnDef.enableColumnOrdering !== false ? ( - - ) : ( - - ))} - {enableHiding ? ( - - - - } - disabled={!column.getCanHide()} - label={columnDef.header} - onChange={() => handleToggleColumnHidden(column)} - /> - ) : ( - {columnDef.header} - )} - - - {column.columns?.map((c, i) => ( - - ))} - - ); -}; -export default DataTable_ShowHideColumnsMenuItems; diff --git a/src/core/components/DataTable/toolbar/ToolbarInternalButtons.js b/src/core/components/DataTable/toolbar/ToolbarInternalButtons.js deleted file mode 100644 index 9250b89..0000000 --- a/src/core/components/DataTable/toolbar/ToolbarInternalButtons.js +++ /dev/null @@ -1,44 +0,0 @@ -import { parseFromValuesOrFunc } from "@/core/utils/utils"; -import { Box } from "@mui/material"; -import DataTable_ShowHideColumnsButton from "../buttons/ShowHideColumnsButton"; -import DataTable_ToggleFiltersButton from "../buttons/ToggleFiltersButton"; - -const DataTable_ToolbarInternalButtons = ({ table, ...rest }) => { - const { - options: { - columnFilterDisplayMode, - enableColumnFilters, - enableColumnOrdering, - enableColumnPinning, - enableFilters, - enableHiding, - renderToolbarInternalActions, - }, - } = table; - - return ( - ({ - alignItems: "center", - display: "flex", - zIndex: 3, - ...parseFromValuesOrFunc(rest?.sx, theme), - })} - > - {renderToolbarInternalActions?.({ - table, - }) ?? ( - <> - {enableFilters && enableColumnFilters && columnFilterDisplayMode !== "popover" && ( - - )} - {(enableHiding || enableColumnOrdering || enableColumnPinning) && ( - - )} - - )} - - ); -}; -export default DataTable_ToolbarInternalButtons; diff --git a/src/core/components/DataTable/toolbar/TopToolbar.js b/src/core/components/DataTable/toolbar/TopToolbar.js index 09836e2..e5dc946 100644 --- a/src/core/components/DataTable/toolbar/TopToolbar.js +++ b/src/core/components/DataTable/toolbar/TopToolbar.js @@ -2,10 +2,10 @@ import { getCommonToolbarStyles, parseFromValuesOrFunc } from "@/core/utils/util 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"; import UpdateTable from "@/core/components/DataTable/update/UpdateTable"; import ResetStorage from "@/core/components/DataTable/reset/ResetStorage"; +import HideColumn from "@/core/components/DataTable/hide"; const DataTable_TopToolbar = ({ mutate, need_filter, table, columns, table_url, user_id, page_name, table_name }) => { const { @@ -74,6 +74,11 @@ const DataTable_TopToolbar = ({ mutate, need_filter, table, columns, table_url, page_name={page_name} table_name={table_name} /> + {need_filter && ( )} - )} diff --git a/src/core/utils/flattenArrayOfObjects.js b/src/core/utils/flattenArrayOfObjects.js index 5b1564c..ec3cc23 100644 --- a/src/core/utils/flattenArrayOfObjects.js +++ b/src/core/utils/flattenArrayOfObjects.js @@ -1,10 +1,10 @@ export const flattenArrayOfObjects = (array, key) => { return array.reduce((acc, obj) => { + acc.push(obj); if (Array.isArray(obj[key])) { - acc.push(...flattenArrayOfObjects(obj[key])); - } else { - acc.push(obj); + acc.push(...flattenArrayOfObjects(obj[key], key)); } return acc; }, []); }; + diff --git a/src/lib/contexts/DataTable.js b/src/lib/contexts/DataTable.js index 85f521f..4d50a7f 100644 --- a/src/lib/contexts/DataTable.js +++ b/src/lib/contexts/DataTable.js @@ -19,44 +19,53 @@ const DataTableProvider = ({ children, user_id, page_name, table_name, columns } useEffect(() => { if (!settingStore) return; - const values = flatColumns.reduce((acc, column) => { + const filterValues = flatColumns.reduce((acc, column) => { if (!column.enableColumnFilter) return acc; - const filter = settingStore?.[user_id]?.[page_name]?.[table_name]?.["filters"]?.find( + const storeFilter = 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, + value: storeFilter?.value || [], + filterFn: storeFilter?.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, + value: storeFilter?.value || ["", ""], + filterFn: storeFilter?.filterFn || column._filterFn, datatype: column.datatype, }; } else { acc[column.id] = { id: column.id, - value: filter?.value || "", - filterFn: filter?.filterFn || column._filterFn, + value: storeFilter?.value || "", + filterFn: storeFilter?.filterFn || column._filterFn, datatype: column.datatype, }; } return acc; }, {}); - setFilterData(values); - setInitFilter(values); + setFilterData(filterValues); + setInitFilter(filterValues); setSortData(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || []); setInitSort(settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] || []); - setHideData(settingStore?.[user_id]?.[page_name]?.[table_name]?.["hides"] || {}); - setInitHide(settingStore?.[user_id]?.[page_name]?.[table_name]?.["hides"] || {}); setInitStates(true); }, [settingStore, isInitStates]); + useEffect(() => { + const hideValues = flatColumns.reduce((acc, column) => { + const columnId = column.id; + const storeHide = settingStore?.[user_id]?.[page_name]?.[table_name]?.["hides"]?.[columnId]; + acc[columnId] = storeHide !== undefined ? storeHide : true; + return acc; + }, {}); + setHideData(hideValues); + setInitHide(hideValues); + }, [settingStore, isInitStates]); + useEffect(() => { if ( JSON.stringify(initFilter) !== JSON.stringify(filterData) || From f9743637902623a5089f33ff3ff892961bbf0309 Mon Sep 17 00:00:00 2001 From: Mohammad Jalali Date: Sat, 27 Jul 2024 11:32:27 +0330 Subject: [PATCH 3/3] complete hide action part with button for hide or show all table header --- package.json | 1 + .../dashboard/inquiryPrivacyFencing/index.jsx | 51 ++++++++-- src/core/components/DataTable/Main.js | 8 +- .../components/DataTable/hide/HideBody.jsx | 27 +++-- .../DataTable/hide/HideBodyField.jsx | 98 +++++++++++++------ .../DataTable/hide/HideOrShowAll.jsx | 70 +++++++++++++ .../NotificationDesign/AskForKeepData.jsx | 6 +- src/core/utils/flattenObjectOfObjects.jsx | 12 +++ src/lib/contexts/DataTable.js | 23 +++-- 9 files changed, 239 insertions(+), 57 deletions(-) create mode 100644 src/core/components/DataTable/hide/HideOrShowAll.jsx create mode 100644 src/core/utils/flattenObjectOfObjects.jsx diff --git a/package.json b/package.json index 36ec3d1..f9593c8 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@mui/material": "^5.15.6", "@mui/material-nextjs": "^5.15.6", "@mui/x-date-pickers": "^6.19.5", + "@mui/x-tree-view": "^7.11.0", "axios": "^1.7.2", "date-fns": "^3.3.1", "date-fns-jalali": "^2.13.0-0", diff --git a/src/components/dashboard/inquiryPrivacyFencing/index.jsx b/src/components/dashboard/inquiryPrivacyFencing/index.jsx index 3ee9a28..1de7b31 100644 --- a/src/components/dashboard/inquiryPrivacyFencing/index.jsx +++ b/src/components/dashboard/inquiryPrivacyFencing/index.jsx @@ -10,25 +10,41 @@ const InquiryPrivacyFencingPage = () => { () => [ { accessorKey: "fullname", - header: "fullname", + header: "نام کامل", id: "fullname", columns: [ { accessorKey: "first_name", - header: "first_name", + header: "اسم", id: "first_name", columns: [ { - accessorKey: "first_name_first", - header: "first_name_first", - id: "first_name_first", + accessorKey: "part1", + header: "فامیل", + id: "part1", enableColumnFilter: false, datatype: "text", + columns: [ + { + accessorKey: "part1_1", + header: "part1_1", + id: "part1_1", + enableColumnFilter: false, + datatype: "text", + }, + { + accessorKey: "part1_2", + header: "part1_2", + id: "part1_2", + enableColumnFilter: false, + datatype: "text", + }, + ], }, { - accessorKey: "first_name_last", - header: "first_name_last", - id: "first_name_last", + accessorKey: "part2", + header: "part2", + id: "part2", enableColumnFilter: false, datatype: "text", }, @@ -43,6 +59,25 @@ const InquiryPrivacyFencingPage = () => { }, ], }, + { + accessorKey: "something", + header: "something", + id: "something", + columns: [ + { + accessorKey: "something1", + header: "something1", + id: "something1", + }, + { + accessorKey: "something2", + header: "something2", + id: "something2", + enableColumnFilter: false, + datatype: "text", + }, + ], + }, { accessorKey: "dabirkhaneh_number", header: "شماره دبیرخانه درخواست", diff --git a/src/core/components/DataTable/Main.js b/src/core/components/DataTable/Main.js index 4a3908b..78bf9a8 100644 --- a/src/core/components/DataTable/Main.js +++ b/src/core/components/DataTable/Main.js @@ -6,13 +6,15 @@ import useDataTable from "@/lib/hooks/useDataTable"; import { useMemo, useState } from "react"; import useRequest from "@/lib/hooks/useRequest"; import useSWR from "swr"; +import { flattenObjectOfObjects } from "@/core/utils/flattenObjectOfObjects"; const DataTable_Main = (props) => { const request = useRequest(); - const { filterData, sortData, setSortData, hideData, setHideData } = useDataTable(); + const { filterData, sortData, setSortData, hideData } = useDataTable(); const { need_filter, table_url, user_id, page_name, table_name, columns, initialStateProps, TableToolbar } = props; const flatColumns = flattenArrayOfObjects(columns, "columns"); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 }); + const flattenHideData = flattenObjectOfObjects(hideData); const onSortingChange = (event) => { setSortData(event); }; @@ -58,13 +60,13 @@ const DataTable_Main = (props) => { data: data?.data ?? [], initialState: { density: "compact", - columnVisibility: hideData, + columnVisibility: flattenHideData, sorting: sortData, ...initialStateProps, }, state: { showProgressBars: isValidating, - columnVisibility: hideData, + columnVisibility: flattenHideData, sorting: sortData, pagination, }, diff --git a/src/core/components/DataTable/hide/HideBody.jsx b/src/core/components/DataTable/hide/HideBody.jsx index adf4c12..d6e18cb 100644 --- a/src/core/components/DataTable/hide/HideBody.jsx +++ b/src/core/components/DataTable/hide/HideBody.jsx @@ -1,9 +1,10 @@ "use client"; import { Box, Drawer, styled } from "@mui/material"; -import useDataTable from "@/lib/hooks/useDataTable"; import HideBodyField from "@/core/components/DataTable/hide/HideBodyField"; import HideHeader from "@/core/components/DataTable/hide/HideHeader"; +import useDataTable from "@/lib/hooks/useDataTable"; +import HideOrShowAll from "@/core/components/DataTable/hide/HideOrShowAll"; const ScrollBox = styled(Box)({ flexGrow: 1, @@ -23,7 +24,7 @@ const ScrollBox = styled(Box)({ }); function HideBody({ columns, drawerState, setDrawerState }) { - const { hideData } = useDataTable(); + const { hideData, setHideData } = useDataTable(); return ( - {Object.keys(hideData).length > 0 && ( - - - {columns.map((column) => ())} - - - )} + + + + {columns.map((column) => ( + + ))} + + ); } -export default HideBody; +export default HideBody; \ No newline at end of file diff --git a/src/core/components/DataTable/hide/HideBodyField.jsx b/src/core/components/DataTable/hide/HideBodyField.jsx index e8123af..fe3dc2d 100644 --- a/src/core/components/DataTable/hide/HideBodyField.jsx +++ b/src/core/components/DataTable/hide/HideBodyField.jsx @@ -1,37 +1,79 @@ -import { Box, FormControlLabel, Switch, Typography } from "@mui/material"; -import useDataTable from "@/lib/hooks/useDataTable"; +import { alpha, Box, Checkbox, styled, Typography } from "@mui/material"; +import { SimpleTreeView } from "@mui/x-tree-view"; +import { TreeItem, treeItemClasses } from "@mui/x-tree-view/TreeItem"; -function HideBodyField({ column, level = 0 }) { - const { hideData, setHideData } = useDataTable(); - const paddingLeft = 2 * level; +const CustomTreeItem = styled(TreeItem)(({ theme }) => ({ + [`& .${treeItemClasses.content}`]: { + padding: theme.spacing(0.5, 0.5), + margin: theme.spacing(0.2, 0), + gap: 0, + }, + [`& .${treeItemClasses.iconContainer}`]: { + "& .close": { + opacity: 0.3, + }, + }, + [`& .${treeItemClasses.groupTransition}`]: { + marginLeft: 16, + paddingLeft: 16, + borderLeft: `1px dashed ${alpha(theme.palette.text.primary, 0.4)}`, + }, +})); - return ( - - { + setHideData(prevData => { + const updateHideData = (data, id) => { + if (data.hasOwnProperty(id)) { + return { ...data, [id]: !data[id] }; + } + const updatedData = { ...data }; + for (const key in data) { + if (data[key] && typeof data[key] === "object") { + updatedData[key] = updateHideData(data[key], id); + } + } + return updatedData; + }; + + return updateHideData(prevData, column.id); + }); + }; + + const labelContent = (() => { + if (typeof hideData[column.id] === "boolean") { + return ( + + { - setHideData(hideData => ({ - ...hideData, - [column.id]: !hideData[column.id], - })); - }} + onChange={handleCheckboxChange} name={column.id} /> - } - label={ {column.header} - } - /> - {column.columns?.map((subColumn) => ( - - ))} - + + ); + } else { + return ( + + {column.header} + + ); + } + })(); + + return ( + + + {column.columns?.map((subColumn) => ( + + ))} + + ); } diff --git a/src/core/components/DataTable/hide/HideOrShowAll.jsx b/src/core/components/DataTable/hide/HideOrShowAll.jsx new file mode 100644 index 0000000..3c9131a --- /dev/null +++ b/src/core/components/DataTable/hide/HideOrShowAll.jsx @@ -0,0 +1,70 @@ +"use client"; + +import { Box, Button } from "@mui/material"; +import VisibilityIcon from "@mui/icons-material/Visibility"; +import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; + +const setAllValues = (obj, value) => { + const result = {}; + for (const key in obj) { + if (typeof obj[key] === "object" && obj[key] !== null) { + result[key] = setAllValues(obj[key], value); + } else { + result[key] = value; + } + } + return result; +}; + +const allValuesAre = (obj, value) => { + for (const key in obj) { + if (typeof obj[key] === "object" && obj[key] !== null) { + if (!allValuesAre(obj[key], value)) { + return false; + } + } else if (obj[key] !== value) { + return false; + } + } + return true; +}; + +function HideOrShowAll({ hideData, setHideData }) { + const handleShowAll = () => { + const newHideData = setAllValues(hideData, true); + setHideData(newHideData); + }; + + const handleHideAll = () => { + const newHideData = setAllValues(hideData, false); + setHideData(newHideData); + }; + + const allHidden = allValuesAre(hideData, false); + const allVisible = allValuesAre(hideData, true); + + return ( + + + + + ); +} + +export default HideOrShowAll; \ No newline at end of file diff --git a/src/core/components/NotificationDesign/AskForKeepData.jsx b/src/core/components/NotificationDesign/AskForKeepData.jsx index 58475c1..ab8c272 100644 --- a/src/core/components/NotificationDesign/AskForKeepData.jsx +++ b/src/core/components/NotificationDesign/AskForKeepData.jsx @@ -5,9 +5,11 @@ 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"; +import { flattenObjectOfObjects } from "@/core/utils/flattenObjectOfObjects"; function AskForKeepData({ filterData, sortData, hideData, user_id, page_name, table_name, columns }) { const { filterAction, sortAction, hideAction } = useTableSetting(); + const flattenHideData = flattenObjectOfObjects(hideData); const onSaveFilter = () => { const filteredItems = Object.keys(filterData) @@ -26,7 +28,7 @@ function AskForKeepData({ filterData, sortData, hideData, user_id, page_name, ta .filter(Boolean); filterAction(user_id, page_name, table_name, filteredItems, columns); sortAction(user_id, page_name, table_name, sortData, columns); - hideAction(user_id, page_name, table_name, hideData, columns); + hideAction(user_id, page_name, table_name, flattenHideData, columns); toast.dismiss({ containerId: "datatable" }); }; @@ -75,4 +77,4 @@ function AskForKeepData({ filterData, sortData, hideData, user_id, page_name, ta ); } -export default AskForKeepData; +export default AskForKeepData; \ No newline at end of file diff --git a/src/core/utils/flattenObjectOfObjects.jsx b/src/core/utils/flattenObjectOfObjects.jsx new file mode 100644 index 0000000..3fdac84 --- /dev/null +++ b/src/core/utils/flattenObjectOfObjects.jsx @@ -0,0 +1,12 @@ +export const flattenObjectOfObjects = (obj, res = {}) => { + for (let key in obj) { + if (obj.hasOwnProperty(key)) { + if (typeof obj[key] === "object" && obj[key] !== null) { + flattenObjectOfObjects(obj[key], res); + } else { + res[key] = obj[key]; + } + } + } + return res; +}; \ No newline at end of file diff --git a/src/lib/contexts/DataTable.js b/src/lib/contexts/DataTable.js index 4d50a7f..f5a625d 100644 --- a/src/lib/contexts/DataTable.js +++ b/src/lib/contexts/DataTable.js @@ -56,12 +56,23 @@ const DataTableProvider = ({ children, user_id, page_name, table_name, columns } }, [settingStore, isInitStates]); useEffect(() => { - const hideValues = flatColumns.reduce((acc, column) => { - const columnId = column.id; - const storeHide = settingStore?.[user_id]?.[page_name]?.[table_name]?.["hides"]?.[columnId]; - acc[columnId] = storeHide !== undefined ? storeHide : true; - return acc; - }, {}); + const getHideValues = (columns) => { + return columns.reduce((acc, column) => { + const columnId = column.id; + const storeHide = settingStore?.[user_id]?.[page_name]?.[table_name]?.["hides"]?.[columnId]; + + // If the column has nested columns, process them recursively + if (column.columns && column.columns.length > 0) { + acc[columnId] = getHideValues(column.columns); + } else { + acc[columnId] = storeHide !== undefined ? storeHide : true; + } + + return acc; + }, {}); + }; + + const hideValues = getHideValues(columns); setHideData(hideValues); setInitHide(hideValues); }, [settingStore, isInitStates]);