| @@ -12,7 +12,6 @@ import { | |||
| REGISTER_PAGE, | |||
| REGISTER_SUCCESSFUL_PAGE, | |||
| RESET_PASSWORD_PAGE, | |||
| CREATE_OFFER_PAGE, | |||
| } from './constants/pages'; | |||
| import LoginPage from './pages/LoginPage/LoginPage'; | |||
| import HomePage from './pages/HomePage/HomePageMUI'; | |||
| @@ -24,7 +23,6 @@ import MailSent from './pages/ForgotPasswordPage/ForgotPasswordMailSent/MailSent | |||
| import Register from './pages/RegisterPages/Register/Register'; | |||
| import RegisterSuccessful from './pages/RegisterPages/RegisterSuccessful.js/RegisterSuccessful'; | |||
| import ResetPasswordPage from './pages/ResetPasswordPage/ResetPasswordPage'; | |||
| import CreateOffer from './pages/CreateOffer/CreateOffer'; | |||
| const AppRoutes = () => { | |||
| @@ -39,7 +37,6 @@ const AppRoutes = () => { | |||
| <Route path={FORGOT_PASSWORD_MAIL_SENT} component={MailSent} /> | |||
| <Route path={FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} /> | |||
| <Route path={RESET_PASSWORD_PAGE} component={ResetPasswordPage}/> | |||
| <Route path={CREATE_OFFER_PAGE} component={CreateOffer}/> | |||
| <PrivateRoute | |||
| exact | |||
| @@ -88,6 +88,7 @@ export default { | |||
| terms: "Uslove Korišćenja", | |||
| success: 'Registracija Uspešna', | |||
| PIBTaken: "PIB je zauzet!", | |||
| PIBnoOfCharacters: "PIB mora imati 9 karaktera!", | |||
| welcome: 'Dobro došli na trampu, želimo vam uspešno trampovanje!', | |||
| }, | |||
| forgotPassword: { | |||
| @@ -0,0 +1,3 @@ | |||
| export default { | |||
| email: "", | |||
| }; | |||
| @@ -0,0 +1,4 @@ | |||
| export default { | |||
| email: "", | |||
| password: "", | |||
| }; | |||
| @@ -0,0 +1,4 @@ | |||
| export default { | |||
| mail: "", | |||
| password: "", | |||
| }; | |||
| @@ -0,0 +1,4 @@ | |||
| export default { | |||
| nameOfFirm: "", | |||
| PIB: "", | |||
| }; | |||
| @@ -0,0 +1,5 @@ | |||
| export default { | |||
| phoneNumber: "", | |||
| location: "", | |||
| website: "", | |||
| }; | |||
| @@ -0,0 +1,4 @@ | |||
| export default { | |||
| password: "", | |||
| passwordConfirm: "", | |||
| }; | |||
| @@ -1,26 +0,0 @@ | |||
| /* eslint-disable */ | |||
| import React, { useEffect, useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| CreateOfferContainer, | |||
| } from "./CreateOffer.styled"; | |||
| import CreateOffer from "../../components/Cards/CreateOfferCard/CreateOffer"; | |||
| const CreateOfferPage = ({ history }) => { | |||
| return ( | |||
| <CreateOfferContainer> | |||
| <CreateOffer/> | |||
| </CreateOfferContainer> | |||
| ); | |||
| }; | |||
| CreateOfferPage.propTypes = { | |||
| history: PropTypes.shape({ | |||
| replace: PropTypes.func, | |||
| push: PropTypes.func, | |||
| location: PropTypes.shape({ | |||
| pathname: PropTypes.string, | |||
| }), | |||
| }), | |||
| }; | |||
| export default CreateOfferPage; | |||
| @@ -1,56 +0,0 @@ | |||
| import { Box, Container, Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../themes"; | |||
| export const CreateOfferContainer = styled(Container)` | |||
| margin-top: 0px; | |||
| display: flex; | |||
| width: 380px; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| `; | |||
| export const CreateOfferTitle = styled(Typography)` | |||
| font-family: "Open Sans"; | |||
| width: 328px; | |||
| height: 33px; | |||
| text-align: center; | |||
| flex: 1; | |||
| font-style: normal; | |||
| font-weight: 700; | |||
| font-size: 24px; | |||
| line-height: 33px; | |||
| color: ${selectedTheme.primaryPurple}; | |||
| margin-top: 36px; | |||
| margin-bottom: 40px; | |||
| `; | |||
| export const CreateOfferDescription = styled(Typography)` | |||
| font-family: "Open Sans"; | |||
| margin-top: 9px; | |||
| width: 221px; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| font-size: 16px; | |||
| line-height: 22px; | |||
| display: flex; | |||
| align-items: center; | |||
| text-align: center; | |||
| color: ${selectedTheme.primaryGrayText}; | |||
| margin-bottom: 20px; | |||
| `; | |||
| export const CreateOfferFormContainer = styled(Box)` | |||
| width: 335px; | |||
| height: 216px; | |||
| `; | |||
| export const RegisterAltText = styled(Typography)` | |||
| font-family: "Poppins"; | |||
| color: ${selectedTheme.primaryText}; | |||
| font-size: 14px; | |||
| padding-right: 6px; | |||
| line-height: 14px; | |||
| ` | |||
| export const RegisterTextContainer = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| margin-top: 36px; | |||
| justify-content: center; | |||
| ` | |||
| @@ -1,70 +1,11 @@ | |||
| import React, { useEffect } from "react"; | |||
| import Navbar from "../../components/MUI/NavbarComponent"; | |||
| // import FilterCard from "../../components/Cards/FilterCard/FilterCard"; | |||
| import { HomePageContainer } from "./HomePage.styled"; | |||
| // import MarketPlace from "../../components/MarketPlace/MarketPlace"; | |||
| // import MainLayout from "../../layouts/MainLayout/MainLayout"; | |||
| import { useDispatch } from "react-redux"; | |||
| // import { logoutUser } from "../../store/actions/login/loginActions"; | |||
| import Mockupdata from "../../components/Cards/FilterCard/Mockupdata"; | |||
| import qs from "query-string"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import { setFilters } from "../../store/actions/filters/filtersActions"; | |||
| // import ProfileLayout from "../../layouts/ProfileLayout/ProfileLayout"; | |||
| import FilterCard from "../../components/Cards/FilterCard/FilterCard"; | |||
| import MainLayout from "../../layouts/MainLayout/MainLayout"; | |||
| import MarketPlace from "../../components/MarketPlace/MarketPlace"; | |||
| import React from "react"; | |||
| const HomePage = () => { | |||
| const dispatch = useDispatch(); | |||
| const history = useHistory(); | |||
| useEffect(() => { | |||
| let category = null, subcategory = null, cities = [], _des_date = false, _des_popular = false, page, size; | |||
| const queryString = history.location.search.substring(1); | |||
| const queryObject = qs.parse(queryString); | |||
| if (queryObject.category) { | |||
| category = Mockupdata[1].find( | |||
| (item) => item.string === queryObject.category.toString() | |||
| ).id; | |||
| } | |||
| if (queryObject.subcategory) { | |||
| subcategory = Mockupdata[1].find( | |||
| (item) => item.string === queryObject.subcategory.toString() | |||
| ).id; | |||
| } | |||
| if (queryObject.city) { | |||
| if (Array.isArray(queryObject.city)) { | |||
| queryObject.city.forEach((item) => { | |||
| cities.push(Mockupdata[0].find((p) => p.string === item).id); | |||
| }); | |||
| } else { | |||
| cities.push( | |||
| Mockupdata[0].find((p) => p.string === queryObject.city).id | |||
| ); | |||
| } | |||
| } | |||
| if (queryObject.sortBy) { | |||
| if (queryObject.sortBy === "dateAsc") _des_date = false; | |||
| if (queryObject.sortBy === "dateDesc") _des_date = true; | |||
| if (queryObject.sortBy === "popular") _des_popular = true; | |||
| } | |||
| if (queryObject.page) { | |||
| page = queryObject.page; | |||
| } | |||
| if (queryObject.size) { | |||
| size = queryObject.size; | |||
| } | |||
| dispatch(setFilters({ category, subcategory, cities, _des_date, _des_popular, page, size })); | |||
| }, [history.location.search]); | |||
| return ( | |||
| <HomePageContainer> | |||
| <Navbar /> | |||
| <MainLayout leftCard={<FilterCard />} content={<MarketPlace />} /> | |||
| </HomePageContainer> | |||
| <div> | |||
| Home page | |||
| </div> | |||
| ); | |||
| }; | |||
| @@ -32,6 +32,8 @@ import { | |||
| ErrorMessage, | |||
| } from "./Login.styled"; | |||
| import selectedTheme from "../../themes"; | |||
| import loginValidation from "../../validations/loginValidation"; | |||
| import loginInitialValues from "../../initialValues/loginInitialValues"; | |||
| const LoginPage = ({ history }) => { | |||
| const dispatch = useDispatch(); | |||
| @@ -83,16 +85,8 @@ const LoginPage = ({ history }) => { | |||
| }; | |||
| const formik = useFormik({ | |||
| initialValues: { | |||
| email: "", | |||
| password: "", | |||
| }, | |||
| validationSchema: Yup.object().shape({ | |||
| email: Yup.string().required(t("login.mailRequired")), | |||
| password: Yup.string() | |||
| .required(t("login.passwordRequired")) | |||
| .min(8, t("login.passwordLength")), | |||
| }), | |||
| initialValues: loginInitialValues, | |||
| validationSchema: loginValidation, | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| @@ -1,250 +0,0 @@ | |||
| /* eslint-disable */ | |||
| import React, { useEffect, useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useFormik } from "formik"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { NavLink } from "react-router-dom"; | |||
| import * as Yup from "yup"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import i18next from "i18next"; | |||
| import { | |||
| clearLoginErrors, | |||
| fetchUser, | |||
| } from "../../store/actions/login/loginActions"; | |||
| import { selectLoginError } from "../../store/selectors/loginSelectors"; | |||
| import { FORGOT_PASSWORD_PAGE, HOME_PAGE } from "../../constants/pages"; | |||
| import { | |||
| Box, | |||
| Button, | |||
| Container, | |||
| Grid, | |||
| InputAdornment, | |||
| Link, | |||
| Typography, | |||
| } from "@mui/material"; | |||
| import { | |||
| LocationOn, | |||
| LocationOnOutlined, | |||
| Visibility, | |||
| VisibilityOff, | |||
| } from "@mui/icons-material"; | |||
| import Backdrop from "../../components/MUI/BackdropComponent"; | |||
| import ErrorMessage from "../../components/MUI/ErrorMessageComponent"; | |||
| import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors"; | |||
| import { LOGIN_USER_LOADING } from "../../store/actions/login/loginActionConstants"; | |||
| import { TextField } from "../../components/TextFields/TextField/TextField"; | |||
| import { PrimaryButton } from "../../components/Buttons/PrimaryButton/PrimaryButton"; | |||
| import { IconButton } from "../../components/Buttons/IconButton/IconButton"; | |||
| import { Icon } from "../../components/Icon/Icon"; | |||
| import PrimaryButtonWithIcon from "../../components/Buttons/PrimaryButtonWithIcon/PrimaryButtonWithIcon"; | |||
| import TextFieldWithIcon from "../../components/TextFields/TextFieldWithIcon/TextFieldWithIcon"; | |||
| import DropdownList from "../../components/Dropdown/DropdownList/DropdownList"; | |||
| import DropdownItem from "../../components/Dropdown/DropdownItem/DropdownItem"; | |||
| import { CheckBox } from "../../components/CheckBox/CheckBox"; | |||
| import selectedTheme from "../../themes"; | |||
| const LoginPage = ({ history }) => { | |||
| const dispatch = useDispatch(); | |||
| const { t } = useTranslation(); | |||
| const error = useSelector(selectLoginError); | |||
| const [showPassword, setShowPassword] = useState(false); | |||
| const handleClickShowPassword = () => setShowPassword(!showPassword); | |||
| const handleMouseDownPassword = () => setShowPassword(!showPassword); | |||
| // When user refreshes page | |||
| // useEffect(() => { | |||
| // function redirectClient() { | |||
| // if (!tokens.RefreshToken && !tokens.JwtToken) { | |||
| // return; | |||
| // } | |||
| // } | |||
| // redirectClient(); | |||
| // }, [history, tokens]); | |||
| const isLoading = useSelector( | |||
| selectIsLoadingByActionType(LOGIN_USER_LOADING) | |||
| ); | |||
| const handleApiResponseSuccess = () => { | |||
| history.push({ | |||
| pathname: HOME_PAGE, | |||
| state: { | |||
| from: history.location.pathname, | |||
| }, | |||
| }); | |||
| }; | |||
| const handleSubmit = (values) => { | |||
| const { username: Username, password: Password } = values; | |||
| dispatch(clearLoginErrors()); | |||
| dispatch( | |||
| fetchUser({ | |||
| Username, | |||
| Password, | |||
| handleApiResponseSuccess, | |||
| }) | |||
| ); | |||
| }; | |||
| const formik = useFormik({ | |||
| initialValues: { | |||
| username: "", | |||
| password: "", | |||
| }, | |||
| validationSchema: Yup.object().shape({ | |||
| username: Yup.string().required(t("login.usernameRequired")), | |||
| password: Yup.string().required(t("login.passwordRequired")), | |||
| }), | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| }); | |||
| return ( | |||
| <Container component="main" maxWidth="md"> | |||
| <Box | |||
| sx={{ | |||
| marginTop: 32, | |||
| display: "flex", | |||
| flexDirection: "column", | |||
| alignItems: "center", | |||
| }} | |||
| > | |||
| <Typography component="h1" variant="h5"> | |||
| {t("login.logInTitle")} | |||
| </Typography> | |||
| {error && <ErrorMessage error={error} />} | |||
| <Box | |||
| component="form" | |||
| onSubmit={formik.handleSubmit} | |||
| sx={{ position: "relative", mt: 1, p: 1 }} | |||
| > | |||
| <Backdrop position="absolute" isLoading={isLoading} /> | |||
| <TextField | |||
| name="username" | |||
| placeholder={t("common.labelUsername")} | |||
| margin="normal" | |||
| value={formik.values.username} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.username && Boolean(formik.errors.username)} | |||
| helperText={formik.touched.username && formik.errors.username} | |||
| autoFocus | |||
| fullWidth | |||
| /> | |||
| <TextField | |||
| name="password" | |||
| placeholder={t("common.labelPassword")} | |||
| margin="normal" | |||
| type={showPassword ? "text" : "password"} | |||
| value={formik.values.password} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.password && Boolean(formik.errors.password)} | |||
| helperText={formik.touched.password && formik.errors.password} | |||
| fullWidth | |||
| InputProps={{ | |||
| endAdornment: ( | |||
| <IconButton | |||
| onClick={handleClickShowPassword} | |||
| onMouseDown={handleMouseDownPassword} | |||
| > | |||
| {showPassword ? <Visibility /> : <VisibilityOff />} | |||
| </IconButton> | |||
| ), | |||
| }} | |||
| /> | |||
| <DropdownList | |||
| title="Naslov" | |||
| toggleIconOpened={<Visibility />} | |||
| toggleIconClosed={<VisibilityOff />} | |||
| dropdownIcon={<LocationOnOutlined />} | |||
| fullWidth | |||
| defaultOpen={false} | |||
| > | |||
| <DropdownItem> | |||
| <CheckBox fullWidth leftText="Kategorija" rightText="124" /> | |||
| </DropdownItem> | |||
| <DropdownItem> | |||
| <CheckBox fullWidth leftText="Kategorija" rightText="124" /> | |||
| </DropdownItem> | |||
| <DropdownItem> | |||
| <CheckBox fullWidth leftText="Kategorija" rightText="124" /> | |||
| </DropdownItem> | |||
| <DropdownItem> | |||
| <CheckBox fullWidth leftText="Kategorija" rightText="124" /> | |||
| </DropdownItem> | |||
| <DropdownItem> | |||
| <CheckBox fullWidth leftText="Kategorija" rightText="124" /> | |||
| </DropdownItem> | |||
| <DropdownItem> | |||
| <CheckBox fullWidth leftText="Kategorija" rightText="124" /> | |||
| </DropdownItem> | |||
| <DropdownItem> | |||
| <CheckBox fullWidth leftText="Kategorija" rightText="124" /> | |||
| </DropdownItem> | |||
| </DropdownList> | |||
| <PrimaryButtonWithIcon | |||
| icon={<Visibility />} | |||
| sx={{ mt: 3, mb: 2 }} | |||
| buttonProps={{ | |||
| type: "submit", | |||
| variant: "contained", | |||
| height: "40px", | |||
| fullWidth: true, | |||
| buttoncolor: selectedTheme.primaryPurple, | |||
| textcolor: "white", | |||
| }} | |||
| > | |||
| {t("login.logIn")} | |||
| </PrimaryButtonWithIcon> | |||
| <Grid container> | |||
| <Grid | |||
| item | |||
| xs={12} | |||
| md={6} | |||
| sx={{ textAlign: { xs: "center", md: "left" } }} | |||
| > | |||
| <Link | |||
| to={FORGOT_PASSWORD_PAGE} | |||
| component={NavLink} | |||
| variant="body2" | |||
| underline="hover" | |||
| > | |||
| {t("login.forgotYourPassword")} | |||
| </Link> | |||
| </Grid> | |||
| <Grid | |||
| item | |||
| xs={12} | |||
| md={6} | |||
| sx={{ textAlign: { xs: "center", md: "right" } }} | |||
| > | |||
| <Link | |||
| to="#" | |||
| component={NavLink} | |||
| variant="body2" | |||
| underline="hover" | |||
| > | |||
| {t("login.dontHaveAccount")} | |||
| </Link> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| </Box> | |||
| </Container> | |||
| ); | |||
| }; | |||
| LoginPage.propTypes = { | |||
| history: PropTypes.shape({ | |||
| replace: PropTypes.func, | |||
| push: PropTypes.func, | |||
| location: PropTypes.shape({ | |||
| pathname: PropTypes.string, | |||
| }), | |||
| }), | |||
| }; | |||
| export default LoginPage; | |||
| @@ -68,12 +68,6 @@ const FirstPartOfRegistration = (props) => { | |||
| fullWidth | |||
| /> | |||
| {formik.errors.mail && formik.touched.mail ? ( | |||
| <ErrorMessage>{formik.errors.mail}</ErrorMessage> | |||
| ) : ( | |||
| <></> | |||
| )} | |||
| <TextField | |||
| name="password" | |||
| placeholder={t("common.labelPassword")} | |||
| @@ -94,11 +88,15 @@ const FirstPartOfRegistration = (props) => { | |||
| ), | |||
| }} | |||
| /> | |||
| {formik.errors.password && formik.touched.password ? ( | |||
| {formik.errors.mail && formik.touched.mail ? ( | |||
| <ErrorMessage>{formik.errors.mail}</ErrorMessage> | |||
| ) : formik.errors.password && formik.touched.password ? ( | |||
| <ErrorMessage>{formik.errors.password}</ErrorMessage> | |||
| ) : ( | |||
| <></> | |||
| )} | |||
| {props.error && <ErrorMessage>{props.errorMessage}</ErrorMessage>} | |||
| <PrimaryButton | |||
| @@ -6,8 +6,6 @@ import randomDataReducer from "./randomData/randomDataReducer"; | |||
| import storage from "redux-persist/lib/storage"; | |||
| import createFilter from "redux-persist-transform-filter"; | |||
| import persistReducer from "redux-persist/es/persistReducer"; | |||
| import filtersReducer from "./filters/filtersReducer"; | |||
| import offersReducer from "./offers/offersReducer"; | |||
| const loginPersistConfig = { | |||
| key: "login", | |||
| @@ -41,7 +39,5 @@ export default combineReducers({ | |||
| login: persistReducer(loginPersistConfig, loginReducer), | |||
| user: persistReducer(userPersistConfig, userReducer), | |||
| loading: loadingReducer, | |||
| filters: filtersReducer, | |||
| randomData: persistReducer(randomDataPersistConfig, randomDataReducer), | |||
| offers: offersReducer | |||
| }); | |||
| @@ -1,7 +1,6 @@ | |||
| import { all } from 'redux-saga/effects'; | |||
| import forgotPasswordSaga from './forgotPasswordSaga'; | |||
| import loginSaga from './loginSaga'; | |||
| import offersSaga from './offersSaga'; | |||
| import registerSaga from './registerSaga'; | |||
| export default function* rootSaga() { | |||
| @@ -9,6 +8,5 @@ export default function* rootSaga() { | |||
| loginSaga(), | |||
| registerSaga(), | |||
| forgotPasswordSaga(), | |||
| offersSaga() | |||
| ]); | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| import * as Yup from "yup"; | |||
| import i18n from "../i18n"; | |||
| export default Yup.object().shape({ | |||
| email: Yup.string() | |||
| .required(i18n.t("forgotPassword.emailRequired")) | |||
| .email(i18n.t("forgotPassword.emailFormat")), | |||
| }); | |||
| @@ -0,0 +1,9 @@ | |||
| import * as Yup from "yup"; | |||
| import i18n from "../i18n"; | |||
| export default Yup.object().shape({ | |||
| email: Yup.string().email(i18n.t("login.emailFormat")).required(i18n.t("login.mailRequired")), | |||
| password: Yup.string() | |||
| .required(i18n.t("login.passwordRequired")) | |||
| .min(8, i18n.t("login.passwordLength")), | |||
| }); | |||
| @@ -0,0 +1,10 @@ | |||
| import * as Yup from "yup"; | |||
| import i18n from "../../i18n"; | |||
| export default Yup.object().shape({ | |||
| mail: Yup.string() | |||
| .email(i18n.t("forgotPassword.emailFormat")) | |||
| .required(i18n.t("login.usernameRequired")), | |||
| password: Yup.string() | |||
| .required(i18n.t("login.passwordRequired")) | |||
| .min(8, i18n.t("login.passwordLength")), | |||
| }); | |||
| @@ -0,0 +1,10 @@ | |||
| import * as Yup from "yup"; | |||
| import i18n from "../../i18n"; | |||
| export default Yup.object().shape({ | |||
| nameOfFirm: Yup.string().required(i18n.t("login.usernameRequired")), | |||
| PIB: Yup.number() | |||
| .required(i18n.t("login.passwordRequired")) | |||
| .min(100000000, i18n.t("register.PIBnoOfCharacters")) | |||
| .max(999999999, i18n.t("register.PIBnoOfCharacters")), | |||
| }); | |||
| @@ -0,0 +1,10 @@ | |||
| import * as Yup from "yup"; | |||
| import i18n from "../../i18n"; | |||
| export default Yup.object().shape({ | |||
| phoneNumber: Yup.number().required(i18n.t("login.usernameRequired")), | |||
| location: Yup.string().required(i18n.t("login.passwordRequired")), | |||
| website: Yup.string().matches( | |||
| /^((ftp|http|https):\/\/)?(www.)?(?!.*(ftp|http|https|www.))[a-zA-Z0-9_-]+(\.[a-zA-Z]+)+((\/)[\w#]+)*(\/\w+\?[a-zA-Z0-9_]+=\w+(&[a-zA-Z0-9_]+=\w+)*)?$/gm | |||
| ), | |||
| }); | |||
| @@ -0,0 +1,6 @@ | |||
| import * as Yup from "yup"; | |||
| export default Yup.object().shape({ | |||
| password: Yup.string().required().min(8), | |||
| passwordConfirm: Yup.string().oneOf([Yup.ref("password"), null]), | |||
| }); | |||