瀏覽代碼

add new filters

pull/100/head
Dzenis Hadzifejzovic 3 年之前
父節點
當前提交
39f25bbb7d

二進制
src/assets/images/search.png 查看文件


+ 1
- 0
src/assets/styles/components/_candidate-card.scss 查看文件

@@ -12,6 +12,7 @@
border-radius: 18px;
width: 354px;
height: 238px;
margin-right: 27px;
}

@media only screen and (max-width: 480px) {

+ 26
- 27
src/assets/styles/components/_candidatePage.scss 查看文件

@@ -1,12 +1,14 @@
.main-candidate-container {
display: flex;
flex-direction: column;
margin-top: 36px;
padding-right: 72px;
}

.top-candidate-container {
display: flex;
width: 100%;
justify-content: space-between;
margin-left: 144px;
}

.candidate-header {
@@ -46,6 +48,7 @@
display: flex;
justify-content: space-between;
margin-top: 14px;
margin-left: 144px;
}

.technologies-candidate-container {
@@ -317,8 +320,8 @@
border-radius: 12px;
width: 247px;
height: 238px;
margin-left: 20px;
cursor: pointer;
margin-left: 27px;
}

.applicant-add-date {
@@ -434,7 +437,7 @@
color: #272727;
}

.proba {
.applicant-ads-container-2 {
display: flex;
align-items: center;
margin-top: 18px;
@@ -564,6 +567,17 @@
}

@media only screen and (max-width: 361px) {
.main-candidate-container{
padding-right: 36px;
}
.top-candidate-container {
margin-left: 36px;
}

.content-candidate-container {
margin-left: 36px;
}

.comment-input {
@extend .tagStyle;
min-width: 195px;
@@ -590,7 +604,7 @@
justify-content: initial;
}

.proba {
.applicant-ads-container-2 {
flex-direction: column-reverse;
align-items: flex-start;
}
@@ -600,37 +614,22 @@

.applicant-ads-back-button {
font-size: 14px;
line-height: 16px;
letter-spacing: 0em;
line-height: 18px;
margin-right: 0;
}

.applicant-cv-button {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 18px 72px;
gap: 10px;
background: #226cb0;
border-radius: 9px;
width: 212px;
height: 51px;
font-family: "Source Sans Pro";
font-weight: 600;
padding: 18px 45px;
width: 158px;
font-size: 12px;
line-height: 15px;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #ffffff;
}

.applicant-cv-button {
padding: 18px 45px;
width: 158px;
height: 51px;
.active-ads-ads-arrows {
margin-left: -0.75rem;
}

.active-ads-ads-arrows{
margin-left: -0.75rem;
.applicant-ads-buttons-container{
margin-left: 36px;
}
}

+ 68
- 2
src/assets/styles/components/_candidatesPage.scss 查看文件

@@ -2,11 +2,15 @@
display: flex;
flex-direction: column;
margin-bottom: 36px;
margin-top: 36px;
padding-left: 72px;
}

.top-candidates-container {
display: flex;
justify-content: space-between;
margin-left: 72px;
margin-right: 72px;
}

.candidate-btn {
@@ -15,7 +19,7 @@
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
color: #226cb0;
text-transform: none;
}
@@ -90,11 +94,21 @@

.ads-candidates-slider {
display: flex;
margin-top: 31px;
}

.ads-candidates-slider .slick-track{
margin: 0 !important;
}

.ads-candidates-slider .slick-slider{
width: 100% !important;
}

.ads-candidates-top-container {
display: flex;
align-items: center;
margin-left: 72px;
}

.ads-candidates-title {
@@ -153,6 +167,32 @@
margin-left: -0.75rem !important;
}

.left-move-candidateAd-page {
margin-left: 16px;
}

.left-move-candidateAd-page-2 {
margin-left: 42px;
}

.candidate-search-field{
width: 816px;
border:1px solid #226CB0;
border-radius:10px;
padding: 20px;
background-color: white;
position: absolute;
top: 112px;
left: 800px;
z-index: 1000;
}

.candidate-search-field::placeholder{
font-size: 1rem;
color: #9D9D9D;
font-style: italic;
}

@media only screen and (max-width: 600px) {
.ads-candidates-title {
font-size: 18px;
@@ -165,4 +205,30 @@
.ads-candidates-numberOfApplicants {
font-size: 14px;
}
}
}

@media only screen and (max-width: 361px) {
.ads-candidates-slider {
flex-direction: column-reverse;
}

.main-candidates-container {
padding-left: 0px;
}

.top-candidates-container {
margin-left: 36px;
margin-right: 29px;
}
.candidates-textField{
margin-left:36px
}

.ads-candidates-top-container{
margin-left: 36px;
}

.left-move-candidateAd-page{
margin-left: -12px;
}
}

+ 2
- 1
src/components/IconButton/IconButton.js 查看文件

@@ -4,7 +4,8 @@ import PropType from 'prop-types';
const IconButton = ({ children, onClick, className }) => {
const buttonRef = useRef(null);

function handleClick() {
function handleClick(e) {
e.stopPropagation()
buttonRef.current.blur();
if (typeof onClick === 'function') {
onClick();

+ 30
- 19
src/pages/CandidatesPage/AdsCandidatesPage.js 查看文件

@@ -8,11 +8,15 @@ import { fetchAdsCandidates } from "../../store/actions/candidates/candidatesAct
import { useDispatch, useSelector } from "react-redux";
import CandidateCard from "../../components/Candidates/CandidateCard";
import PropTypes from "prop-types";
import { useTheme } from "@mui/system";
import { useMediaQuery } from "@mui/material";

const AdsCandidatesPage = ({history}) => {
const AdsCandidatesPage = ({ history, search }) => {
const dispatch = useDispatch();
const { adsCandidates } = useSelector((s) => s.candidates);
const [getRef, setRef] = useDynamicRefs();
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down("361"));

useEffect(() => {
dispatch(
@@ -27,14 +31,14 @@ const AdsCandidatesPage = ({history}) => {
technologies: [],
})
);
},[dispatch]);
}, [dispatch]);

var settings = {
dots: false,
infinite: false,
speed: 400,
slidesToShow: 4,
slidesToScroll: 4,
slidesToShow: 3,
slidesToScroll: 3,
initialSlide: 0,
arrows: true,
variableWidth: true,
@@ -42,8 +46,8 @@ const AdsCandidatesPage = ({history}) => {
{
breakpoint: 1024,
settings: {
slidesToShow: 3,
slidesToScroll: 3,
slidesToShow: 2,
slidesToScroll: 2,
infinite: true,
dots: false,
},
@@ -51,8 +55,8 @@ const AdsCandidatesPage = ({history}) => {
{
breakpoint: 900,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
slidesToShow: 1,
slidesToScroll: 1,
initialSlide: 0,
},
},
@@ -61,6 +65,7 @@ const AdsCandidatesPage = ({history}) => {
settings: {
slidesToShow: 1,
slidesToScroll: 1,
initialSlide:0
},
},
],
@@ -99,10 +104,9 @@ const AdsCandidatesPage = ({history}) => {
</div>
<div
className="ads-candidates-slider"
style={{ display: "flex", marginTop: "31px" }}
>
{adCandidates.applicants.length > 3 && (
<div className="active-ads-ads-arrows">
<div className="active-ads-ads-arrows" style={matches ? {marginLeft:36} : {marginLeft:0}}>
<button onClick={() => activeAdsArrowLeftHandler(index)}>
<img src={arrow_left} alt="arrow-left" />
</button>
@@ -114,16 +118,22 @@ const AdsCandidatesPage = ({history}) => {
<Slider
{...settings}
ref={setRef(index.toString())}
style={{ width: "100%" }}
style={{ width: "100%",marginLeft:adCandidates.applicants.length > 3 ? (matches ? 30 : 20) : (matches ? 30 : 66) }}
className='left-move-candidateAd-page'
>
{adCandidates.applicants.map((candidate, index) => (
<CandidateCard
key={index}
candidate={candidate}
history={history}
className={index === 0 ? "" : "left-move-candidateAd"}
/>
))}
{adCandidates.applicants
.filter((candidate) =>
(candidate.firstName + " " + candidate.lastName)
.toLowerCase()
.includes(search.toLowerCase())
)
.map((candidate, index) => (
<CandidateCard
key={index}
candidate={candidate}
history={history}
/>
))}
{adCandidates.applicants.length <= 4 &&
getDummyCandidates(adCandidates.applicants.length)}
</Slider>
@@ -142,6 +152,7 @@ AdsCandidatesPage.propTypes = {
pathname: PropTypes.string,
}),
}),
search: PropTypes.string,
};

export default AdsCandidatesPage;

+ 6
- 7
src/pages/CandidatesPage/CandidateDetailsPage.js 查看文件

@@ -111,7 +111,7 @@ const CandidateDetailsPage = ({ history }) => {
slidesToShow: 5,
slidesToScroll: 5,
initialSlide: 0,
arrows: true,
arrows: false,
variableWidth: true,
responsive: [
{
@@ -205,7 +205,7 @@ const CandidateDetailsPage = ({ history }) => {
user === undefined ? (
<p>Loading...</p>
) : (
<div className="main-candidate-container pl-144 pt-36px">
<div className="main-candidate-container">
<ConfirmDialog
open={showDelete}
title={"Brisanje kandidata"}
@@ -397,9 +397,9 @@ const CandidateDetailsPage = ({ history }) => {
</div>
</div>
<div className="applicant-ads-container">
<p>Sve prijave</p>
<div className="proba">
<div>
<p style={{marginLeft:matches ? 36 : 144}}>Sve prijave</p>
<div className="applicant-ads-container-2">
<div style={{marginLeft:matches ? 36 : (candidate.ads.length < 5 ? 108 : 72)}}>
{(matches ? candidate.ads.length > 1 : candidate.ads.length > 5) && (
<div className="active-ads-ads-arrows">
<button onClick={activeAdsArrowLeftHandler}>
@@ -411,13 +411,12 @@ const CandidateDetailsPage = ({ history }) => {
</div>
)}
</div>
<Slider {...settings} style={{ width: "100%" }} ref={adsSliderRef}>
<Slider {...settings} style={{ width: "100%"}} ref={adsSliderRef}>
{candidate.ads.map((add, index) => (
<CandidateAd
add={add}
key={index}
onclick={() => navigateToDetailsPage(add.id)}
className={(matches === true || candidate.ads.length <= 5) && index === 0 ? "left-move-candidateAd" : "" }
/>
))}
{candidate.ads.length <= 5 && getDummyAds(candidate.ads.length)}

+ 114
- 50
src/pages/CandidatesPage/CandidatesPage.js 查看文件

@@ -2,6 +2,7 @@ import IconButton from "../../components/IconButton/IconButton";
import React, { useEffect, useState } from "react";
import tableImage from "../../assets/images/table.png";
import filterImage from "../../assets/images/filters.png";
import searchImage from "../../assets/images/search.png";
import { useMediaQuery } from "@mui/material";
import { useTheme } from "@mui/system";
import PropTypes from "prop-types";
@@ -12,6 +13,7 @@ import TableViewPage from "./TableViewPage";
import { setTechnologiesReq } from "../../store/actions/technologies/technologiesActions";
import { selectTechnologies } from "../../store/selectors/technologiesSelectors";
import { useDispatch, useSelector } from "react-redux";
import Fade from "@mui/material/Fade";

const CandidatesPage = ({ history }) => {
const dispatch = useDispatch();
@@ -21,12 +23,15 @@ const CandidatesPage = ({ history }) => {
const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false);
const [page, setPage] = React.useState(1);
const technologies = useSelector(selectTechnologies);
const [search, setSearch] = useState("");
const [isSearchFieldVisible, setIsSearchFieldVisible] = useState(false);

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

const changeView = () => {
setSearch("");
setIsTableView(!isTableView);
};

@@ -34,8 +39,32 @@ const CandidatesPage = ({ history }) => {
setToggleFiltersDrawer((oldState) => !oldState);
};

const handleChangeVisibility = () => {
setIsSearchFieldVisible(!isSearchFieldVisible);
};

const stopPropagation = (e) => {
e.stopPropagation();
};

const input = (
<div>
<input
placeholder="Pretrazi..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="candidate-search-field"
onClick={stopPropagation}
style={{ zIndex: 1000 }}
/>
</div>
);

return (
<div className="main-candidates-container pl-144 pt-36px">
<div
className="main-candidates-container"
onClick={() => setIsSearchFieldVisible(false)}
>
<CandidateFilters
open={toggleFiltersDrawer}
handleClose={handleToggleFiltersDrawer}
@@ -54,75 +83,110 @@ const CandidatesPage = ({ history }) => {
Kandidati
</p>
)}
<div className="candidates-options-container">
{!matches ? (
isTableView ? (
<div style={{ postion: "relative" }}>
<Fade in={isSearchFieldVisible} timeout={500}>
{input}
</Fade>
<Fade in={isSearchFieldVisible} timeout={500}>
<div
style={{
position: "absolute",
zIndex: 10000,
marginLeft: 300,
marginTop: 15,
}}
>
<img src={searchImage} />
</div>
</Fade>
<div className="candidates-options-container">
{!matches ? (
isTableView ? (
<IconButton
className="c-btn c-btn--primary-outlined candidate-btn all-white-btn"
onClick={changeView}
>
Tablicni prikaz
<img
src={tableImage}
alt="table"
className="candidates-image"
/>
</IconButton>
) : (
<IconButton
className="c-btn c-btn--primary-outlined candidate-btn"
onClick={changeView}
>
Tablicni prikaz
<img
src={tableImage}
alt="table"
className="candidates-image"
/>
</IconButton>
)
) : (
<IconButton
className="c-btn c-btn--primary-outlined candidate-btn all-white-btn"
className="c-btn--primary-outlined c-btn candidate-btn-mobile"
onClick={changeView}
>
Tablicni prikaz
<img
className="candidate-image-mobile"
src={tableImage}
alt="table"
/>
</IconButton>
)}
{!matches && (
<IconButton
className="c-btn c-btn--primary-outlined candidate-btn"
onClick={handleChangeVisibility}
>
Pretraga
<img
src={searchImage}
alt="filter"
className="candidates-image"
/>
</IconButton>
) : (
)}
{!matches ? (
<IconButton
className="c-btn c-btn--primary-outlined candidate-btn"
onClick={changeView}
onClick={handleToggleFiltersDrawer}
>
Tablicni prikaz
Filteri
<img
src={tableImage}
alt="table"
src={filterImage}
alt="filter"
className="candidates-image"
/>
</IconButton>
)
) : (
<IconButton
className="c-btn--primary-outlined c-btn candidate-btn-mobile"
onClick={changeView}
>
<img
className="candidate-image-mobile"
src={tableImage}
alt="table"
/>
</IconButton>
)}
{!matches ? (
<IconButton
className="c-btn c-btn--primary-outlined candidate-btn"
onClick={handleToggleFiltersDrawer}
>
Filteri
<img
src={filterImage}
alt="filter"
className="candidates-image"
/>
</IconButton>
) : (
<IconButton
className="c-btn c-btn--primary-outlined candidate-btn-mobile"
onClick={handleToggleFiltersDrawer}
>
<img
src={filterImage}
alt="filter"
className="candidate-image-mobile"
/>
</IconButton>
)}
) : (
<IconButton
className="c-btn c-btn--primary-outlined candidate-btn-mobile"
onClick={handleToggleFiltersDrawer}
>
<img
src={filterImage}
alt="filter"
className="candidate-image-mobile"
/>
</IconButton>
)}
</div>
</div>
</div>
{isTableView ? (
<TableViewPage history={history} page={page} setPage={setPage} />
<TableViewPage
history={history}
page={page}
setPage={setPage}
search={search}
/>
) : (
<AdsCandidatesPage history={history}/>
<AdsCandidatesPage history={history} search={search} />
)}
</div>
);

+ 66
- 35
src/pages/CandidatesPage/TableViewPage.js 查看文件

@@ -6,11 +6,15 @@ import PropTypes from "prop-types";
import { CANDIDATES_PAGE } from "../../constants/pages";
import { filterCandidates } from "../../store/actions/candidates/candidatesActions";
import { PAGE_SIZE_CANDIDATES } from "../../constants/keyCodeConstants";
import { useTheme } from "@mui/system";
import { useMediaQuery } from "@mui/material";

const TableViewPage = ({ history, setPage, page }) => {
const TableViewPage = ({ history, setPage, page, search }) => {
const dispatch = useDispatch();
const { candidates } = useSelector((s) => s.candidates);
const { pagination } = useSelector((s) => s.candidates); // pagination is total number of candidates on backend
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down("361"));

const navigate = (applicantId) => {
history.push({
@@ -52,15 +56,31 @@ const TableViewPage = ({ history, setPage, page }) => {
setPage(value);
};

const formatLabel = (string, value) => {
if (!value) {
return string;
}
return (
<span>
{string.split(value).reduce((prev, current, i) => {
if (!i) {
return [current];
}
return prev.concat(
<b className="highlighted" key={value + current}>
{value}
</b>,
current
);
}, [])}
</span>
);
};

return (
<div
className="candidates-table"
>
<div style={{overflowX:'auto'}}>
<table
className="usersTable"
style={{ width: "1017px" }}
>
<div className="candidates-table">
<div style={{ overflowX: "auto",marginLeft:matches ? 36 : 72 }}>
<table className="usersTable" style={{ width: "1017px" }}>
<thead>
<tr className="headingRow">
<th>Ime i prezime</th>
@@ -71,32 +91,42 @@ const TableViewPage = ({ history, setPage, page }) => {
</tr>
</thead>
<tbody>
{candidates.map((candidate, index) => (
<tr
key={index}
className="secondaryRow cadidate-row"
style={{
width: "800px",
height: "40px",
borderRadius: "12px",
cursor: "pointer",
}}
onClick={() => navigate(candidate.applicantId)}
>
<td>
{candidate.firstName} {candidate.lastName}
</td>
<td>{candidate.experience}</td>
<td>{formatDate(candidate.dateOfApplication)}</td>
<td>{candidate.position}</td>
<td>
<a href={candidate.CV} className="cvLink">
{candidate.firstName}
{candidate.lastName}.pdf
</a>
</td>
</tr>
))}
{candidates
.filter((n) =>
(n.firstName + " " + n.lastName)
.toLowerCase()
.includes(search.toLowerCase())
)
.map((candidate, index) => (
<tr
key={index}
className="secondaryRow cadidate-row"
style={{
width: "800px",
height: "40px",
borderRadius: "12px",
cursor: "pointer",
}}
onClick={() => navigate(candidate.applicantId)}
>
<td>
{(candidate.firstName + " " + candidate.lastName).includes(search) ? (
formatLabel(candidate.firstName + " " + candidate.lastName, search)
) : (
<span>{candidate.firstName + " " + candidate.lastName}</span>
)}
</td>
<td>{candidate.experience}</td>
<td>{formatDate(candidate.dateOfApplication)}</td>
<td>{candidate.position}</td>
<td>
<a href={candidate.CV} className="cvLink">
{candidate.firstName}
{candidate.lastName}.pdf
</a>
</td>
</tr>
))}
</tbody>
</table>
</div>
@@ -125,6 +155,7 @@ TableViewPage.propTypes = {
}),
setPage: PropTypes.func,
page: PropTypes.number,
search: PropTypes.string,
};

export default TableViewPage;

Loading…
取消
儲存