#17 Added ad management page ui

Unito
safet.purkovic ha unito 1 commit da feature/1512_ad_management_page_ui a FE_dev 3 anni fa

+ 13
- 15
src/AppRoutes.js Vedi File

@@ -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;

BIN
src/assets/images/.net_icon.png Vedi File


BIN
src/assets/images/arrow_left.png Vedi File


BIN
src/assets/images/arrow_right.png Vedi File


BIN
src/assets/images/filter_vector.png Vedi File


BIN
src/assets/images/logo_react.png Vedi File


+ 275
- 0
src/assets/styles/components/_ads.scss Vedi File

@@ -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;
}

+ 32
- 0
src/components/Ads/Ad.js Vedi File

@@ -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;

+ 9
- 0
src/components/Ads/AdFilters.js Vedi File

@@ -0,0 +1,9 @@
import React from 'react'

const AdFilters = () => {
return (
<div>AdFilter</div>
)
}

export default AdFilters

+ 43
- 0
src/components/Ads/AddAdModal.js Vedi File

@@ -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;

+ 23
- 0
src/components/Ads/ArchiveAd.js Vedi File

@@ -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;

+ 10
- 3
src/components/MUI/NavbarComponent.js Vedi File

@@ -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> */}

+ 1
- 0
src/constants/pages.js Vedi File

@@ -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';

+ 4
- 0
src/i18n/resources/rs.js Vedi File

@@ -106,5 +106,9 @@ nav:{
stats: 'Statistika',
users: 'Korisnici',
signOut: 'Izloguj se'
},
ads: {
activeAds: "Aktivni Oglasi",
archiveAds: "Arhiva"
}
};

+ 1
- 1
src/main.scss Vedi File

@@ -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

+ 93
- 0
src/pages/AdsPage/AdsPage.js Vedi File

@@ -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;

Loading…
Annulla
Salva