implementation error handler

This commit is contained in:
Amirhossein Mahmoodi
2023-08-09 13:41:14 +03:30
parent 929a7741a6
commit b17bc0136c
22 changed files with 214 additions and 48 deletions

View File

@@ -1,4 +1,5 @@
NEXT_PUBLIC_API_NAME = "Loan Facilities Dashboard"
NEXT_PUBLIC_API_VERSION = "1.0.0"
NEXT_PUBLIC_DEFAULT_LANGUAGE = "fa"
NEXT_PUBLIC_DEFAULT_DIRECTION = "rtl"

View File

@@ -34,7 +34,7 @@ const NavyFormComponent = () => {
setProvinceList(formattedData);
})
.catch((error) => {
Notifications(directionApp, t, error.response);
Notifications(t, error.response);
});
}, []);
// end get province list
@@ -115,7 +115,7 @@ const NavyFormComponent = () => {
// submit
const handleSubmit = async (values, props) => {
Notifications(directionApp, t, undefined);
Notifications(t, undefined);
const formData = new FormData();
formData.append("name", values.name);
formData.append("phone_number", values.phone_number);
@@ -138,10 +138,10 @@ const NavyFormComponent = () => {
},
})
.then(function (response) {
Notifications(directionApp, t, response);
Notifications(t, response);
})
.catch(function (error) {
Notifications(directionApp, t, error.response);
Notifications(t, error.response);
props.setSubmitting(false);
});
};

View File

@@ -87,7 +87,7 @@ const WelfareServicesFormComponent = () => {
});
const handleSubmit = async (values, props) => {
Notifications(directionApp, t, undefined);
Notifications(t, undefined);
const formData = new FormData();
formData.append("name", values.name);
formData.append("phone_number", values.phone_number);
@@ -109,10 +109,10 @@ const WelfareServicesFormComponent = () => {
},
})
.then(function (response) {
Notifications(directionApp, t, response);
Notifications(t, response);
})
.catch(function (error) {
Notifications(directionApp, t, error.response);
Notifications(t, error.response);
props.setSubmitting(false);
});
};

View File

@@ -2,7 +2,7 @@ import {NextLinkComposed} from "@/core/components/LinkRouting";
import CenterLayout from "@/layouts/CenterLayout";
import FullPageLayout from "@/layouts/FullPageLayout";
import useUser from "@/lib/app/hooks/useUser";
import {Button, Typography} from "@mui/material";
import {Button, Stack, Typography} from "@mui/material";
import {useTranslations} from "next-intl";
import SvgDashboard from "@/core/components/svgs/SvgDashboard";
@@ -41,6 +41,14 @@ const FirstComponent = () => {
</Button>
)}
</CenterLayout>
<Stack direction="row" alignItems="center" justifyContent="center">
<Typography variant={"caption"}
sx={{
color: 'primary.main',
fontFamily: 'Arial',
fontWeight: 'bold'
}}>v{process.env.NEXT_PUBLIC_API_VERSION}</Typography>
</Stack>
</FullPageLayout>
);
};

View File

@@ -37,18 +37,18 @@ const SendTokenComponent = ({
});
const handleSubmit = async (values, props) => {
Notifications(directionApp, t, undefined);
Notifications(t, undefined);
await axios
.post(LOGIN, {
phone_number: values.phone_number,
verification_code: values.verification_code,
})
.then(function (response) {
Notifications(directionApp, t, response);
Notifications(t, response);
setToken(response.data.token);
})
.catch(function (error) {
Notifications(directionApp, t, error.response);
Notifications(t, error.response);
props.setSubmitting(false);
});
};

View File

@@ -36,19 +36,19 @@ const SendUserDataComponent = ({
});
const handleSubmit = async (values, props) => {
Notifications(directionApp, t, undefined);
Notifications(t, undefined);
await axios
.post(SEND_OTP_TOKEN, {
phone_number: values.phone_number,
})
.then(function (response) {
Notifications(directionApp, t, response);
Notifications(t, response);
setPhoneNumber(values.phone_number);
setOtpToken(true);
setTimer(initialTimerValue);
})
.catch(function (error) {
Notifications(directionApp, t, error.response);
Notifications(t, error.response);
props.setSubmitting(false);
});
};

View File

@@ -37,19 +37,19 @@ const RequestOtpComponent = ({
});
const handleSubmit = async (values, props) => {
Notifications(directionApp, t, undefined);
Notifications(t, undefined);
await axios
.post(SEND_OTP_TOKEN, {
phone_number: values.phone_number,
})
.then(function (response) {
Notifications(directionApp, t, response);
Notifications(t, response);
setPhoneNumber(values.phone_number);
setOtpToken(true);
setTimer(initialTimerValue);
})
.catch(function (error) {
Notifications(directionApp, t, error.response);
Notifications(t, error.response);
props.setSubmitting(false);
});
};

View File

@@ -45,7 +45,7 @@ const UserRegisterComponent = ({
});
const handleSubmit = async (values, props) => {
Notifications(directionApp, t, undefined);
Notifications(t, undefined);
console.log(initialValues);
await axios
.post(REGISTER, {
@@ -56,12 +56,12 @@ const UserRegisterComponent = ({
verification_code: values.verification_code,
})
.then(function (response) {
Notifications(directionApp, t, response);
Notifications(t, response);
setOtpToken(true);
setToken(response.data.token);
})
.catch(function (error) {
Notifications(directionApp, t, error.response);
Notifications(t, error.response);
props.setSubmitting(false);
});
};

View File

@@ -43,7 +43,7 @@ const UserRegisterComponent = ({
});
const handleSubmit = async (values, props) => {
Notifications(directionApp, t, undefined);
Notifications(t, undefined);
console.log(initialValues);
await axios
.post(REGISTER, {
@@ -53,12 +53,12 @@ const UserRegisterComponent = ({
verification_code: values.verification_code,
})
.then(function (response) {
Notifications(directionApp, t, response);
Notifications(t, response);
setOtpToken(true);
setToken(response.data.token);
})
.catch(function (error) {
Notifications(directionApp, t, error.response);
Notifications(t, error.response);
props.setSubmitting(false);
});
};

View File

@@ -29,17 +29,17 @@ const ResendToken = ({initialTimerValue, timer, setTimer, PhoneNumber}) => {
const handleResendClick = async () => {
// work on it
if (timer != 0) return;
Notifications(directionApp, t, undefined);
Notifications(t, undefined);
await axios
.post(SEND_OTP_TOKEN, {
phone_number: PhoneNumber,
})
.then(function (response) {
Notifications(directionApp, t, response);
Notifications(t, response);
setTimer(initialTimerValue);
})
.catch(function (error) {
Notifications(directionApp, t, error.response);
Notifications(t, error.response);
});
};
// End Handle Resend Token

View File

@@ -2,7 +2,7 @@ import DangerousIcon from "@mui/icons-material/Dangerous";
import {Box, Typography} from "@mui/material";
import {toast} from "react-toastify";
const ErrorNotification = (directionApp, t, status, message) => {
const ErrorNotification = (t, status, message) => {
toast(
({closeToast}) => (
<>
@@ -29,7 +29,6 @@ const ErrorNotification = (directionApp, t, status, message) => {
</>
),
{
position: directionApp === "ltr" ? "top-left" : "top-right",
autoClose: false,
closeOnClick: false,
draggable: false,

View File

@@ -0,0 +1,11 @@
import {toast} from "react-toastify";
const PendingNotification = (t) => {
toast(t("notifications.pending"), {
autoClose: false,
closeOnClick: false,
draggable: false,
});
};
export default PendingNotification;

View File

@@ -2,7 +2,7 @@ import BeenhereIcon from "@mui/icons-material/Beenhere";
import {Box, Typography} from "@mui/material";
import {toast} from "react-toastify";
const SuccessNotification = (directionApp, t, status) => {
const SuccessNotification = (t, status) => {
toast(
() => (
<>
@@ -30,7 +30,6 @@ const SuccessNotification = (directionApp, t, status) => {
</>
),
{
position: directionApp === "ltr" ? "top-left" : "top-right",
autoClose: 3000,
hideProgressBar: true,
pauseOnHover: true,

View File

@@ -2,7 +2,7 @@ import ReportIcon from "@mui/icons-material/Report";
import {Box, Divider, Typography} from "@mui/material";
import {toast} from "react-toastify";
const WarningNotification = (directionApp, t, status) => {
const WarningNotification = (t, status) => {
toast(
({closeToast}) => (
<>
@@ -30,7 +30,6 @@ const WarningNotification = (directionApp, t, status) => {
</>
),
{
position: directionApp === "ltr" ? "top-left" : "top-right",
autoClose: false,
closeOnClick: false,
draggable: false,

View File

@@ -3,37 +3,36 @@ import ErrorNotification from "./ErrorNotification";
import SuccessNotification from "./SuccessNotification";
import WarningNotification from "./WarningNotification";
const Notifications = async (directionApp, t, response) => {
const Notifications = async (t, response) => {
const {status, data} = response != undefined ? response : ""
toast.dismiss();
switch (status) {
case 200:
SuccessNotification(directionApp, t, status);
SuccessNotification(t, status);
break;
case 400:
ErrorNotification(directionApp, t, status);
ErrorNotification(t, status);
break;
case 401:
ErrorNotification(directionApp, t, status);
ErrorNotification(t, status);
break;
case 403:
ErrorNotification(directionApp, t, status);
ErrorNotification(t, status);
break;
case 422:
ErrorNotification(directionApp, t, status, data.message);
ErrorNotification(t, status, data.message);
break;
case 500:
WarningNotification(directionApp, t, status);
WarningNotification(t, status);
break;
case 503:
WarningNotification(directionApp, t, status);
WarningNotification(t, status);
break;
case 504:
WarningNotification(directionApp, t, status);
WarningNotification(t, status);
break;
default:
toast(t("pending"), {
position: directionApp === "ltr" ? "top-left" : "top-right",
autoClose: false,
closeOnClick: false,
draggable: false,
@@ -47,9 +46,9 @@ export default Notifications;
/*
usage document
** for pending use ( Notifications(directionApp, t, undefined) ) this before your request.
** for success use ( Notifications(directionApp, t, response) ) this inside .then() of your request.
** for Error and Warning use ( Notifications(directionApp, t, error.response) ) this inside .catche() of your request.
** for pending use ( Notifications( t, undefined) ) this before your request.
** for success use ( Notifications( t, response) ) this inside .then() of your request.
** for Error and Warning use ( Notifications( t, error.response) ) this inside .catche() of your request.
end usage document
*/

View File

@@ -0,0 +1,62 @@
import ErrorNotification from "@/core/components/notifications/ErrorNotification";
import WarningNotification from "@/core/components/notifications/WarningNotification";
import {toast} from "react-toastify";
export const errorSetting = (t, notification) => {
//todo
}
export const errorRequest = (t, notification) => {
//todo
}
export const errorResponse = (response, clearToken, t, notification) => {
toast.dismiss();
if (isServerError(response.status)) {
errorServer(response, t, notification)
} else if (isClientError(response.status)) {
errorClient(response, clearToken, t, notification)
}
}
const errorServer = (response, t, notification) => {
if (notification) WarningNotification(t, response.status);
}
const errorClient = (response, clearToken, t, notification) => {
switch (response.status) {
case 401:
clearToken()
if (notification) ErrorNotification(t, response.status)
break;
case 422:
if ('type' in response.data) {
errorLogic(response, t, notification)
break;
}
errorValidation(response, t, notification)
break;
case 429:
if (notification) ErrorNotification(t, response.status)
break
default:
if (notification) ErrorNotification(t, response.status)
break
}
}
const isServerError = status => status >= 500 && status <= 599;
const isClientError = status => status >= 400 && status <= 499;
const errorLogic = (response, t, notification) => {
if (notification) ErrorNotification(t, response.status)
}
const errorValidation = (response, t, notification) => {
if (notification) {
const errorsMap = Object.keys(response.data.errors)
const errorsArray = response.data.errors
errorsMap.map((item, index) => {
ErrorNotification(t, response.status, errorsArray[item][0]);
})
}
}

View File

@@ -0,0 +1,9 @@
import SuccessNotification from "@/core/components/notifications/SuccessNotification";
import {toast} from "react-toastify";
export const successRequest = (response, t, options) => {
toast.dismiss();
if (options.notification && options.success.notification.show) {
SuccessNotification(t, response.status)
}
}

View File

@@ -15,6 +15,15 @@ const theme = {
dark: colord(process.env.NEXT_PUBLIC_SECONDARY_MAIN).darken(0.1).toHex(),
},
},
components: {
MuiBackdrop: {
styleOverrides: {
root: {
backgroundColor: colord(process.env.NEXT_PUBLIC_PRIMARY_MAIN).darken(0.2).alpha(0.7).toHex(),
},
},
},
},
};
export default theme;

View File

@@ -96,7 +96,7 @@ function AppLayout({children, isBot}) {
color={theme.palette.secondary.dark}
options={{showSpinner: false}}
/>
{directionApp === "rtl" ? <ToastContainer rtl/> : <ToastContainer ltr/>}
<ToastContainer position={directionApp === "ltr" ? "top-left" : "top-right"} rtl={directionApp === 'rtl'}/>
{children}
</>
);

View File

@@ -1,6 +1,7 @@
import {USER_INFO} from "@/core/data/apiRoutes";
import axios from "axios";
import {createContext, useCallback, useEffect, useReducer} from "react";
import {errorRequest, errorResponse, errorSetting} from "@/core/utils/errorHandler";
const initialUser = {
isAuth: false,
@@ -75,7 +76,17 @@ export const UserProvider = ({children}) => {
})
.then(({data}) => {
if (typeof callback === "function") callback(data);
});
})
.catch(error => {
if (error.response) {
errorResponse(error.response, clearToken, null, false)
} else if (error.request) {
errorRequest(null, false)
} else {
errorSetting(null, false)
}
})
},
[state.token]
);

View File

@@ -0,0 +1,59 @@
import axios from "axios";
import {successRequest} from "@/core/utils/succesHandler";
import PendingNotification from "@/core/components/notifications/PendingNotification";
import {useTranslations} from "next-intl";
import useUser from "@/lib/app/hooks/useUser";
import {errorRequest, errorResponse, errorSetting} from "@/core/utils/errorHandler";
const defaultOptions = {
data: {},
requestOptions: {},
notification: true,
pending: true,
success: {
notification: {
show: true,
},
},
failed: {
notification: {
show: true,
},
},
}
const useRequest = (initOptions) => {
const t = useTranslations()
const {clearToken} = useUser()
let _options = {...defaultOptions, ...initOptions}
function requestServer(url = '', method = 'get', options) {
_options = {..._options, ...options}
return new Promise((resolve, reject) => {
if (_options.notification && _options.failed.notification.show && _options.pending) PendingNotification(t)
axios({
url: url,
method: method,
data: _options.data,
..._options.requestOptions
})
.then(response => {
successRequest(response, t, _options)
resolve(response)
})
.catch(error => {
if (error.response) {
errorResponse(error.response, clearToken, t, _options.notification && _options.failed.notification.show)
} else if (error.request) {
errorRequest(t, _options.notification && _options.failed.notification.show)
} else {
errorSetting(t, _options.notification && _options.failed.notification.show)
}
reject(error)
})
});
}
return requestServer
}
export default useRequest

View File

@@ -26,7 +26,7 @@ const WithAuthMiddleware = ({children}) => {
variant="contained"
component={NextLinkComposed}
to={{
pathname: "/",
pathname: "/login",
}}
>
{t("login")}