Feature/migration
This commit is contained in:
@@ -1,97 +0,0 @@
|
||||
import {FA_DATATABLE_LOCALIZATION} from "&/locales/fa/datatable";
|
||||
import {useRouter} from "next/router";
|
||||
import {createContext, useEffect, useState} from "react";
|
||||
import useUser from "../hooks/useUser";
|
||||
|
||||
export const LanguageContext = createContext();
|
||||
|
||||
export const LanguageProvider = ({children}) => {
|
||||
const router = useRouter();
|
||||
const languageList = [
|
||||
{
|
||||
key: "fa",
|
||||
dir: "rtl",
|
||||
name: "فارسی",
|
||||
fontFamily: `IRANSans, sans-serif`,
|
||||
tableLocalization: FA_DATATABLE_LOCALIZATION,
|
||||
}
|
||||
];
|
||||
const {user, userChangedLanguage, changeLanguageState} = useUser();
|
||||
const [languageIsReady, setLanguageIsReady] = useState(false);
|
||||
const [languageApp, setLanguageApp] = useState(process.env.NEXT_PUBLIC_DEFAULT_LANGUAGE);
|
||||
const [directionApp, setDirectionApp] = useState(
|
||||
process.env.NEXT_PUBLIC_DEFAULT_DIRECTION
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const lang = localStorage.getItem("_language");
|
||||
|
||||
if (lang) {
|
||||
setLanguageApp(lang);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!languageApp) return;
|
||||
|
||||
const lang = localStorage.getItem("_language");
|
||||
|
||||
if (!lang) {
|
||||
localStorage.setItem("_language", languageApp);
|
||||
} else {
|
||||
if (languageApp != lang) {
|
||||
localStorage.setItem("_language", languageApp);
|
||||
}
|
||||
}
|
||||
}, [languageApp]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!router.isReady) return;
|
||||
if (user.user_language) {
|
||||
if (user.user_language != languageApp) {
|
||||
setLanguageApp(user.user_language);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (languageApp != router.locale) {
|
||||
router.replace(
|
||||
{pathname: router.pathname, query: router.query},
|
||||
router.asPath,
|
||||
{
|
||||
locale: languageApp,
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
for (const lang of languageList) {
|
||||
if (languageApp != lang.key) continue;
|
||||
setDirectionApp(lang.dir);
|
||||
document.dir = lang.dir;
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
changeLanguageState(false);
|
||||
setLanguageIsReady(true);
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}, [router.locale, router.isReady, userChangedLanguage, languageApp]);
|
||||
|
||||
return (
|
||||
<LanguageContext.Provider
|
||||
value={{
|
||||
languageApp,
|
||||
setLanguageApp,
|
||||
directionApp,
|
||||
languageIsReady,
|
||||
setLanguageIsReady,
|
||||
languageList,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</LanguageContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
import LoadingHardPage from "@/core/components/LoadingHardPage";
|
||||
import {createContext, useState} from "react";
|
||||
|
||||
export const LoadingContext = createContext();
|
||||
|
||||
export const LoadingProvider = ({children}) => {
|
||||
const [loadingPage, setLoadingPage] = useState(false);
|
||||
return (
|
||||
<LoadingContext.Provider value={{loadingPage, setLoadingPage}}>
|
||||
<LoadingHardPage loading={loadingPage}/>
|
||||
{children}
|
||||
</LoadingContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,75 +0,0 @@
|
||||
import {createContext, useEffect, useMemo, useRef, useState} from "react";
|
||||
import {io} from "socket.io-client";
|
||||
import useUser from "@/lib/app/hooks/useUser";
|
||||
import {useTranslations} from "next-intl";
|
||||
import {toast} from "react-toastify";
|
||||
import PowerIcon from "@mui/icons-material/Power";
|
||||
import PowerOffIcon from "@mui/icons-material/PowerOff";
|
||||
import useDirection from "@/lib/app/hooks/useDirection";
|
||||
|
||||
export const SocketContext = createContext()
|
||||
|
||||
export const SocketProvider = ({children}) => {
|
||||
const {user, isAuth} = useUser()
|
||||
const [connectionError, setConnectionError] = useState(false)
|
||||
const socketToastId = useRef(null);
|
||||
const t = useTranslations()
|
||||
const {directionApp} = useDirection();
|
||||
const socket = useMemo(() => io(process.env.NEXT_PUBLIC_SERVER_SOCKET_URL, {
|
||||
autoConnect: false,
|
||||
// transports: ['websocket'],
|
||||
auth: {
|
||||
token: ""
|
||||
}
|
||||
}), [])
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuth) {
|
||||
socket.auth.token = user.telephone_id
|
||||
return
|
||||
}
|
||||
socket.auth.token = ''
|
||||
}, [isAuth]);
|
||||
|
||||
useEffect(() => {
|
||||
const connectErrorHandler = () => {
|
||||
setConnectionError(true)
|
||||
}
|
||||
const connectHandler = () => {
|
||||
setConnectionError(false)
|
||||
}
|
||||
socket.on("connect_error", connectErrorHandler);
|
||||
socket.on("connect", connectHandler);
|
||||
|
||||
return () => {
|
||||
socket.off("connect_error", connectErrorHandler);
|
||||
socket.off("connect", connectHandler);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!connectionError) {
|
||||
toast.update(socketToastId.current, {
|
||||
type: toast.TYPE.SUCCESS,
|
||||
render: t('socket_is_connect_message'),
|
||||
autoClose: 2000,
|
||||
closeButton: true,
|
||||
closeOnClick: true,
|
||||
icon: <PowerIcon/>
|
||||
});
|
||||
return
|
||||
}
|
||||
socketToastId.current = toast.warn(t('socket_is_not_connect_message'), {
|
||||
containerId: 'connection',
|
||||
draggable: false,
|
||||
autoClose: false, closeButton: false, closeOnClick: false, icon: <PowerOffIcon/>
|
||||
})
|
||||
|
||||
}, [connectionError]);
|
||||
|
||||
return (
|
||||
<SocketContext.Provider value={{socket}}>
|
||||
{children}
|
||||
</SocketContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import {createContext, useReducer} from "react";
|
||||
import {toast} from "react-toastify";
|
||||
|
||||
export const ToastContext = createContext()
|
||||
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case "PUSH":
|
||||
return {
|
||||
...state,
|
||||
[action.toast_type]: [...state[action.toast_type], action.toast_id]
|
||||
};
|
||||
case "DISMISS":
|
||||
action.toast_type.map((item) => {
|
||||
state[item].map((id) => {
|
||||
toast.dismiss(id);
|
||||
})
|
||||
state[item] = []
|
||||
});
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export const ToastProvider = ({children}) => {
|
||||
const [state, dispatch] = useReducer(reducer, {
|
||||
pending: [],
|
||||
error: [],
|
||||
warning: [],
|
||||
success: []
|
||||
});
|
||||
|
||||
return (
|
||||
<ToastContext.Provider value={{state, dispatch}}>
|
||||
{children}
|
||||
</ToastContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,125 +0,0 @@
|
||||
import {GET_USER} from "@/core/data/apiRoutes";
|
||||
import axios from "axios";
|
||||
import {createContext, useCallback, useEffect, useReducer} from "react";
|
||||
|
||||
const initialUser = {
|
||||
isAuth: false,
|
||||
userChangedLanguage: false,
|
||||
token: null,
|
||||
user: {},
|
||||
};
|
||||
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case "CLEAR_USER":
|
||||
return {...state, user: {}};
|
||||
case "CHANGE_USER":
|
||||
return {...state, user: action.user};
|
||||
case "CHANGE_USER_LANGUAGE":
|
||||
return {
|
||||
...state,
|
||||
user: {...state.user, user_language: action.language},
|
||||
};
|
||||
case "CHANGE_AUTH_STATE":
|
||||
return {...state, isAuth: action.isAuth};
|
||||
case "CHANGE_LANGUAGE_STATE":
|
||||
return {...state, userChangedLanguage: action.userChangedLanguage};
|
||||
case "CLEAR_TOKEN":
|
||||
localStorage.removeItem("_token");
|
||||
return {...state, token: null};
|
||||
case "SET_TOKEN":
|
||||
localStorage.setItem("_token", action.token);
|
||||
return {...state, token: action.token};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export const UserContext = createContext();
|
||||
|
||||
export const UserProvider = ({children}) => {
|
||||
const [state, dispatch] = useReducer(reducer, initialUser);
|
||||
|
||||
const clearUser = useCallback(() => {
|
||||
dispatch({type: "CLEAR_USER"});
|
||||
}, []);
|
||||
|
||||
const changeUser = useCallback((user) => {
|
||||
dispatch({type: "CHANGE_USER", user});
|
||||
}, []);
|
||||
|
||||
const changeUserLanguage = useCallback(/* use in multi language app */);
|
||||
|
||||
const changeAuthState = useCallback((isAuth) => {
|
||||
dispatch({type: "CHANGE_AUTH_STATE", isAuth});
|
||||
}, []);
|
||||
|
||||
const changeLanguageState = useCallback((userChangedLanguage) => {
|
||||
dispatch({type: "CHANGE_LANGUAGE_STATE", userChangedLanguage});
|
||||
}, []);
|
||||
|
||||
const clearToken = useCallback(() => {
|
||||
dispatch({type: "CLEAR_TOKEN"});
|
||||
}, []);
|
||||
|
||||
const setToken = useCallback((token) => {
|
||||
dispatch({type: "SET_TOKEN", token});
|
||||
}, []);
|
||||
|
||||
const getUser = useCallback(
|
||||
(callback = () => {
|
||||
}) => {
|
||||
axios
|
||||
.get(GET_USER, {
|
||||
headers: {authorization: `Bearer ${state.token}`},
|
||||
})
|
||||
.then(({data}) => {
|
||||
if (typeof callback === "function") callback(data.data);
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.response.status === 401) clearToken()
|
||||
})
|
||||
},
|
||||
[state.token]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const localToken = localStorage.getItem("_token");
|
||||
if (localToken) dispatch({type: "SET_TOKEN", token: localToken});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!state.token) {
|
||||
clearUser();
|
||||
changeAuthState(false);
|
||||
changeLanguageState(false);
|
||||
return;
|
||||
}
|
||||
getUser((data) => {
|
||||
changeUser(data);
|
||||
changeAuthState(true);
|
||||
changeLanguageState(true);
|
||||
});
|
||||
}, [state.token]);
|
||||
|
||||
return (
|
||||
<UserContext.Provider
|
||||
value={{
|
||||
isAuth: state.isAuth,
|
||||
userChangedLanguage: state.userChangedLanguage,
|
||||
token: state.token,
|
||||
user: state.user,
|
||||
getUser,
|
||||
clearUser,
|
||||
changeUser,
|
||||
changeUserLanguage,
|
||||
changeAuthState,
|
||||
changeLanguageState,
|
||||
clearToken,
|
||||
setToken,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</UserContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
import {useContext} from "react";
|
||||
import {LanguageContext} from "../contexts/language";
|
||||
|
||||
const useDirection = () => {
|
||||
const {directionApp} = useContext(LanguageContext);
|
||||
|
||||
return {directionApp};
|
||||
};
|
||||
|
||||
export default useDirection;
|
||||
@@ -1,28 +0,0 @@
|
||||
import {useContext} from "react";
|
||||
import {LanguageContext} from "../contexts/language";
|
||||
import useUser from "./useUser";
|
||||
|
||||
const useLanguage = () => {
|
||||
const {isAuth, changeUserLanguage} = useUser();
|
||||
const {
|
||||
languageApp,
|
||||
setLanguageApp,
|
||||
languageIsReady,
|
||||
setLanguageIsReady,
|
||||
languageList,
|
||||
} = useContext(LanguageContext);
|
||||
|
||||
const changeLanguage = (lang) => {
|
||||
if (lang == languageApp) return;
|
||||
|
||||
setLanguageIsReady(false);
|
||||
setLanguageApp(lang);
|
||||
if (isAuth) {
|
||||
changeUserLanguage(lang);
|
||||
}
|
||||
};
|
||||
|
||||
return {languageApp, changeLanguage, languageIsReady, languageList};
|
||||
};
|
||||
|
||||
export default useLanguage;
|
||||
@@ -1,10 +0,0 @@
|
||||
import {useContext} from "react";
|
||||
import {LoadingContext} from "../contexts/loading";
|
||||
|
||||
const useLoading = () => {
|
||||
const {setLoadingPage} = useContext(LoadingContext);
|
||||
|
||||
return {setLoadingPage};
|
||||
};
|
||||
|
||||
export default useLoading;
|
||||
@@ -1,34 +0,0 @@
|
||||
import {useEffect, useState} from "react";
|
||||
|
||||
function useNetwork() {
|
||||
const [state, setState] = useState(() => {
|
||||
return {
|
||||
online: navigator.onLine,
|
||||
};
|
||||
});
|
||||
useEffect(() => {
|
||||
const handleOnline = () => {
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
online: true,
|
||||
}));
|
||||
};
|
||||
const handleOffline = () => {
|
||||
setState((prevState) => ({
|
||||
...prevState,
|
||||
online: false,
|
||||
}));
|
||||
};
|
||||
|
||||
window.addEventListener("online", handleOnline);
|
||||
window.addEventListener("offline", handleOffline);
|
||||
return () => {
|
||||
window.removeEventListener("online", handleOnline);
|
||||
window.removeEventListener("offline", handleOffline);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
export default useNetwork;
|
||||
@@ -1,26 +0,0 @@
|
||||
import useSWR from 'swr'
|
||||
import {GET_SIDEBAR_NOTIFICATION} from "@/core/data/apiRoutes";
|
||||
import useRequest from "@/lib/app/hooks/useRequest";
|
||||
const GLOBAL_HAS_VALUE = process.env.NEXT_PUBLIC_HAS_VALUE;
|
||||
const hasPermissionsValue = GLOBAL_HAS_VALUE ? JSON.parse(GLOBAL_HAS_VALUE) : [];
|
||||
const isNotificationEnabled = hasPermissionsValue.includes("NEXT_PUBLIC_HAS_NOTIFICATION");
|
||||
|
||||
const useNotification = () => {
|
||||
|
||||
const requestServer = useRequest({auth: true, notification: false})
|
||||
|
||||
//swr config
|
||||
const fetcher = (...args) => {
|
||||
return requestServer(args, 'get').then((response) => {
|
||||
return response.data.data;
|
||||
}).catch(() => {
|
||||
})
|
||||
};
|
||||
|
||||
const {data, mutate} = useSWR( isNotificationEnabled ? GET_SIDEBAR_NOTIFICATION : '', fetcher , {keepPreviousData:true})
|
||||
//swr config
|
||||
|
||||
// render data
|
||||
return {notification_count: data, update_notification: mutate}
|
||||
}
|
||||
export default useNotification
|
||||
@@ -1,26 +0,0 @@
|
||||
import useRequest from "@/lib/app/hooks/useRequest";
|
||||
import useSWR from "swr";
|
||||
import {GET_PERMISSIONS_LIST} from "@/core/data/apiRoutes";
|
||||
|
||||
const usePermissions = () => {
|
||||
const requestServer = useRequest({auth: true, notification: false})
|
||||
const fetcher = (...args) => {
|
||||
return requestServer(args, 'get').then((response) => {
|
||||
return response.data.data;
|
||||
}).catch(() => {
|
||||
})
|
||||
};
|
||||
|
||||
const {data, isLoading} = useSWR(GET_PERMISSIONS_LIST, fetcher, {
|
||||
revalidateIfStale: false,
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
keepPreviousData : true
|
||||
})
|
||||
const permissions_list = data
|
||||
//swr config
|
||||
|
||||
// render data
|
||||
return {permissions_list, isLoading}
|
||||
}
|
||||
export default usePermissions;
|
||||
@@ -1,30 +0,0 @@
|
||||
import {GET_PROVINCE_LIST} from "@/core/data/apiRoutes";
|
||||
import useRequest from "@/lib/app/hooks/useRequest";
|
||||
import useSWR from "swr";
|
||||
|
||||
const useProvince = () => {
|
||||
const requestServer = useRequest({auth: true, notification: false})
|
||||
|
||||
//swr config
|
||||
const fetcher = (...args) => {
|
||||
return requestServer(args, 'get').then(({data}) => {
|
||||
return data.data;
|
||||
}).catch(() => {
|
||||
})
|
||||
};
|
||||
|
||||
const {data, isLoading} = useSWR(GET_PROVINCE_LIST, fetcher, {
|
||||
revalidateIfStale: false,
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
keepPreviousData: true
|
||||
});
|
||||
|
||||
return {
|
||||
provinceList: data,
|
||||
isLoadingProvinceList: isLoading,
|
||||
errorProvinceList: !data
|
||||
}
|
||||
};
|
||||
|
||||
export default useProvince;
|
||||
@@ -1,71 +0,0 @@
|
||||
import axios from "axios";
|
||||
import {successRequest} from "@/core/utils/succesHandler";
|
||||
import {useTranslations} from "next-intl";
|
||||
import useUser from "@/lib/app/hooks/useUser";
|
||||
import {errorRequest, errorResponse, errorSetting} from "@/core/utils/errorHandler";
|
||||
import useNetwork from "@/lib/app/hooks/useNetwork";
|
||||
import Notifications from "@/core/components/notifications";
|
||||
import useToast from "@/lib/app/hooks/useToast";
|
||||
|
||||
const defaultOptions = {
|
||||
auth: false, data: {}, requestOptions: {
|
||||
headers: {}
|
||||
}, notification: true, pending: true, success: {
|
||||
notification: {
|
||||
show: true,
|
||||
},
|
||||
}, failed: {
|
||||
notification: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
const useRequest = (initOptions) => {
|
||||
const network = useNetwork()
|
||||
const t = useTranslations()
|
||||
const {token, clearToken} = useUser()
|
||||
const {pushToastList, dismissToastList} = useToast();
|
||||
let _options = {...defaultOptions, ...initOptions}
|
||||
|
||||
function requestServer(url = '', method = 'get', options) {
|
||||
_options = {..._options, ...options}
|
||||
if (_options.auth) _options = {
|
||||
..._options, requestOptions: {
|
||||
..._options.requestOptions,
|
||||
headers: {..._options.requestOptions.headers, authorization: `Bearer ${token}`}
|
||||
}
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!network.online) {
|
||||
reject()
|
||||
return
|
||||
}
|
||||
if (_options.notification && _options.failed.notification.show && _options.pending) {
|
||||
dismissToastList(["pending", "warning", "error", "success"]);
|
||||
Notifications(pushToastList, "pending", t);
|
||||
}
|
||||
|
||||
axios({
|
||||
url: url, method: method, data: _options.data, ..._options.requestOptions
|
||||
})
|
||||
.then(response => {
|
||||
successRequest(pushToastList, dismissToastList, response, t, _options)
|
||||
resolve(response)
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.response) {
|
||||
errorResponse(pushToastList, dismissToastList, error.response, clearToken, t, _options.notification && _options.failed.notification.show)
|
||||
} else if (error.request) {
|
||||
errorRequest(dismissToastList, t, _options.notification && _options.failed.notification.show)
|
||||
} else {
|
||||
errorSetting(dismissToastList, t, _options.notification && _options.failed.notification.show)
|
||||
}
|
||||
reject(error)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return requestServer
|
||||
}
|
||||
|
||||
export default useRequest
|
||||
@@ -1,30 +0,0 @@
|
||||
import {GET_ROLE_LIST} from "@/core/data/apiRoutes";
|
||||
import useRequest from "@/lib/app/hooks/useRequest";
|
||||
import useSWR from "swr";
|
||||
|
||||
const useRole = () => {
|
||||
const requestServer = useRequest({auth: true, notification: false})
|
||||
|
||||
//swr config
|
||||
const fetcher = (...args) => {
|
||||
return requestServer(args, 'get').then(({data}) => {
|
||||
return data.data;
|
||||
}).catch(() => {
|
||||
})
|
||||
};
|
||||
|
||||
const {data, isLoading} = useSWR(GET_ROLE_LIST, fetcher, {
|
||||
revalidateIfStale: false,
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
keepPreviousData: true
|
||||
});
|
||||
|
||||
return {
|
||||
roleList: data,
|
||||
isLoadingRoleList: isLoading,
|
||||
errorRoleList: !data
|
||||
}
|
||||
};
|
||||
|
||||
export default useRole;
|
||||
@@ -1,10 +0,0 @@
|
||||
import {useContext} from "react";
|
||||
import {SocketContext} from "@/lib/app/contexts/socket";
|
||||
|
||||
const useSocket = () => {
|
||||
const {socket} = useContext(SocketContext)
|
||||
|
||||
return socket
|
||||
}
|
||||
|
||||
export default useSocket
|
||||
@@ -1,21 +0,0 @@
|
||||
import {useContext} from "react";
|
||||
import {ToastContext} from "@/lib/app/contexts/toast";
|
||||
|
||||
const useToast = () => {
|
||||
const {dispatch} = useContext(ToastContext);
|
||||
|
||||
const pushToastList = (toast_type, toast_id) => {
|
||||
dispatch({type: "PUSH", toast_type, toast_id});
|
||||
};
|
||||
|
||||
const dismissToastList = (toast_type) => {
|
||||
dispatch({type: "DISMISS", toast_type});
|
||||
};
|
||||
|
||||
return {
|
||||
pushToastList,
|
||||
dismissToastList
|
||||
}
|
||||
};
|
||||
|
||||
export default useToast;
|
||||
@@ -1,34 +0,0 @@
|
||||
import {useContext} from "react";
|
||||
import {UserContext} from "../contexts/user";
|
||||
|
||||
const useUser = () => {
|
||||
const {
|
||||
isAuth,
|
||||
userChangedLanguage,
|
||||
token,
|
||||
user,
|
||||
getUser,
|
||||
clearUser,
|
||||
changeUser,
|
||||
changeAuthState,
|
||||
changeLanguageState,
|
||||
clearToken,
|
||||
setToken,
|
||||
} = useContext(UserContext);
|
||||
|
||||
return {
|
||||
isAuth,
|
||||
userChangedLanguage,
|
||||
token,
|
||||
user,
|
||||
getUser,
|
||||
clearUser,
|
||||
changeUser,
|
||||
changeAuthState,
|
||||
changeLanguageState,
|
||||
clearToken,
|
||||
setToken,
|
||||
};
|
||||
};
|
||||
|
||||
export default useUser;
|
||||
@@ -1,87 +0,0 @@
|
||||
import {createContext, useEffect, useReducer, useState} from "react";
|
||||
import {GET_CATEGORY} from "@/core/data/apiRoutes";
|
||||
import useRequest from "@/lib/app/hooks/useRequest";
|
||||
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case "CHANGE_ACTIVE_TAB":
|
||||
return state.map((answer, index) =>
|
||||
action.newValue === index
|
||||
? {...answer, active: true}
|
||||
: {
|
||||
...answer,
|
||||
active: false,
|
||||
}
|
||||
);
|
||||
case "CHANGE_LIST":
|
||||
return [...action.newList];
|
||||
case "CHANGE_ACTIVE_ID":
|
||||
return state.map((answer, index) =>
|
||||
action.tab_id === answer.id
|
||||
? {
|
||||
...answer,
|
||||
active_category_id: action.category_id,
|
||||
}
|
||||
: {...answer}
|
||||
);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const submission = (handleCategories, operation) => {
|
||||
switch (operation.type) {
|
||||
case "SET_CATEGORIES":
|
||||
return {...handleCategories, categoryLists: operation.categoriesByAPI};
|
||||
case "SET_SUBCATEGORIES":
|
||||
return {...handleCategories, subCategoryLists: operation.subCategoriesByAPI};
|
||||
default:
|
||||
return handleCategories;
|
||||
}
|
||||
};
|
||||
|
||||
export const AnswersContext = createContext();
|
||||
export const AnswersProvider = ({children}) => {
|
||||
const requestServer = useRequest({auth: true, notification: false});
|
||||
const [openCallDialog, setOpenCallDialog] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
const [handleCategories, operation] = useReducer(submission, {
|
||||
categoryLists: [],
|
||||
subCategoryLists: [],
|
||||
});
|
||||
const [state, dispatch] = useReducer(reducer, []);
|
||||
|
||||
useEffect(() => {
|
||||
requestServer(GET_CATEGORY, "get")
|
||||
.then(({data}) => {
|
||||
const subCategoriesByAPI = data.data;
|
||||
const categoriesByAPI = subCategoriesByAPI.reduce((accumulator, currentValue) => {
|
||||
if (!accumulator.find((item) => item.category_id === currentValue.category_id)) {
|
||||
accumulator.push(currentValue);
|
||||
}
|
||||
return accumulator;
|
||||
}, []);
|
||||
operation({type: "SET_CATEGORIES", categoriesByAPI});
|
||||
operation({type: "SET_SUBCATEGORIES", subCategoriesByAPI});
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AnswersContext.Provider
|
||||
value={{
|
||||
openCallDialog,
|
||||
setOpenCallDialog,
|
||||
state,
|
||||
dispatch,
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
categoryLists: handleCategories.categoryLists,
|
||||
subCategoryLists: handleCategories.subCategoryLists,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AnswersContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,59 +0,0 @@
|
||||
import { createContext, useCallback, useEffect, useReducer } from "react";
|
||||
import useRequest from "@/lib/app/hooks/useRequest";
|
||||
import { GET_CATEGORY } from "@/core/data/apiRoutes";
|
||||
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case "CHANGE_ACTIVE_ID":
|
||||
return { ...state, active_id: action.id };
|
||||
case "SET_CATEGORIES":
|
||||
return { ...state, categoryLists: action.cat };
|
||||
case "SET_SUBCATEGORIES":
|
||||
return { ...state, subCategoryLists: action.subCat };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
export const CategoriesContext = createContext();
|
||||
|
||||
export const CategoriesProvider = ({ children }) => {
|
||||
const requestServer = useRequest({ auth: true, notification: false });
|
||||
const [state, dispatch] = useReducer(reducer, {
|
||||
categoryLists: [],
|
||||
subCategoryLists: [],
|
||||
active_id: null,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
requestServer(GET_CATEGORY, "get")
|
||||
.then(({ data }) => {
|
||||
const subCat = data.data;
|
||||
const cat = subCat.reduce((accumulator, currentValue) => {
|
||||
if (!accumulator.find((item) => item.category_id === currentValue.category_id)) {
|
||||
accumulator.push(currentValue);
|
||||
}
|
||||
return accumulator;
|
||||
}, []);
|
||||
dispatch({ type: "SET_CATEGORIES", cat });
|
||||
dispatch({ type: "SET_SUBCATEGORIES", subCat });
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
const setCategoryID = useCallback((id) => {
|
||||
dispatch({ type: "CHANGE_ACTIVE_ID", id });
|
||||
});
|
||||
|
||||
return (
|
||||
<CategoriesContext.Provider
|
||||
value={{
|
||||
categoryLists: state.categoryLists,
|
||||
subCategoryLists: state.subCategoryLists,
|
||||
active_id: state.active_id,
|
||||
setCategoryID,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</CategoriesContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -1,46 +0,0 @@
|
||||
import { useContext } from "react";
|
||||
import { AnswersContext } from "@/lib/callWidget/contexts/answers";
|
||||
|
||||
const useAnswers = () => {
|
||||
const { state, dispatch, activeTab, setActiveTab } = useContext(AnswersContext);
|
||||
|
||||
const changeActiveTabAnswers = (newValue) => {
|
||||
dispatch({ type: "CHANGE_ACTIVE_TAB", newValue });
|
||||
setActiveTab(newValue);
|
||||
};
|
||||
const newAnswerTab = (data) => {
|
||||
const newAnswer = {
|
||||
id: data.id,
|
||||
phone_number: data.phone_number,
|
||||
date: new Date(),
|
||||
active: true,
|
||||
active_category_id: null,
|
||||
};
|
||||
const newList = [...state, newAnswer];
|
||||
dispatch({ type: "CHANGE_LIST", newList });
|
||||
changeActiveTabAnswers(newList.length - 1);
|
||||
};
|
||||
|
||||
const closeAnswerTab = (id) => {
|
||||
const index = state.findIndex((answer) => answer.id === id);
|
||||
const newIndex = index === 0 ? 0 : index - 1;
|
||||
const newList = [...state];
|
||||
newList.splice(index, 1);
|
||||
if (newList.length !== 0) {
|
||||
newList[newIndex].active = true;
|
||||
setActiveTab(newIndex);
|
||||
}
|
||||
dispatch({ type: "CHANGE_LIST", newList });
|
||||
};
|
||||
|
||||
return {
|
||||
answersList: state,
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
changeActiveTabAnswers,
|
||||
closeAnswerTab,
|
||||
newAnswerTab,
|
||||
};
|
||||
};
|
||||
|
||||
export default useAnswers;
|
||||
@@ -1,10 +0,0 @@
|
||||
import {useContext} from "react";
|
||||
import {AnswersContext} from "@/lib/callWidget/contexts/answers";
|
||||
|
||||
const useCallDialog = () => {
|
||||
const {openCallDialog, setOpenCallDialog} = useContext(AnswersContext)
|
||||
|
||||
return {openCallDialog, setOpenCallDialog}
|
||||
}
|
||||
|
||||
export default useCallDialog
|
||||
@@ -1,37 +0,0 @@
|
||||
import useRequest from "@/lib/app/hooks/useRequest";
|
||||
import {useEffect, useState} from "react";
|
||||
import useSWR from "swr";
|
||||
import {GET_CALLER_HISTORY} from "@/core/data/apiRoutes";
|
||||
|
||||
const useCallerHistory = (call_id, phone_number, max_size) => {
|
||||
const requestServer = useRequest({auth: true, notification: false});
|
||||
const [validData, setValidData] = useState([]);
|
||||
|
||||
//swr config
|
||||
const fetcher = (...args) => {
|
||||
return requestServer(args, 'get').then(({data}) => {
|
||||
return data.data;
|
||||
}).catch(() => {
|
||||
})
|
||||
};
|
||||
|
||||
const {data, isLoading} = useSWR(`${GET_CALLER_HISTORY}?phone_number=${phone_number}&size=${max_size}`, fetcher, {
|
||||
revalidateIfStale: true,
|
||||
revalidateOnFocus: false,
|
||||
revalidateOnReconnect: false,
|
||||
keepPreviousData: true
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
data && setValidData(data.filter((item) => item.id != call_id));
|
||||
}, [data, call_id]);
|
||||
|
||||
return {
|
||||
firstItemOfHistory: validData.length === 0 ? false : validData[0],
|
||||
historyList: validData.length < 2 ? false : validData.slice(1),
|
||||
isLoadingHistoryList: isLoading,
|
||||
errorHistoryList: !data
|
||||
}
|
||||
};
|
||||
|
||||
export default useCallerHistory;
|
||||
@@ -1,17 +0,0 @@
|
||||
import { useContext } from "react";
|
||||
import { AnswersContext } from "@/lib/callWidget/contexts/answers";
|
||||
|
||||
const useCategories = () => {
|
||||
const { categoryLists, subCategoryLists, active_category_id, dispatch } = useContext(AnswersContext);
|
||||
|
||||
const setActiveCategoryID = (tab_id, category_id) => {
|
||||
dispatch({ type: "CHANGE_ACTIVE_ID", tab_id, category_id });
|
||||
};
|
||||
return {
|
||||
categoryLists,
|
||||
subCategoryLists,
|
||||
active_category_id,
|
||||
setActiveCategoryID,
|
||||
};
|
||||
};
|
||||
export default useCategories;
|
||||
109
src/lib/contexts/auth.js
Normal file
109
src/lib/contexts/auth.js
Normal file
@@ -0,0 +1,109 @@
|
||||
"use client";
|
||||
import { GET_USER_ROUTE } from "@/core/utils/routes";
|
||||
import axios from "axios";
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useReducer,
|
||||
useState,
|
||||
} from "react";
|
||||
|
||||
const initAuth = {
|
||||
initAuthState: false,
|
||||
isAuth: false,
|
||||
user: {},
|
||||
};
|
||||
|
||||
const authReducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case "CLEAR_USER":
|
||||
return { ...state, user: {} };
|
||||
case "CHANGE_USER":
|
||||
return { ...state, user: action.user };
|
||||
case "CHANGE_AUTH_STATE":
|
||||
return { ...state, isAuth: action.isAuth };
|
||||
case "CHANGE_INIT_AUTH":
|
||||
return { ...state, initAuthState: action.initAuthState };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
const AuthContext = createContext();
|
||||
|
||||
export const AuthProvider = ({ children }) => {
|
||||
const [errorState, setErrorState] = useState({ status: null, message: "" });
|
||||
const [state, dispatch] = useReducer(authReducer, initAuth);
|
||||
|
||||
const clearUser = useCallback(() => {
|
||||
dispatch({ type: "CLEAR_USER" });
|
||||
}, []);
|
||||
|
||||
const changeUser = useCallback((user) => {
|
||||
dispatch({ type: "CHANGE_USER", user });
|
||||
}, []);
|
||||
|
||||
const changeAuthState = useCallback((isAuth) => {
|
||||
dispatch({ type: "CHANGE_AUTH_STATE", isAuth });
|
||||
}, []);
|
||||
|
||||
const changeInitAuth = useCallback((initAuthState) => {
|
||||
dispatch({ type: "CHANGE_INIT_AUTH", initAuthState });
|
||||
}, []);
|
||||
|
||||
const logout = () => {
|
||||
clearUser();
|
||||
changeAuthState(false);
|
||||
changeInitAuth(true);
|
||||
};
|
||||
|
||||
const getUser = useCallback(async () => {
|
||||
try {
|
||||
const { data } = await axios.get(GET_USER_ROUTE, {
|
||||
withCredentials: true,
|
||||
});
|
||||
changeUser(data.data);
|
||||
changeAuthState(true);
|
||||
changeInitAuth(true);
|
||||
setErrorState(false);
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === 401) {
|
||||
clearUser();
|
||||
changeAuthState(false);
|
||||
changeInitAuth(true);
|
||||
return;
|
||||
}
|
||||
let status =
|
||||
error.response && error.response.status
|
||||
? error.response.status
|
||||
: "نامشخص";
|
||||
setErrorState({
|
||||
status,
|
||||
message: " مشکلی در احراز هویت رخ داده است . دقایقی بعد امتحان کنید!!!",
|
||||
});
|
||||
}
|
||||
}, [clearUser, changeUser, changeAuthState, changeInitAuth]);
|
||||
|
||||
useEffect(() => {
|
||||
getUser();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AuthContext.Provider
|
||||
value={{
|
||||
user: state.user,
|
||||
isAuth: state.isAuth,
|
||||
initAuthState: state.initAuthState,
|
||||
getUser,
|
||||
logout,
|
||||
errorState,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useAuth = () => useContext(AuthContext);
|
||||
106
src/lib/contexts/call.js
Normal file
106
src/lib/contexts/call.js
Normal file
@@ -0,0 +1,106 @@
|
||||
"use client";
|
||||
import {
|
||||
createContext,
|
||||
useReducer,
|
||||
useState,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useContext,
|
||||
} from "react";
|
||||
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case "CHANGE_ACTIVE_TAB":
|
||||
return state.map((call, index) => ({
|
||||
...call,
|
||||
active: index === action.index_call,
|
||||
}));
|
||||
case "CHANGE_LIST":
|
||||
return [...action.new_list];
|
||||
case "CHANGE_ACTIVE_CATEGORY_ID":
|
||||
return state.map((call) =>
|
||||
call.id === action.tab_id
|
||||
? { ...call, active_category_id: action.category_id }
|
||||
: call,
|
||||
);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export const CallContext = createContext(null);
|
||||
|
||||
export const CallProvider = ({ children }) => {
|
||||
const [openCallDialog, setOpenCallDialog] = useState(false);
|
||||
const [activeCall, setActiveCall] = useState(0);
|
||||
const [calls, dispatch] = useReducer(reducer, []);
|
||||
|
||||
const changeActiveTab = useCallback((index_call) => {
|
||||
dispatch({ type: "CHANGE_ACTIVE_TAB", index_call });
|
||||
}, []);
|
||||
|
||||
const changeList = useCallback((new_list) => {
|
||||
dispatch({ type: "CHANGE_LIST", new_list });
|
||||
}, []);
|
||||
|
||||
const changeActiveCategoryId = useCallback((tab_id, category_id) => {
|
||||
dispatch({ type: "CHANGE_ACTIVE_CATEGORY_ID", tab_id, category_id });
|
||||
}, []);
|
||||
|
||||
const changeActiveTabCall = (newValue) => {
|
||||
changeActiveTab(newValue);
|
||||
setActiveCall(newValue);
|
||||
};
|
||||
|
||||
const newCall = useCallback(
|
||||
(data) => {
|
||||
const newCall = {
|
||||
id: data.id,
|
||||
phone_number: data.phone_number,
|
||||
date: new Date(),
|
||||
active: true,
|
||||
active_category_id: null,
|
||||
};
|
||||
const newList = [...calls, newCall];
|
||||
changeList(newList);
|
||||
changeActiveTabCall(newList.length - 1);
|
||||
},
|
||||
[calls],
|
||||
);
|
||||
|
||||
const closeCall = useCallback(
|
||||
(id) => {
|
||||
const index = calls.findIndex((answer) => answer.id === id);
|
||||
const newIndex = index === 0 ? 0 : index - 1;
|
||||
const newList = [...calls];
|
||||
newList.splice(index, 1);
|
||||
if (newList.length !== 0) {
|
||||
newList[newIndex].active = true;
|
||||
changeActiveTabCall(newIndex);
|
||||
}
|
||||
changeList(newList);
|
||||
},
|
||||
[calls],
|
||||
);
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
openCallDialog,
|
||||
setOpenCallDialog,
|
||||
calls,
|
||||
newCall,
|
||||
closeCall,
|
||||
activeCall,
|
||||
setActiveCall,
|
||||
changeActiveCategoryId,
|
||||
changeActiveTabCall,
|
||||
}),
|
||||
[openCallDialog, calls, activeCall],
|
||||
);
|
||||
|
||||
return (
|
||||
<CallContext.Provider value={contextValue}>{children}</CallContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useCall = () => useContext(CallContext);
|
||||
72
src/lib/contexts/category.js
Normal file
72
src/lib/contexts/category.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import { GET_CATEGORY } from "@/core/utils/routes";
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useReducer,
|
||||
} from "react";
|
||||
import useRequest from "../hooks/useRequest";
|
||||
|
||||
const initialState = {
|
||||
categoryLists: [],
|
||||
subCategoryLists: [],
|
||||
active_category_id: null,
|
||||
};
|
||||
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case "SET_CATEGORIES":
|
||||
return { ...state, categoryLists: action.payload };
|
||||
case "SET_SUBCATEGORIES":
|
||||
return { ...state, subCategoryLists: action.payload };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export const CategoriesContext = createContext(null);
|
||||
|
||||
export const CategoriesProvider = ({ children }) => {
|
||||
const requestServer = useRequest({ notificationShow: false });
|
||||
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
const { data } = await requestServer(GET_CATEGORY, "get");
|
||||
const subCategories = data.data;
|
||||
|
||||
const categories = Array.from(
|
||||
new Map(
|
||||
subCategories.map((item) => [item.category_id, item]),
|
||||
).values(),
|
||||
);
|
||||
|
||||
dispatch({ type: "SET_CATEGORIES", payload: categories });
|
||||
dispatch({ type: "SET_SUBCATEGORIES", payload: subCategories });
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
fetchCategories();
|
||||
}, []);
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
categoryLists: state.categoryLists,
|
||||
subCategoryLists: state.subCategoryLists,
|
||||
active_category_id: state.active_category_id,
|
||||
}),
|
||||
[state],
|
||||
);
|
||||
|
||||
return (
|
||||
<CategoriesContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</CategoriesContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useCategory = () => useContext(CategoriesContext);
|
||||
104
src/lib/contexts/socket.js
Normal file
104
src/lib/contexts/socket.js
Normal file
@@ -0,0 +1,104 @@
|
||||
"use client";
|
||||
import { Power, PowerOff } from "@mui/icons-material";
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
useCallback,
|
||||
} from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { io } from "socket.io-client";
|
||||
import { useAuth } from "./auth";
|
||||
|
||||
const SocketContext = createContext({
|
||||
socket: null,
|
||||
status: "",
|
||||
});
|
||||
|
||||
export const useSocket = () => useContext(SocketContext);
|
||||
|
||||
export const SocketProvider = ({ children }) => {
|
||||
const { user } = useAuth();
|
||||
const socketToastId = useRef(null);
|
||||
const [status, setStatus] = useState("");
|
||||
|
||||
const socket = useMemo(() => {
|
||||
return io(`${process.env.NEXT_PUBLIC_SERVER_SOCKET_URL}`, {
|
||||
path: "/notification/socket.io",
|
||||
autoConnect: false,
|
||||
auth: { token: null },
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (user?.telephone_id) {
|
||||
socket.auth.token = user.telephone_id;
|
||||
}
|
||||
}, [user, socket]);
|
||||
|
||||
const handleConnect = useCallback(() => setStatus("connected"), []);
|
||||
const handleDisconnect = useCallback(() => setStatus("disconnected"), []);
|
||||
const handleConnectError = useCallback(() => setStatus("error"), []);
|
||||
|
||||
useEffect(() => {
|
||||
socket.on("connect", handleConnect);
|
||||
socket.on("disconnect", handleDisconnect);
|
||||
socket.on("connect_error", handleConnectError);
|
||||
|
||||
return () => {
|
||||
socket.off("connect", handleConnect);
|
||||
socket.off("disconnect", handleDisconnect);
|
||||
socket.off("connect_error", handleConnectError);
|
||||
};
|
||||
}, [socket, handleConnect, handleDisconnect, handleConnectError]);
|
||||
|
||||
useEffect(() => {
|
||||
if (status === "connected") {
|
||||
toast.update(socketToastId.current, {
|
||||
containerId: "socket_connection",
|
||||
type: "success",
|
||||
render: "ارتباط با سرور برقرار شد",
|
||||
autoClose: 2000,
|
||||
closeButton: true,
|
||||
closeOnClick: true,
|
||||
icon: ({ theme, type }) => <Power />,
|
||||
});
|
||||
} else if (status === "error" || status === "disconnected") {
|
||||
if (socketToastId.current) {
|
||||
toast.update(socketToastId.current, {
|
||||
containerId: "socket_connection",
|
||||
type: "warning",
|
||||
render: "ارتباط با سرور قطع شد. در حال تلاش برای بازیابی…",
|
||||
autoClose: false,
|
||||
closeButton: false,
|
||||
draggable: false,
|
||||
icon: ({ theme, type }) => <PowerOff />,
|
||||
});
|
||||
} else {
|
||||
socketToastId.current = toast.warn(
|
||||
"ارتباط با سرور قطع شد. در حال تلاش برای بازیابی…",
|
||||
{
|
||||
containerId: "socket_connection",
|
||||
autoClose: false,
|
||||
closeButton: false,
|
||||
draggable: false,
|
||||
icon: ({ theme, type }) => <PowerOff />,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
const contextValue = useMemo(() => ({ socket, status }), [socket, status]);
|
||||
|
||||
return (
|
||||
<SocketContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</SocketContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default SocketContext;
|
||||
189
src/lib/contexts/tableSetting.js
Normal file
189
src/lib/contexts/tableSetting.js
Normal file
@@ -0,0 +1,189 @@
|
||||
"use client";
|
||||
|
||||
import { createContext, useCallback, useEffect, useState } from "react";
|
||||
import LZString from "lz-string";
|
||||
|
||||
export const TableSettingContext = createContext();
|
||||
|
||||
const decompressData = (compressedData) => {
|
||||
try {
|
||||
const decompressed = LZString.decompressFromUTF16(compressedData);
|
||||
if (decompressed === null) {
|
||||
localStorage.removeItem("_setting-app");
|
||||
return null;
|
||||
} else {
|
||||
return JSON.parse(decompressed);
|
||||
}
|
||||
} catch (error) {
|
||||
localStorage.removeItem("_setting-app");
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const TableSettingProvider = ({ children }) => {
|
||||
const [settingStore, setSettingStore] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
const compressedData = localStorage.getItem("_setting-app");
|
||||
|
||||
const data = compressedData ? decompressData(compressedData) : null;
|
||||
if (data) {
|
||||
setSettingStore(data);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const hideAction = useCallback(
|
||||
(user_id, page_name, table_name, settingValue) => {
|
||||
clearAction(user_id, page_name, table_name, "hides");
|
||||
updateSettingStorage(
|
||||
user_id,
|
||||
page_name,
|
||||
table_name,
|
||||
"hides",
|
||||
settingValue,
|
||||
);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const sortAction = useCallback(
|
||||
(user_id, page_name, table_name, settingValue) => {
|
||||
updateSettingStorage(
|
||||
user_id,
|
||||
page_name,
|
||||
table_name,
|
||||
"sorts",
|
||||
settingValue,
|
||||
);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const filterAction = (user_id, page_name, table_name, settingValue) => {
|
||||
updateSettingStorage(
|
||||
user_id,
|
||||
page_name,
|
||||
table_name,
|
||||
"filters",
|
||||
settingValue,
|
||||
);
|
||||
};
|
||||
|
||||
const resetAction = (user_id, page_name, table_name) => {
|
||||
const compressedData = localStorage.getItem("_setting-app");
|
||||
const prevLocalStorage = compressedData
|
||||
? decompressData(compressedData)
|
||||
: {};
|
||||
|
||||
const userSettings = prevLocalStorage[user_id] || {};
|
||||
const pageSettings = userSettings[page_name] || {};
|
||||
delete pageSettings[table_name];
|
||||
|
||||
const resetData = {
|
||||
...prevLocalStorage,
|
||||
[user_id]: {
|
||||
...userSettings,
|
||||
[page_name]: {
|
||||
...pageSettings,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
localStorage.setItem(
|
||||
"_setting-app",
|
||||
LZString.compressToUTF16(JSON.stringify(resetData)),
|
||||
);
|
||||
setSettingStore(resetData);
|
||||
};
|
||||
|
||||
const updateSettingStorage = (
|
||||
user_id,
|
||||
page_name,
|
||||
table_name,
|
||||
settingType,
|
||||
settingValue,
|
||||
) => {
|
||||
const compressedData = localStorage.getItem("_setting-app");
|
||||
const prevLocalStorage = compressedData
|
||||
? decompressData(compressedData)
|
||||
: {};
|
||||
|
||||
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 },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
localStorage.setItem(
|
||||
"_setting-app",
|
||||
LZString.compressToUTF16(JSON.stringify(updatedLocalStorage)),
|
||||
);
|
||||
setSettingStore(updatedLocalStorage);
|
||||
};
|
||||
|
||||
const clearAction = (user_id, page_name, table_name, key) => {
|
||||
const compressedData = localStorage.getItem("_setting-app");
|
||||
const prevLocalStorage = compressedData
|
||||
? decompressData(compressedData)
|
||||
: {};
|
||||
|
||||
const userSettings = prevLocalStorage[user_id] || {};
|
||||
const pageSettings = userSettings[page_name] || {};
|
||||
const tableSettings = pageSettings[table_name] || {};
|
||||
|
||||
const updatedSettings = {
|
||||
...prevLocalStorage,
|
||||
[user_id]: {
|
||||
...userSettings,
|
||||
[page_name]: {
|
||||
...pageSettings,
|
||||
[table_name]: {
|
||||
...tableSettings,
|
||||
[key]: Array.isArray(tableSettings[key]) ? [] : {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
localStorage.setItem(
|
||||
"_setting-app",
|
||||
LZString.compressToUTF16(JSON.stringify(updatedSettings)),
|
||||
);
|
||||
setSettingStore(updatedSettings);
|
||||
};
|
||||
|
||||
return (
|
||||
<TableSettingContext.Provider
|
||||
value={{
|
||||
settingStore,
|
||||
hideAction,
|
||||
sortAction,
|
||||
filterAction,
|
||||
resetAction,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</TableSettingContext.Provider>
|
||||
);
|
||||
};
|
||||
50
src/lib/hooks/useCallerHistory.js
Normal file
50
src/lib/hooks/useCallerHistory.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import useRequest from "./useRequest";
|
||||
import { GET_CALLER_HISTORY } from "@/core/utils/routes";
|
||||
|
||||
const useCallerHistory = (callId, phoneNumber, maxSize) => {
|
||||
const requestServer = useRequest();
|
||||
|
||||
const [historyData, setHistoryData] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [hasError, setHasError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchHistory = async () => {
|
||||
if (!phoneNumber || !maxSize) return;
|
||||
|
||||
setIsLoading(true);
|
||||
setHasError(false);
|
||||
|
||||
try {
|
||||
const url = `${GET_CALLER_HISTORY}?phone_number=${phoneNumber}&size=${maxSize}`;
|
||||
const { data } = await requestServer(url, "get");
|
||||
const items = data?.data || [];
|
||||
|
||||
const filtered = items.filter((item) => item.id !== callId);
|
||||
const unique = Array.from(
|
||||
new Map(filtered.map((item) => [item.id, item])).values(),
|
||||
);
|
||||
|
||||
setHistoryData(unique);
|
||||
} catch (error) {
|
||||
console.error("Error fetching caller history:", error);
|
||||
setHasError(true);
|
||||
setHistoryData([]);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchHistory();
|
||||
}, [callId, phoneNumber, maxSize]);
|
||||
|
||||
return {
|
||||
firstItemOfHistory: historyData[0] || false,
|
||||
historyList: historyData.length > 1 ? historyData.slice(1) : false,
|
||||
isLoadingHistoryList: isLoading,
|
||||
errorHistoryList: hasError,
|
||||
};
|
||||
};
|
||||
|
||||
export default useCallerHistory;
|
||||
21
src/lib/hooks/usePermissions.js
Normal file
21
src/lib/hooks/usePermissions.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import { GET_PERMISSIONS_ROUTE } from "@/core/utils/routes";
|
||||
import useSWR from "swr";
|
||||
import useRequest from "./useRequest";
|
||||
|
||||
export const usePermissions = () => {
|
||||
const request = useRequest();
|
||||
|
||||
const fetcher = async (url) => {
|
||||
try {
|
||||
const response = await request(url);
|
||||
return response.data.data;
|
||||
} catch (error) {
|
||||
throw new Error();
|
||||
}
|
||||
};
|
||||
|
||||
return useSWR(GET_PERMISSIONS_ROUTE, fetcher, {
|
||||
keepPreviousData: true,
|
||||
dedupingInterval: 30000,
|
||||
});
|
||||
};
|
||||
66
src/lib/hooks/useRequest.js
Normal file
66
src/lib/hooks/useRequest.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import { errorResponse } from "@/core/utils/errorResponse";
|
||||
import { successRequest } from "@/core/utils/successRequest";
|
||||
import axios from "axios";
|
||||
import { useAuth } from "../contexts/auth";
|
||||
import { useSidebarBadge } from "./useSidebarBadge";
|
||||
|
||||
const defaultOptions = {
|
||||
data: {},
|
||||
requestOptions: {
|
||||
headers: {},
|
||||
},
|
||||
notificationShow: true,
|
||||
notificationSuccess: false,
|
||||
notificationFailed: true,
|
||||
hasSidebarUpdate: false,
|
||||
};
|
||||
|
||||
const useRequest = (initOptions) => {
|
||||
const _options = Object.assign({}, defaultOptions, initOptions);
|
||||
|
||||
const { mutate: sidebarUpdate } = useSidebarBadge({
|
||||
enable: _options.hasSidebarUpdate,
|
||||
});
|
||||
const { logout } = useAuth();
|
||||
|
||||
/**
|
||||
* Performs an HTTP request.
|
||||
* @param {string} url - The URL to send the request to.
|
||||
* @param {'get' | 'post' | 'put' | 'delete'} [method='get'] - HTTP method.
|
||||
* @param {object} [options] - Additional request options.
|
||||
* @returns {Promise<AxiosResponse>} - Axios response.
|
||||
*/
|
||||
return async (url = "", method = "get", options) => {
|
||||
const mergedOptions = Object.assign({}, _options, options);
|
||||
try {
|
||||
const response = await axios({
|
||||
url,
|
||||
method,
|
||||
data: method === "get" ? null : mergedOptions.data,
|
||||
withCredentials: true,
|
||||
...mergedOptions.requestOptions,
|
||||
});
|
||||
if (mergedOptions.hasSidebarUpdate) {
|
||||
if (method === "post" || method === "put" || method === "delete")
|
||||
sidebarUpdate();
|
||||
}
|
||||
successRequest(
|
||||
mergedOptions.notificationShow && mergedOptions.notificationSuccess,
|
||||
"request_data",
|
||||
);
|
||||
return response;
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
errorResponse(
|
||||
error.response,
|
||||
mergedOptions.notificationShow && mergedOptions.notificationFailed,
|
||||
"request_data",
|
||||
logout,
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default useRequest;
|
||||
23
src/lib/hooks/useSidebarBadge.js
Normal file
23
src/lib/hooks/useSidebarBadge.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { GET_SIDEBAR_BADGE_ROUTE } from "@/core/utils/routes";
|
||||
import axios from "axios";
|
||||
import useSWR from "swr";
|
||||
|
||||
export const useSidebarBadge = ({ enable = true }) => {
|
||||
const fetcher = async (url) => {
|
||||
try {
|
||||
const response = await axios({
|
||||
url,
|
||||
method: "get",
|
||||
withCredentials: true,
|
||||
});
|
||||
return response.data.data;
|
||||
} catch (error) {
|
||||
throw new Error();
|
||||
}
|
||||
};
|
||||
|
||||
return useSWR(enable ? GET_SIDEBAR_BADGE_ROUTE : "", fetcher, {
|
||||
keepPreviousData: true,
|
||||
dedupingInterval: 30000,
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user