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

Changed design for creating ad

pull/126/head
Ermin Bronja 3 лет назад
Родитель
Сommit
649c63c834

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

@@ -21,7 +21,8 @@ import {
PATTERN_DETAILS_PAGE,
SCHEDULE_PAGE,
STATS_PAGE,
REGISTER_PAGE
REGISTER_PAGE,
CREATE_AD_PAGE
} from "./constants/pages";

// import LoginPage from './pages/LoginPage/LoginPage';
@@ -48,6 +49,7 @@ import PatternDetailsPage from "./pages/PatternsPage/PatternDetailsPage";
import SchedulePage from "./pages/SchedulePage/SchedulePage";
import StatsPage from "./pages/StatsPage/StatsPage";
import RegisterPage from "./pages/RegisterPage/RegisterPage";
import CreateAdPage from "./pages/AdsPage/CreateAdPage";

const AppRoutes = () => (
<Switch>
@@ -68,6 +70,7 @@ const AppRoutes = () => (
<PrivateRoute exact path={USER_DETAILS_PAGE} component={UserDetails} />
<PrivateRoute exact path={USERS_PAGE} component={UsersPage} />
<PrivateRoute exact path={CANDIDATES_PAGE} component={CandidatesPage} />
<PrivateRoute exact path={CREATE_AD_PAGE} component={CreateAdPage} />
<PrivateRoute
exact
path={CANDIDATES_DETAILS_PAGE}

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

@@ -1038,3 +1038,134 @@ h3 {
text-decoration: underline;
font-size: 16px;
}

.create-ad-page {
display: flex;
align-items: center;
justify-content: center;
height: 100% !important;
position: relative;
}

.create-ad-page-content {
max-width: 630px !important;
min-width: 630px !important;
@include media-below($bp-xl) {
padding: 20px;
min-width: 0 !important;
}
}

.create-ad-page-content-header {
display: flex;
align-items: center;
gap: 9px;
margin-bottom: 36px;
}

.create-ad-page-content-header sub {
color: $mainBlue;
}

.create-ad-form-control {
display: flex;
flex-direction: column;
margin-bottom: 18px;
}

.create-ad-form-control label {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
color: #272727;
margin-bottom: 4.5px;
}

.create-ad-form-control input {
box-sizing: border-box;
display: flex;
flex-direction: row;
align-items: center;
padding: 18px;
gap: 10px;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 7px;
}

.create-ad-form-control-buttons {
display: flex;
}

.create-ad-form-control-buttons > button {
margin-right: 9px;
}

.create-ad-buttons {
display: flex;
justify-content: flex-end;
flex-wrap: wrap;
}

.create-ad-buttons-back {
margin-left: 18px;
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 18px 72px;
gap: 10px;
background: #f4f4f4;
border: 1px solid #e4e4e4;
border-radius: 9px;
margin-left: 9px;
cursor: pointer;
@include media-below($bp-xl) {
margin-bottom: 9px !important;
}
}

.create-ad-buttons-forward {
box-sizing: border-box;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 18px 72px;
gap: 10px;
background: #226cb0;
border-radius: 9px;
border: none;
color: white;
margin-left: 9px;
cursor: pointer;
@include media-below($bp-xl) {
margin-bottom: 9px !important;
}
}

.create-ad-go-back {
position: absolute;
right: 72px;
bottom: 36px;
}

.create-ad-go-back button {
border: none;
background-color: transparent;
padding: 0;
outline: none;
margin: 0;
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
text-align: right;
text-decoration-line: underline;
color: #226cb0;
cursor: pointer;
}

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

@@ -18,3 +18,4 @@ export const PATTERN_DETAILS_PAGE = '/patterns/:id';
export const SCHEDULE_PAGE = '/schedule'
export const STATS_PAGE = '/statistics';
export const REGISTER_PAGE = '/register';
export const CREATE_AD_PAGE = '/create-ad';

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

@@ -7,7 +7,6 @@ import arrow_left from "../../assets/images/arrow_left.png";
import arrow_right from "../../assets/images/arrow_right.png";
import searchImage from "../../assets/images/search.png";
import { useTranslation } from "react-i18next";
import AddAdModal from "../../components/Ads/AddAdModal";
import AdFilters from "../../components/Ads/AdFilters";
import { useDispatch } from "react-redux";
import {
@@ -16,7 +15,7 @@ import {
} from "../../store/actions/ads/adsAction";
import { useSelector } from "react-redux";
import { selectAds } from "../../store/selectors/adsSelectors";
import { AD_DETAILS_PAGE } from "../../constants/pages";
import { AD_DETAILS_PAGE, CREATE_AD_PAGE } from "../../constants/pages";
import FilterButton from "../../components/Button/FilterButton";
import Slider from "react-slick";
import "slick-carousel/slick/slick.css";
@@ -37,7 +36,6 @@ const AdsPage = ({ history }) => {
const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false);
const [isSearchFieldVisible, setIsSearchFieldVisible] = useState(false);
const [searchInput, setSearchInput] = useState("");
const [toggleModal, setToggleModal] = useState(false);
const [tmp, setTmp] = useState(null);
const ads = useSelector(selectAds);
const technologies = useSelector(selectTechnologies);
@@ -104,8 +102,8 @@ const AdsPage = ({ history }) => {
setToggleFiltersDrawer((oldState) => !oldState);
};

const handleToggleModal = () => {
setToggleModal((oldState) => !oldState);
const createAd = () => {
history.push(CREATE_AD_PAGE);
};

var settings = {
@@ -229,7 +227,11 @@ const AdsPage = ({ history }) => {
<div style={{ postion: "absolute" }}>
{!matches && (
<>
<Fade in={isSearchFieldVisible} timeout={500}className="ads-page-search-by-title">
<Fade
in={isSearchFieldVisible}
timeout={500}
className="ads-page-search-by-title"
>
{inputNormal}
</Fade>
<Fade in={isSearchFieldVisible} timeout={500}>
@@ -266,7 +268,6 @@ const AdsPage = ({ history }) => {
</>
)}
</div>
<AddAdModal open={toggleModal} handleClose={handleToggleModal} />
<div className="ads" onClick={() => handleChangeVisibility(false)}>
{ads && ads.length > 0 && (
<div className="active-ads">
@@ -360,7 +361,7 @@ const AdsPage = ({ history }) => {
<div className="add-ad add-ad-no-ads">
<IconButton
className="c-btn ads-page-btn c-btn--primary add-ad-btn"
onClick={handleToggleModal}
onClick={() => history.push(CREATE_AD_PAGE)}
>
Dodaj Oglas
</IconButton>
@@ -438,7 +439,7 @@ const AdsPage = ({ history }) => {
<div className="add-ad">
<IconButton
className="c-btn ads-page-btn c-btn--primary add-ad-btn"
onClick={handleToggleModal}
onClick={createAd}
>
+ Oglas
</IconButton>

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

@@ -0,0 +1,106 @@
import React from "react";
import PropTypes from "prop-types";

const CreateAdFirstStep = ({
title,
setTitle,
employmentType,
setEmploymentType,
workHour,
setWorkHour,
expiredAt,
setExpiredAt,
}) => {
const employmentTypeHandler = (type) => {
setEmploymentType(type);
};

const workHourHandler = (type) => {
setWorkHour(type);
};

return (
<div>
<div className="create-ad-form-control">
<label>Naslov</label>
<input
type="text"
onChange={(e) => setTitle(e.target.value)}
value={title}
placeholder="ex. Medior React Developer"
/>
</div>
<div className="create-ad-form-control">
<label>Tip zaposlenja</label>
<div className="create-ad-form-control-buttons">
<button
className={`c-btn ${
employmentType === "Work"
? "c-btn c-btn--primary"
: "c-btn--primary-outlined"
}`}
onClick={employmentTypeHandler.bind(this, "Work")}
>
Posao
</button>
<button
className={`c-btn ${
employmentType === "Intership"
? "c-btn c-btn--primary"
: "c-btn--primary-outlined"
}`}
onClick={employmentTypeHandler.bind(this, "Intership")}
>
Intership
</button>
</div>
</div>
<div className="create-ad-form-control">
<label>Radno vreme</label>
<div className="create-ad-form-control-buttons">
<button
className={`c-btn ${
workHour === "PartTime"
? "c-btn c-btn--primary"
: "c-btn--primary-outlined"
}`}
onClick={workHourHandler.bind(this, "PartTime")}
>
Part-time
</button>
<button
className={`c-btn ${
workHour === "FullTime"
? "c-btn c-btn--primary"
: "c-btn--primary-outlined"
}`}
onClick={workHourHandler.bind(this, "FullTime")}
>
Full-time
</button>
</div>
</div>
<div className="create-ad-form-control">
<label>Datum isteka oglasa</label>
<input
type="date"
onChange={(e) => setExpiredAt(e.target.value)}
value={expiredAt}
/>
</div>
</div>
);
};

CreateAdFirstStep.propTypes = {
title: PropTypes.string,
setTitle: PropTypes.any,
employmentType: PropTypes.string,
setEmploymentType: PropTypes.any,
workHour: PropTypes.string,
setWorkHour: PropTypes.any,
expiredAt: PropTypes.any,
setExpiredAt: PropTypes.any,
};

export default CreateAdFirstStep;

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

@@ -0,0 +1,170 @@
import React, { useState } from "react";
import { useRef } from "react";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import plusIcon from "../../assets/images/plus.png";
import {
resetIsCheckedAddAdValue,
setTechnologiesAddAdReq,
} from "../../store/actions/addAdTechnologies/addAdTechnologiesActions";
import { setCreateAdReq } from "../../store/actions/createAd/createAdActions";
import CreateAdFirstStep from "./CreateAdFirstStep";
import CreateAdSecondStep from "./CreateAdSecondStep";
import CreateAdThirdStep from "./CreateAdThirdStep";
import { useHistory } from "react-router-dom";
import { ADS_PAGE } from "../../constants/pages";

const CreateAdPage = () => {
const [stage, setStage] = useState(1);
const [title, setTitle] = useState("");
const [employmentType, setEmploymentType] = useState("Work");
const [workHour, setWorkHour] = useState("FullTime");
const [expiredAt, setExpiredAt] = useState("");
const [experience, setExperience] = useState(0);
const childRef = useRef();
const history = useHistory();

const technologies = useSelector(
(state) => state.addAdTechnologies.technologies
);
const dispatch = useDispatch();

useEffect(() => {
dispatch(setTechnologiesAddAdReq());
}, []);

const createAd = (keyResponsibilities, requirements, offer) => {
const onSuccessAddAd = () => {
setStage(1);
setTitle("");
setEmploymentType("Work");
setWorkHour("FullTime");
setExpiredAt("");
setExperience(0);
dispatch(resetIsCheckedAddAdValue());
history.push(ADS_PAGE);
};

const technologiesIds = technologies
.filter((x) => x.isChecked === true)
.map((x) => x.technologyId);

dispatch(
setCreateAdReq({
title,
minimumExperience: experience,
createdAt: new Date(),
expiredAt,
keyResponsibilities,
requirements,
offer,
workHour,
employmentType,
technologiesIds,
onSuccessAddAd,
})
);
};

const backClickHandler = () => {
if (stage === 1) {
return;
}

setStage((oldState) => oldState - 1);
};

const forwardClickHandler = () => {
if (stage === 1 && (title === "" || expiredAt === "")) {
return;
}

if (stage === 2) {
const checkedTechnologies = technologies.filter(
(x) => x.isChecked === true
);
if (checkedTechnologies.length === 0) {
return;
}
}

if (stage === 3) {
const {
keyResponsibilities: k,
requirements: r,
offer: o,
} = childRef.current();

if (k === "" || r === "" || o === "") return;

createAd(k, r, o);
}

if (stage < 3) {
setStage((oldState) => oldState + 1);
return;
}
};

return (
<div className="create-ad-page">
<div className="create-ad-page-content">
<div className="create-ad-page-content-header">
<img
src={plusIcon}
alt="plus"
style={{ width: "18px", height: "18px" }}
/>
<h2>Dodavanje</h2>
<h2>
<sub> | Oglas</sub>
</h2>
</div>
<div className="create-ad-steps">
{stage === 1 && (
<CreateAdFirstStep
title={title}
setTitle={setTitle}
employmentType={employmentType}
setEmploymentType={setEmploymentType}
workHour={workHour}
setWorkHour={setWorkHour}
expiredAt={expiredAt}
setExpiredAt={setExpiredAt}
/>
)}
{stage === 2 && (
<CreateAdSecondStep
technologies={technologies}
experience={experience}
setExperience={setExperience}
/>
)}
{stage === 3 && <CreateAdThirdStep childRef={childRef} />}
</div>
<div className="create-ad-buttons" style={{ marginBottom: "2rem" }}>
<button
className="create-ad-buttons-back"
disabled={stage === 1}
onClick={backClickHandler}
>
NAZAD
</button>
<button
className="create-ad-buttons-forward"
onClick={forwardClickHandler}
>
NASTAVI
</button>
</div>
</div>
<div className="create-ad-go-back">
<button onClick={() => history.push(ADS_PAGE)}>
Nazad na sve oglase
</button>
</div>
</div>
);
};

export default CreateAdPage;

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

@@ -0,0 +1,102 @@
import React from "react";
import PropTypes from "prop-types";
import { Checkbox, FormControlLabel } from "@mui/material";
import { useDispatch } from "react-redux";
import { changeIsCheckedAddAdValue } from "../../store/actions/addAdTechnologies/addAdTechnologiesActions";

const CreateAdSecondStep = ({ technologies, experience, setExperience }) => {
const dispatch = useDispatch();

const handleCheckboxes = (technologyId) => {
dispatch(changeIsCheckedAddAdValue(technologyId));
};

return (
<div>
<div className="create-ad-form-control">
<label>Napredne tehnologije</label>
</div>
<div className="add-ad-modal-stage-sub-card">
<div className="add-ad-modal-stage-sub-card-checkboxes-group">
<label>Front-End</label>
<div className="add-ad-modal-stage-sub-card-checkboxes">
{technologies
.filter((x) => x.technologyType === "Frontend")
.map((x) => (
<FormControlLabel
key={x.technologyId}
control={
<Checkbox
value={x.name}
checked={x.isChecked}
onChange={handleCheckboxes.bind(this, x.technologyId)}
/>
}
label={x.name}
/>
))}
</div>
</div>

<div className="add-ad-modal-stage-sub-card-checkboxes-group">
<label>Back-End</label>
<div className="add-ad-modal-stage-sub-card-checkboxes">
{technologies
.filter((x) => x.technologyType === "Backend")
.map((x) => (
<FormControlLabel
key={x.technologyId}
control={
<Checkbox
value={x.name}
checked={x.isChecked}
onChange={handleCheckboxes.bind(this, x.technologyId)}
/>
}
label={x.name}
/>
))}
</div>
</div>

<div className="add-ad-modal-stage-sub-card-checkboxes-group">
<label>Other</label>
<div className="add-ad-modal-stage-sub-card-checkboxes">
{technologies
.filter((x) => x.technologyType === "Other")
.map((x) => (
<FormControlLabel
key={x.technologyId}
control={
<Checkbox
value={x.name}
checked={x.isChecked}
onChange={handleCheckboxes.bind(this, x.technologyId)}
/>
}
label={x.name}
/>
))}
</div>
</div>
</div>
<div className="create-ad-form-control">
<label>Godine iskustva</label>
<input
type="number"
min={0}
onChange={(e) => setExperience(e.target.value)}
value={experience}
/>
</div>
</div>
);
};

CreateAdSecondStep.propTypes = {
technologies: PropTypes.any,
experience: PropTypes.number,
setExperience: PropTypes.any,
};

export default CreateAdSecondStep;

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

@@ -0,0 +1,57 @@
import React from "react";
import PropTypes from "prop-types";
import { Editor } from "@tinymce/tinymce-react";
import { useRef } from "react";
import { useEffect } from "react";

const CreateAdThirdStep = ({ childRef }) => {
const editorKeyResponsibilitiesRef = useRef();
const editorRequirementsRef = useRef();
const editorOfferRef = useRef();

useEffect(() => {
childRef.current = alertUser;
}, []);

function alertUser() {
return {
keyResponsibilities: editorKeyResponsibilitiesRef.current.getContent(),
requirements: editorRequirementsRef.current.getContent(),
offer: editorOfferRef.current.getContent(),
};
}

return (
<div style={{ padding: "50rem 0 0 0" }}>
<div className="create-ad-form-control">
<label>Glavna zaduženja</label>
<Editor
onInit={(evt, editor) =>
(editorKeyResponsibilitiesRef.current = editor)
}
style={{ height: "1rem !important" }}
/>
</div>
<div className="create-ad-form-control">
<label>Uslovi</label>
<Editor
onInit={(evt, editor) => (editorRequirementsRef.current = editor)}
style={{ height: "1rem !important" }}
/>
</div>
<div className="create-ad-form-control">
<label>Šta nudimo</label>
<Editor
onInit={(evt, editor) => (editorOfferRef.current = editor)}
style={{ height: "1rem !important" }}
/>
</div>
</div>
);
};

CreateAdThirdStep.propTypes = {
childRef: PropTypes.any,
};

export default CreateAdThirdStep;

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