Merge branch 'develop' into 'feature/debug_forms'

# Conflicts:
#   src/components/dashboard/machinary-office/TableRowActions.jsx
#   src/components/dashboard/passenger-boss/TableRowActions.jsx
#   src/components/dashboard/passenger-office/TableRowActions.jsx
#   src/components/dashboard/province-manager/TableRowActions.jsx
#   src/components/dashboard/transportation-assistance/TableRowActions.jsx
This commit is contained in:
AmirHossein Mahmoodi
2023-08-09 10:05:10 +00:00
17 changed files with 205 additions and 49 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

@@ -20,7 +20,7 @@ const DashboardChangePasswordComponent = () => {
const {directionApp} = useDirection();
const handleSubmit = (values, {setSubmitting, resetForm}) => {
Notifications(directionApp, t);
Notifications(t);
axios
.post(
CHANGE_PASSWORD,
@@ -35,12 +35,12 @@ const DashboardChangePasswordComponent = () => {
)
.then((response) => {
toast.dismiss(pendingToast); // Dismiss the pending toast notification
Notifications(directionApp, t, response);
Notifications(t, response);
resetForm();
})
.catch((error) => {
toast.dismiss(pendingToast); // Dismiss the pending toast notification
Notifications(directionApp, t, error.response);
Notifications(t, error.response);
})
.finally(() => {
setSubmitting(false); // Set `setSubmitting` to false after the API request completes

View File

@@ -20,7 +20,7 @@ const DashboardEditProfile = () => {
const {directionApp} = useDirection();
const editAvatar = async (avatar) => {
Notifications(directionApp, t);
Notifications(t);
try {
const formData = new FormData();
@@ -37,7 +37,7 @@ const DashboardEditProfile = () => {
},
});
} catch (error) {
Notifications(directionApp, t, error.response);
Notifications(t, error.response);
throw error;
}
};
@@ -55,13 +55,13 @@ const DashboardEditProfile = () => {
// });
// })
// .then((response) => {
// Notifications(directionApp, t, response);
// Notifications( t, response);
// getUser((data) => {
// changeUser(data);
// });
// })
// .catch((error) => {
// Notifications(directionApp, t, error.response);
// Notifications( t, error.response);
// })
// .finally(() => {
// setSubmitting(false);
@@ -75,13 +75,13 @@ const DashboardEditProfile = () => {
// },
// })
// .then((response) => {
// Notifications(directionApp, t, response);
// Notifications( t, response);
// getUser((data) => {
// changeUser(data);
// });
// })
// .catch((error) => {
// Notifications(directionApp, t, error.response);
// Notifications( t, error.response);
// })
// .finally(() => {
// setSubmitting(false);

View File

@@ -2,9 +2,10 @@ 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";
import process from "next/dist/build/webpack/loaders/resolve-url-loader/lib/postcss";
const FirstComponent = () => {
const t = useTranslations();
@@ -40,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

@@ -1,5 +1,4 @@
import LinkRouting from "@/core/components/LinkRouting";
import Notifications from "@/core/components/notifications";
import PasswordField from "@/core/components/PasswordField";
import StyledForm from "@/core/components/StyledForm";
import {GET_USER_TOKEN} from "@/core/data/apiRoutes";
@@ -8,38 +7,39 @@ import FullPageLayout from "@/layouts/FullPageLayout";
import useUser from "@/lib/app/hooks/useUser";
import LoginIcon from "@mui/icons-material/Login";
import {Box, Button, Container, Paper, Stack, TextField, Typography,} from "@mui/material";
import axios from "axios";
import {Field, Formik} from "formik";
import {useTranslations} from "next-intl";
import {useSearchParams} from "next/navigation";
import * as Yup from "yup";
import useDirection from "@/lib/app/hooks/useDirection";
import SvgLogin from "@/core/components/svgs/SvgLogin";
import useRequest from "@/lib/app/hooks/useRequest";
const LoginComponent = () => {
const t = useTranslations();
const {directionApp} = useDirection(); // should delete because we don't have direction anymore
const {setToken} = useUser(); // pass token to set token
const requestServer = useRequest()
// getting url query
const searchParams = useSearchParams();
const backUrlDecodedPath = searchParams.get("back_url");
//formik properties
const handleSubmit = async (values, props) => {
Notifications(directionApp, t);
await axios
.post(GET_USER_TOKEN, {
const handleSubmit = (values, props) => {
requestServer(GET_USER_TOKEN, 'post', {
data: {
username: values.username,
password: values.password,
})
.then(function (response) {
setToken(response.data.token);
})
.catch(function (error) {
Notifications(directionApp, t, error.response);
props.setSubmitting(false);
});
},
success: {
notification: {show: false}
}
}).then((response) => {
setToken(response.data.token)
}).catch(() => {
props.setSubmitting(false)
})
};
const initialValues = {
username: "",
@@ -120,7 +120,7 @@ const LoginComponent = () => {
fullWidth
size="medium"
endIcon={<LoginIcon/>}
disabled={props.isSubmitting ? true : false}
disabled={props.isSubmitting}
>
{t("LoginPage.button_submit")}
</Button>

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(
() => (
<>
@@ -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 DangerousIcon from "@mui/icons-material/Dangerous";
import {Box, Typography} from "@mui/material";
import {toast} from "react-toastify";
const UploadFileNotification = (directionApp, t) => {
const UploadFileNotification = (t) => {
toast(
({closeToast}) => (
<>
@@ -26,7 +26,6 @@ const UploadFileNotification = (directionApp, t) => {
</>
),
{
position: directionApp === "ltr" ? "top-left" : "top-right",
autoClose: false,
closeOnClick: false,
draggable: false,

View File

@@ -2,7 +2,7 @@ import ReportIcon from "@mui/icons-material/Report";
import {Box, Typography} from "@mui/material";
import {toast} from "react-toastify";
const WarningNotification = (directionApp, t, status) => {
const WarningNotification = (t, status) => {
toast(
() => (
<>
@@ -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 WarningNotification from "./WarningNotification";
import SuccessNotification from "./SuccessNotification";
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("notifications.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

@@ -19,7 +19,7 @@ const theme = {
MuiBackdrop: {
styleOverrides: {
root: {
backgroundColor: "#02020226", // Replace with your desired backdrop color and opacity
backgroundColor: colord(process.env.NEXT_PUBLIC_PRIMARY_MAIN).darken(0.2).alpha(0.7).toHex(),
},
},
},

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 {GET_USER_ROUTE} 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,16 @@ 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