Feature/user management

This commit is contained in:
AmirHossein Mahmoodi
2025-05-10 05:31:24 +00:00
parent bc59952f7b
commit f07d27b273
101 changed files with 7136 additions and 14 deletions

View File

@@ -0,0 +1,211 @@
import { createContext, useEffect, useState } from "react";
import { toast } from "react-toastify";
import useTableSetting from "@/lib/hooks/useTableSetting";
import { flattenArrayOfObjects } from "@/core/utils/flattenArrayOfObjects";
import AskForKeepData from "@/core/components/NotificationDesign/AskForKeepData";
import { LinearProgress } from "@mui/material";
export const DataTableContext = createContext();
const DataTableProvider = ({
children,
user_id,
page_name,
table_name,
columns,
initialSort = [],
}) => {
const { settingStore } = useTableSetting();
const [isInitStates, setInitStates] = useState(false);
const flatColumns = flattenArrayOfObjects(columns, "columns");
const [initFilter, setInitFilter] = useState({});
const [filterData, setFilterData] = useState({});
const [initSort, setInitSort] = useState(initialSort || []);
const [sortData, setSortData] = useState(initialSort || []);
const [initHide, setInitHide] = useState({});
const [hideData, setHideData] = useState({});
const [isDirty, setIsDirty] = useState(false);
const [isAnyDirty, setIsAnyDirty] = useState(false);
useEffect(() => {
let hasAnyConflict = false;
const hasConflict =
JSON.stringify(hideData) !== JSON.stringify(initHide) ||
JSON.stringify(sortData) !== JSON.stringify(initSort) ||
JSON.stringify(filterData) !== JSON.stringify(initFilter);
const CheckFilterValues = Object.values(filterData).every((innerObject) => {
if (
innerObject.datatype === "date" &&
innerObject.filterMode === "between"
) {
return (
Array.isArray(innerObject.value) &&
innerObject.value.length === 2 &&
innerObject.value.every((v) => v === "")
);
} else {
return innerObject.value === "";
}
});
const flattenObject = (obj) => {
let result = {};
for (const key in obj) {
if (typeof obj[key] === "object" && obj[key] !== null) {
Object.assign(result, flattenObject(obj[key])); // باز کردن اشیای تودرتو
} else {
result[key] = obj[key];
}
}
return result;
};
// تبدیل آبجکت به آبجکت فلت شده
const flatHideData = flattenObject(hideData);
const CheckHideValues = Object.values(flatHideData).every(
(value) => value === true,
);
hasAnyConflict =
!CheckHideValues ||
JSON.stringify(sortData) !== JSON.stringify(initialSort) ||
!CheckFilterValues;
setIsDirty(hasConflict);
setIsAnyDirty(hasAnyConflict);
}, [hideData, sortData, filterData, initHide, initSort, initFilter]);
useEffect(() => {
if (!settingStore) return;
const filterValues = flatColumns.reduce((acc, column) => {
if (!column.enableColumnFilter) return acc;
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: storeFilter?.value || [],
filterMode: storeFilter?.filterMode || column.filterMode,
datatype: column.datatype,
};
} else if (column.filterMode === "between") {
acc[column.id] = {
id: column.id,
value: storeFilter?.value || ["", ""],
filterMode: storeFilter?.filterMode || column.filterMode,
datatype: column.datatype,
};
} else {
acc[column.id] = {
id: column.id,
value: storeFilter?.value || "",
filterMode: storeFilter?.filterMode || column.filterMode,
datatype: column.datatype,
};
}
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 (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);
setFilterData(filterValues);
setInitFilter(filterValues);
setSortData(
settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] ||
initialSort,
);
setInitSort(
settingStore?.[user_id]?.[page_name]?.[table_name]?.["sorts"] ||
initialSort,
);
setInitStates(true);
}, [settingStore]);
useEffect(() => {
if (
JSON.stringify(initFilter) !== JSON.stringify(filterData) ||
JSON.stringify(initSort) !== JSON.stringify(sortData) ||
JSON.stringify(initHide) !== JSON.stringify(hideData)
) {
if (!toast.isActive("keep_data", "filtering")) {
toast(
<AskForKeepData
filterData={filterData}
sortData={sortData}
hideData={hideData}
user_id={user_id}
page_name={page_name}
table_name={table_name}
columns={flatColumns}
/>,
{
containerId: "filtering",
toastId: "keep_data",
className: "filter-toast",
position: "bottom-left",
draggable: true,
autoClose: false,
},
);
} else {
toast.update("keep_data", {
containerId: "filtering",
toastId: "keep_data",
render: (
<AskForKeepData
filterData={filterData}
sortData={sortData}
hideData={hideData}
user_id={user_id}
page_name={page_name}
table_name={table_name}
columns={flatColumns}
/>
),
});
}
} else {
toast.dismiss({ containerId: "datatable" });
}
}, [filterData, sortData, hideData]);
if (!isInitStates) return <LinearProgress sx={{ borderRadius: 2 }} />;
return (
<DataTableContext.Provider
value={{
filterData,
setFilterData,
sortData,
setSortData,
hideData,
setHideData,
isDirty,
isAnyDirty,
}}
>
{children}
</DataTableContext.Provider>
);
};
export default DataTableProvider;

View File

@@ -24,6 +24,7 @@ export const SocketProvider = ({ children }) => {
const { user } = useAuth();
const socketToastId = useRef(null);
const [status, setStatus] = useState("");
const [clientsOnline, setClientsOnline] = useState([]);
const socket = useMemo(() => {
return io(`${process.env.NEXT_PUBLIC_SERVER_SOCKET_URL}`, {
@@ -35,7 +36,7 @@ export const SocketProvider = ({ children }) => {
useEffect(() => {
if (user?.telephone_id) {
socket.auth.token = user.telephone_id;
socket.auth.token = { telephone_id: user.telephone_id, user_id: user.id };
}
}, [user, socket]);
@@ -102,7 +103,21 @@ export const SocketProvider = ({ children }) => {
};
}, [socket]);
const contextValue = useMemo(() => ({ socket, status }), [socket, status]);
useEffect(() => {
const handleNotification = (data) => {
setClientsOnline(data.clients);
};
socket.on("onlineClientsUpdated", handleNotification);
return () => {
socket.off("onlineClientsUpdated", handleNotification);
};
}, [socket]);
const contextValue = useMemo(
() => ({ socket, status, clientsOnline }),
[socket, status, clientsOnline],
);
return (
<SocketContext.Provider value={contextValue}>

View File

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

View File

@@ -0,0 +1,29 @@
import { useEffect, useState } from "react";
import { GET_PROVINCE_LISTS } from "@/core/utils/routes";
import useRequest from "@/lib/hooks/useRequest";
const useProvinces = () => {
const requestServer = useRequest();
const [provinces, setProvinces] = useState([]);
const [loadingProvinces, setLoadingProvinces] = useState(true);
const [errorProvinces, setErrorProvinces] = useState(null);
useEffect(() => {
const fetchProvinces = async () => {
try {
const response = await requestServer(`${GET_PROVINCE_LISTS}`);
setProvinces(response.data.data);
setLoadingProvinces(false);
} catch (e) {
setErrorProvinces(e);
setLoadingProvinces(false);
}
};
fetchProvinces();
}, []);
return { provinces, loadingProvinces, errorProvinces };
};
export default useProvinces;

29
src/lib/hooks/useRoles.js Normal file
View File

@@ -0,0 +1,29 @@
import { useEffect, useState } from "react";
import { GET_ROLE_LISTS } from "@/core/utils/routes";
import useRequest from "@/lib/hooks/useRequest";
const useRoles = () => {
const requestServer = useRequest();
const [roles, setRoles] = useState([]);
const [loadingRoles, setLoadingRoles] = useState(true);
const [errorRoles, setErrorRoles] = useState(null);
useEffect(() => {
const fetchRoles = async () => {
try {
const response = await requestServer(`${GET_ROLE_LISTS}`);
setRoles(response.data.data);
setLoadingRoles(false);
} catch (e) {
setErrorRoles(e);
setLoadingRoles(false);
}
};
fetchRoles();
}, []);
return { roles, loadingRoles, errorRoles };
};
export default useRoles;

View File

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