feature/1512_ad_management_page_ui до FE_dev 3 роки тому
| @@ -1,24 +1,26 @@ | |||
| import React from 'react'; | |||
| import { Redirect, Route, Switch } from 'react-router-dom'; | |||
| import React from "react"; | |||
| import { Redirect, Route, Switch } from "react-router-dom"; | |||
| import { | |||
| LOGIN_PAGE, | |||
| HOME_PAGE, | |||
| ADS_PAGE, | |||
| FORGOT_PASSWORD_PAGE, | |||
| NOT_FOUND_PAGE, | |||
| ERROR_PAGE, | |||
| BASE_PAGE, | |||
| } from './constants/pages'; | |||
| } from "./constants/pages"; | |||
| // import LoginPage from './pages/LoginPage/LoginPage'; | |||
| import LoginPage from './pages/LoginPage/LoginPageMUI'; | |||
| import LoginPage from "./pages/LoginPage/LoginPageMUI"; | |||
| // import HomePage from './pages/HomePage/HomePage'; | |||
| import HomePage from './pages/HomePage/HomePageMUI'; | |||
| import NotFoundPage from './pages/ErrorPages/NotFoundPage'; | |||
| import ErrorPage from './pages/ErrorPages/ErrorPage'; | |||
| import HomePage from "./pages/HomePage/HomePageMUI"; | |||
| import AdsPage from "./pages/AdsPage/AdsPage"; | |||
| import NotFoundPage from "./pages/ErrorPages/NotFoundPage"; | |||
| import ErrorPage from "./pages/ErrorPages/ErrorPage"; | |||
| // import ForgotPasswordPage from './pages/ForgotPasswordPage/ForgotPasswordPage'; | |||
| import ForgotPasswordPage from './pages/ForgotPasswordPage/ForgotPasswordPageMUI'; | |||
| import PrivateRoute from './components/Router/PrivateRoute'; | |||
| import ForgotPasswordPage from "./pages/ForgotPasswordPage/ForgotPasswordPageMUI"; | |||
| import PrivateRoute from "./components/Router/PrivateRoute"; | |||
| const AppRoutes = () => ( | |||
| <Switch> | |||
| @@ -27,14 +29,10 @@ const AppRoutes = () => ( | |||
| <Route path={NOT_FOUND_PAGE} component={NotFoundPage} /> | |||
| <Route path={ERROR_PAGE} component={ErrorPage} /> | |||
| <Route path={FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} /> | |||
| <PrivateRoute | |||
| exact | |||
| path={HOME_PAGE} | |||
| component={HomePage} | |||
| /> | |||
| <PrivateRoute exact path={HOME_PAGE} component={HomePage} /> | |||
| <PrivateRoute exact path={ADS_PAGE} component={AdsPage} /> | |||
| <Redirect from="*" to={NOT_FOUND_PAGE} /> | |||
| </Switch> | |||
| ); | |||
| export default AppRoutes; | |||
| @@ -0,0 +1,275 @@ | |||
| h1, | |||
| h3 { | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| .ads { | |||
| margin-top: 36px; | |||
| padding-left: 72px; | |||
| } | |||
| .active-ads-header { | |||
| padding-left: 81px; | |||
| display: flex; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| padding-right: 5rem; | |||
| } | |||
| .filter-vector { | |||
| margin-left: 0.5rem !important; | |||
| } | |||
| .active-ads-ads { | |||
| display: flex; | |||
| margin-top: 39px; | |||
| position: relative; | |||
| } | |||
| .active-ads-ads-a { | |||
| position: absolute; | |||
| top: 50%; | |||
| transform: translate(0, -50%); | |||
| } | |||
| .active-ads-ads-arrows { | |||
| display: flex; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| justify-content: center; | |||
| } | |||
| .active-ads-ads-arrows button { | |||
| margin: 9px 0; | |||
| box-sizing: border-box; | |||
| width: 45px; | |||
| height: 45px; | |||
| background: #ffffff; | |||
| border: 1px solid #e4e4e4; | |||
| border-radius: 9px; | |||
| cursor: pointer; | |||
| } | |||
| .active-ads-ads-ad { | |||
| padding-left: 81px; | |||
| display: flex; | |||
| } | |||
| .archived-ads { | |||
| margin-top: 56px; | |||
| } | |||
| .archived-ads-header { | |||
| padding-left: 81px; | |||
| } | |||
| .archived-ads-ads { | |||
| display: flex; | |||
| margin-top: 27px; | |||
| position: relative; | |||
| } | |||
| .archived-ads-ads-a { | |||
| position: absolute; | |||
| top: 50%; | |||
| transform: translate(0, -50%); | |||
| } | |||
| .archived-ads-ads-arrows { | |||
| display: flex; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| justify-content: center; | |||
| } | |||
| .archived-ads-ads-arrows button { | |||
| margin: 9px 0; | |||
| box-sizing: border-box; | |||
| width: 45px; | |||
| height: 45px; | |||
| background: #ffffff; | |||
| border: 1px solid #e4e4e4; | |||
| border-radius: 9px; | |||
| cursor: pointer; | |||
| } | |||
| .archived-ads-ads-ad { | |||
| padding-left: 81px; | |||
| display: flex; | |||
| } | |||
| .archive-ad { | |||
| box-sizing: border-box; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| padding: 36px; | |||
| gap: 18px; | |||
| width: 247px; | |||
| height: 215px; | |||
| left: 1px; | |||
| top: 0px; | |||
| background: #ffffff; | |||
| border: 1px solid #e4e4e4; | |||
| border-radius: 12px; | |||
| margin-right: 27px; | |||
| } | |||
| .archive-ad-date p { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| font-size: 12px; | |||
| line-height: 15px; | |||
| color: #272727; | |||
| flex: none; | |||
| order: 0; | |||
| flex-grow: 0; | |||
| } | |||
| .archive-ad-title h3 { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| font-size: 16px; | |||
| line-height: 20px; | |||
| color: #226cb0; | |||
| flex: none; | |||
| order: 1; | |||
| flex-grow: 0; | |||
| } | |||
| .archive-ad-image img { | |||
| width: 49px; | |||
| height: 39px; | |||
| flex: none; | |||
| order: 2; | |||
| flex-grow: 0; | |||
| } | |||
| .archive-ad-experience p { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| font-size: 12px; | |||
| line-height: 15px; | |||
| color: #272727; | |||
| flex: none; | |||
| order: 3; | |||
| flex-grow: 0; | |||
| } | |||
| .ad-card { | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: center; | |||
| align-items: center; | |||
| width: 425px; | |||
| height: 370px; | |||
| padding: 72px; | |||
| background: #ffffff; | |||
| border: 1px solid #e4e4e4; | |||
| border-radius: 18px; | |||
| gap: 18px; | |||
| margin-right: 36px; | |||
| } | |||
| .ad-card-date p { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| font-size: 12px; | |||
| line-height: 15px; | |||
| color: #272727; | |||
| flex: none; | |||
| order: 0; | |||
| flex-grow: 0; | |||
| } | |||
| .ad-card-title h3 { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| font-size: 24px; | |||
| line-height: 32px; | |||
| letter-spacing: 0.02em; | |||
| color: #226cb0; | |||
| flex: none; | |||
| order: 1; | |||
| flex-grow: 0; | |||
| } | |||
| .ad-card-logo img { | |||
| width: 61px; | |||
| height: 49px; | |||
| flex: none; | |||
| order: 2; | |||
| flex-grow: 0; | |||
| } | |||
| .ad-card-experience p { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| font-size: 16px; | |||
| line-height: 20px; | |||
| color: #272727; | |||
| flex: none; | |||
| order: 3; | |||
| flex-grow: 0; | |||
| } | |||
| .ad-card-buttons { | |||
| display: flex; | |||
| flex-direction: row; | |||
| align-items: flex-start; | |||
| justify-content: center; | |||
| padding: 0px; | |||
| gap: 18px; | |||
| width: 281px; | |||
| height: 38px; | |||
| flex: none; | |||
| order: 4; | |||
| flex-grow: 0; | |||
| } | |||
| .ad-card-buttons button { | |||
| box-sizing: border-box; | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: center; | |||
| align-items: center; | |||
| padding: 9px; | |||
| gap: 10px; | |||
| width: 76px; | |||
| height: 38px; | |||
| border: 1px solid #e4e4e4; | |||
| border-radius: 9px; | |||
| flex: none; | |||
| order: 0; | |||
| flex-grow: 0; | |||
| } | |||
| .add-ad { | |||
| margin-top: 49px; | |||
| display: flex; | |||
| justify-content: flex-end; | |||
| align-items: center; | |||
| padding-right: 5rem !important; | |||
| padding-bottom: 49px; | |||
| } | |||
| .add-ad-btn { | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: center; | |||
| align-items: center; | |||
| padding: 18px 72px; | |||
| gap: 10px; | |||
| width: 201px; | |||
| height: 51px; | |||
| background: #226cb0; | |||
| border-radius: 9px; | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| import React from "react"; | |||
| import logoReact from "../../assets/images/logo_react.png"; | |||
| const Ad = () => { | |||
| return ( | |||
| <div className="ad-card"> | |||
| <div className="ad-card-date"> | |||
| <p>30.09.22 - 30.10.22</p> | |||
| </div> | |||
| <div className="ad-card-title"> | |||
| <h3>React Developer</h3> | |||
| </div> | |||
| <div className="ad-card-logo"> | |||
| <img src={logoReact} alt="logo-react" /> | |||
| </div> | |||
| <div className="ad-card-experience"> | |||
| <p>3+ years of experience</p> | |||
| </div> | |||
| <div className="ad-card-buttons"> | |||
| <button>LinkedIn</button> | |||
| <button>Facebook</button> | |||
| <button disabled>Instagram</button> | |||
| </div> | |||
| </div> | |||
| ); | |||
| }; | |||
| export default Ad; | |||
| @@ -0,0 +1,9 @@ | |||
| import React from 'react' | |||
| const AdFilters = () => { | |||
| return ( | |||
| <div>AdFilter</div> | |||
| ) | |||
| } | |||
| export default AdFilters | |||
| @@ -0,0 +1,43 @@ | |||
| import React from "react"; | |||
| import PropType from "prop-types"; | |||
| import Box from "@mui/material/Box"; | |||
| import Modal from "@mui/material/Modal"; | |||
| const style = { | |||
| position: "absolute", | |||
| top: "50%", | |||
| left: "50%", | |||
| transform: "translate(-50%, -50%)", | |||
| width: 400, | |||
| bgcolor: "background.paper", | |||
| border: "2px solid #000", | |||
| boxShadow: 24, | |||
| pt: 2, | |||
| px: 4, | |||
| pb: 3, | |||
| }; | |||
| const AddAdModal = ({ open, handleClose }) => { | |||
| return ( | |||
| <Modal | |||
| open={open} | |||
| onClose={handleClose} | |||
| aria-labelledby="parent-modal-title" | |||
| aria-describedby="parent-modal-description" | |||
| > | |||
| <Box sx={{ ...style, width: 400 }}> | |||
| <h2 id="parent-modal-title">Text in a modal</h2> | |||
| <p id="parent-modal-description"> | |||
| Duis mollis, est non commodo luctus, nisi erat porttitor ligula. | |||
| </p> | |||
| </Box> | |||
| </Modal> | |||
| ); | |||
| }; | |||
| AddAdModal.propTypes = { | |||
| open: PropType.any, | |||
| handleClose: PropType.func | |||
| }; | |||
| export default AddAdModal; | |||
| @@ -0,0 +1,23 @@ | |||
| import React from "react"; | |||
| import net_icon from "../../assets/images/.net_icon.png"; | |||
| const ArchiveAd = () => { | |||
| return ( | |||
| <div className="archive-ad"> | |||
| <div className="archive-ad-date"> | |||
| <p>05.07.22 - 20.08.22</p> | |||
| </div> | |||
| <div className="archive-ad-title"> | |||
| <h3>.NET Intern</h3> | |||
| </div> | |||
| <div className="archive-ad-image"> | |||
| <img src={net_icon} alt=".net icon" /> | |||
| </div> | |||
| <div className="archive-ad-experience"> | |||
| <p>No experience needed</p> | |||
| </div> | |||
| </div> | |||
| ); | |||
| }; | |||
| export default ArchiveAd; | |||
| @@ -32,6 +32,7 @@ import LogoutIcon from "@mui/icons-material/Logout"; | |||
| import UserProfile from "../Profile/UserProfile"; | |||
| import { useSelector } from "react-redux"; | |||
| import { userSelector } from "../../store/selectors/userSelectors"; | |||
| import { Link } from "react-router-dom"; | |||
| const NavbarComponent = () => { | |||
| const navItems = [ | |||
| @@ -54,7 +55,7 @@ const NavbarComponent = () => { | |||
| let btnRef = useRef(); | |||
| // get authenticated user | |||
| const user = useSelector(userSelector) | |||
| const user = useSelector(userSelector); | |||
| const { t } = useTranslation(); | |||
| @@ -69,7 +70,10 @@ const NavbarComponent = () => { | |||
| useEffect(() => { | |||
| let handler = (e) => { | |||
| if (userRef.current) { | |||
| if (!userRef.current.contains(e.target) && !btnRef.current.contains(e.target)) { | |||
| if ( | |||
| !userRef.current.contains(e.target) && | |||
| !btnRef.current.contains(e.target) | |||
| ) { | |||
| setPreview(false); | |||
| } | |||
| } | |||
| @@ -264,8 +268,11 @@ const NavbarComponent = () => { | |||
| // color: "text.primary", | |||
| marginY: "0", | |||
| padding: "0", | |||
| textDecoration: "none" | |||
| }} | |||
| className="text-black" | |||
| as={Link} | |||
| to={n} | |||
| > | |||
| {t("nav." + n)} | |||
| </Typography> | |||
| @@ -283,7 +290,7 @@ const NavbarComponent = () => { | |||
| </div> | |||
| </Box> | |||
| )} | |||
| {!matches && <UserProfile innerRef={userRef} show={preview}/>} | |||
| {!matches && <UserProfile innerRef={userRef} show={preview} />} | |||
| {/* <Box> | |||
| <MenuList /> | |||
| </Box> */} | |||
| @@ -2,5 +2,6 @@ export const BASE_PAGE = '/'; | |||
| export const LOGIN_PAGE = '/login'; | |||
| export const FORGOT_PASSWORD_PAGE = '/forgot-password'; | |||
| export const HOME_PAGE = '/home'; | |||
| export const ADS_PAGE = '/ads'; | |||
| export const ERROR_PAGE = '/error-page'; | |||
| export const NOT_FOUND_PAGE = '/not-found'; | |||
| @@ -106,5 +106,9 @@ nav:{ | |||
| stats: 'Statistika', | |||
| users: 'Korisnici', | |||
| signOut: 'Izloguj se' | |||
| }, | |||
| ads: { | |||
| activeAds: "Aktivni Oglasi", | |||
| archiveAds: "Arhiva" | |||
| } | |||
| }; | |||
| @@ -20,6 +20,7 @@ | |||
| @import './assets/styles/components/error-page'; | |||
| @import './assets/styles/components/rules'; | |||
| @import './assets/styles/components/nav'; | |||
| @import './assets/styles/components/ads'; | |||
| @import './assets/styles/layout'; | |||
| @import './assets/styles/overwrite'; | |||
| @import './assets/styles/utility'; | |||
| @@ -36,7 +37,6 @@ | |||
| .h-withHeader{ | |||
| height: calc(100vh - $navHeight); | |||
| // remove css line below, just for showcase | |||
| background-color: $mainBlue; | |||
| } | |||
| // ================= mui overrides | |||
| @@ -0,0 +1,93 @@ | |||
| import React, { useState } from "react"; | |||
| import Ad from "../../components/Ads/Ad"; | |||
| import ArchiveAd from "../../components/Ads/ArchiveAd"; | |||
| import IconButton from "../../components/IconButton/IconButton"; | |||
| import filterVector from "../../assets/images/filter_vector.png"; | |||
| import arrow_left from "../../assets/images/arrow_left.png"; | |||
| import arrow_right from "../../assets/images/arrow_right.png"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import AddAdModal from "../../components/Ads/AddAdModal"; | |||
| import AdFilters from "../../components/Ads/AdFilters"; | |||
| const AdsPage = () => { | |||
| const [showFilterComponent, setShowFilterComponent] = useState(false); | |||
| const [toggleModal, setToggleModal] = useState(false); | |||
| const { t } = useTranslation(); | |||
| const handleToggleModal = () => { | |||
| setToggleModal((oldState) => !oldState); | |||
| }; | |||
| return ( | |||
| <> | |||
| <div className="l-t-rectangle"></div> | |||
| <div className="r-b-rectangle"></div> | |||
| {showFilterComponent && <AdFilters />} | |||
| <AddAdModal open={toggleModal} handleClose={handleToggleModal} /> | |||
| <div className="ads"> | |||
| <div className="active-ads"> | |||
| <div className="active-ads-header"> | |||
| <h1>{t("ads.activeAds")}</h1> | |||
| <IconButton | |||
| sx={{ marginLeft: "15px" }} | |||
| className="c-btn c-btn--primary-outlined" | |||
| onClick={() => setShowFilterComponent((oldState) => !oldState)} | |||
| > | |||
| Filteri{" "} | |||
| <img src={filterVector} alt="filter" className="filter-vector" /> | |||
| </IconButton> | |||
| </div> | |||
| <div className="active-ads-ads"> | |||
| <div className="active-ads-ads-a"> | |||
| <div className="active-ads-ads-arrows"> | |||
| <button> | |||
| <img src={arrow_left} alt="arrow-left" /> | |||
| </button> | |||
| <button> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <div className="active-ads-ads-ad"> | |||
| <Ad /> | |||
| <Ad /> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div className="archived-ads"> | |||
| <div className="archived-ads-header"> | |||
| <h2>{t("ads.archiveAds")}</h2> | |||
| </div> | |||
| <div className="archived-ads-ads"> | |||
| <div className="archived-ads-ads-a"> | |||
| <div className="archived-ads-ads-arrows"> | |||
| <button> | |||
| <img src={arrow_left} alt="arrow-left" /> | |||
| </button> | |||
| <button> | |||
| <img src={arrow_right} alt="arrow-right" /> | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <div className="archived-ads-ads-ad"> | |||
| <ArchiveAd /> | |||
| <ArchiveAd /> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div className="add-ad"> | |||
| <IconButton | |||
| className="c-btn c-btn--primary add-ad-btn" | |||
| onClick={handleToggleModal} | |||
| > | |||
| + Oglas | |||
| </IconButton> | |||
| </div> | |||
| </> | |||
| ); | |||
| }; | |||
| export default AdsPage; | |||