Просмотр исходного кода

Added ad management page ui

feature/1512_ad_management_page_ui
Ermin Bronja 3 лет назад
Родитель
Сommit
5593def202
16 измененных файлов: 504 добавлений и 19 удалений
  1. 13
    15
      src/AppRoutes.js
  2. Двоичные данные
      src/assets/images/.net_icon.png
  3. Двоичные данные
      src/assets/images/arrow_left.png
  4. Двоичные данные
      src/assets/images/arrow_right.png
  5. Двоичные данные
      src/assets/images/filter_vector.png
  6. Двоичные данные
      src/assets/images/logo_react.png
  7. 275
    0
      src/assets/styles/components/_ads.scss
  8. 32
    0
      src/components/Ads/Ad.js
  9. 9
    0
      src/components/Ads/AdFilters.js
  10. 43
    0
      src/components/Ads/AddAdModal.js
  11. 23
    0
      src/components/Ads/ArchiveAd.js
  12. 10
    3
      src/components/MUI/NavbarComponent.js
  13. 1
    0
      src/constants/pages.js
  14. 4
    0
      src/i18n/resources/rs.js
  15. 1
    1
      src/main.scss
  16. 93
    0
      src/pages/AdsPage/AdsPage.js

+ 13
- 15
src/AppRoutes.js Просмотреть файл

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 { import {
LOGIN_PAGE, LOGIN_PAGE,
HOME_PAGE, HOME_PAGE,
ADS_PAGE,
FORGOT_PASSWORD_PAGE, FORGOT_PASSWORD_PAGE,
NOT_FOUND_PAGE, NOT_FOUND_PAGE,
ERROR_PAGE, ERROR_PAGE,
BASE_PAGE, BASE_PAGE,
} from './constants/pages';
} from "./constants/pages";


// import LoginPage from './pages/LoginPage/LoginPage'; // 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/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/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 = () => ( const AppRoutes = () => (
<Switch> <Switch>
<Route path={NOT_FOUND_PAGE} component={NotFoundPage} /> <Route path={NOT_FOUND_PAGE} component={NotFoundPage} />
<Route path={ERROR_PAGE} component={ErrorPage} /> <Route path={ERROR_PAGE} component={ErrorPage} />
<Route path={FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} /> <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} /> <Redirect from="*" to={NOT_FOUND_PAGE} />
</Switch> </Switch>
); );



export default AppRoutes; export default AppRoutes;

Двоичные данные
src/assets/images/.net_icon.png Просмотреть файл


Двоичные данные
src/assets/images/arrow_left.png Просмотреть файл


Двоичные данные
src/assets/images/arrow_right.png Просмотреть файл


Двоичные данные
src/assets/images/filter_vector.png Просмотреть файл


Двоичные данные
src/assets/images/logo_react.png Просмотреть файл


+ 275
- 0
src/assets/styles/components/_ads.scss Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

import React from 'react'

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

export default AdFilters

+ 43
- 0
src/components/Ads/AddAdModal.js Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

import UserProfile from "../Profile/UserProfile"; import UserProfile from "../Profile/UserProfile";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { userSelector } from "../../store/selectors/userSelectors"; import { userSelector } from "../../store/selectors/userSelectors";
import { Link } from "react-router-dom";


const NavbarComponent = () => { const NavbarComponent = () => {
const navItems = [ const navItems = [
let btnRef = useRef(); let btnRef = useRef();


// get authenticated user // get authenticated user
const user = useSelector(userSelector)
const user = useSelector(userSelector);


const { t } = useTranslation(); const { t } = useTranslation();


useEffect(() => { useEffect(() => {
let handler = (e) => { let handler = (e) => {
if (userRef.current) { 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); setPreview(false);
} }
} }
// color: "text.primary", // color: "text.primary",
marginY: "0", marginY: "0",
padding: "0", padding: "0",
textDecoration: "none"
}} }}
className="text-black" className="text-black"
as={Link}
to={n}
> >
{t("nav." + n)} {t("nav." + n)}
</Typography> </Typography>
</div> </div>
</Box> </Box>
)} )}
{!matches && <UserProfile innerRef={userRef} show={preview}/>}
{!matches && <UserProfile innerRef={userRef} show={preview} />}
{/* <Box> {/* <Box>
<MenuList /> <MenuList />
</Box> */} </Box> */}

+ 1
- 0
src/constants/pages.js Просмотреть файл

export const LOGIN_PAGE = '/login'; export const LOGIN_PAGE = '/login';
export const FORGOT_PASSWORD_PAGE = '/forgot-password'; export const FORGOT_PASSWORD_PAGE = '/forgot-password';
export const HOME_PAGE = '/home'; export const HOME_PAGE = '/home';
export const ADS_PAGE = '/ads';
export const ERROR_PAGE = '/error-page'; export const ERROR_PAGE = '/error-page';
export const NOT_FOUND_PAGE = '/not-found'; export const NOT_FOUND_PAGE = '/not-found';

+ 4
- 0
src/i18n/resources/rs.js Просмотреть файл

stats: 'Statistika', stats: 'Statistika',
users: 'Korisnici', users: 'Korisnici',
signOut: 'Izloguj se' signOut: 'Izloguj se'
},
ads: {
activeAds: "Aktivni Oglasi",
archiveAds: "Arhiva"
} }
}; };

+ 1
- 1
src/main.scss Просмотреть файл

@import './assets/styles/components/error-page'; @import './assets/styles/components/error-page';
@import './assets/styles/components/rules'; @import './assets/styles/components/rules';
@import './assets/styles/components/nav'; @import './assets/styles/components/nav';
@import './assets/styles/components/ads';
@import './assets/styles/layout'; @import './assets/styles/layout';
@import './assets/styles/overwrite'; @import './assets/styles/overwrite';
@import './assets/styles/utility'; @import './assets/styles/utility';
.h-withHeader{ .h-withHeader{
height: calc(100vh - $navHeight); height: calc(100vh - $navHeight);
// remove css line below, just for showcase // remove css line below, just for showcase
background-color: $mainBlue;
} }


// ================= mui overrides // ================= mui overrides

+ 93
- 0
src/pages/AdsPage/AdsPage.js Просмотреть файл

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;

Загрузка…
Отмена
Сохранить