| @@ -17,6 +17,8 @@ import { | |||
| CANDIDATES_DETAILS_PAGE, | |||
| SELECTION_PROCESS_PAGE, | |||
| SELECTION_PROCESS_OF_APPLICANT_PAGE, | |||
| PATTERNS_PAGE, | |||
| PATTERN_DETAILS_PAGE, | |||
| SCHEDULE_PAGE, | |||
| STATS_PAGE | |||
| } from "./constants/pages"; | |||
| @@ -34,12 +36,14 @@ import PrivateRoute from "./components/Router/PrivateRoute"; | |||
| import ForgotPasswordConfirmationPage from "./pages/ForgotPasswordPage/ForgotPasswordConfirmationPageMUI"; | |||
| import ResetPasswordPage from "./pages/ForgotPasswordPage/ResetPasswordPageMUI"; | |||
| import UsersPage from "./pages/UsersPage/UsersPage"; | |||
| import CandidatesPage from './pages/CandidatesPage/CandidatesPage' | |||
| import CandidatesPage from "./pages/CandidatesPage/CandidatesPage"; | |||
| import AdDetailsPage from "./pages/AdsPage/AdDetailsPage"; | |||
| import UserDetails from "./pages/UsersPage/UserDetails"; | |||
| import CandidateDetailsPage from "./pages/CandidatesPage/CandidateDetailsPage"; | |||
| import SelectionProcessPage from "./pages/SelectionProcessPage/SelectionProcessPage"; | |||
| import SelectionProcessOfApplicantPage from "./pages/SelectionProcessPage/SelectionProcessOfApplicantPage"; | |||
| import PatternsPage from "./pages/PatternsPage/PatternsPage"; | |||
| import PatternDetailsPage from "./pages/PatternsPage/PatternDetailsPage"; | |||
| import SchedulePage from "./pages/SchedulePage/SchedulePage"; | |||
| import StatsPage from "./pages/StatsPage/StatsPage"; | |||
| @@ -61,9 +65,27 @@ 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={CANDIDATES_DETAILS_PAGE} component={CandidateDetailsPage} /> | |||
| <PrivateRoute exact path={SELECTION_PROCESS_PAGE} component={SelectionProcessPage} /> | |||
| <PrivateRoute exact path={SELECTION_PROCESS_OF_APPLICANT_PAGE} component={SelectionProcessOfApplicantPage} /> | |||
| <PrivateRoute | |||
| exact | |||
| path={CANDIDATES_DETAILS_PAGE} | |||
| component={CandidateDetailsPage} | |||
| /> | |||
| <PrivateRoute | |||
| exact | |||
| path={SELECTION_PROCESS_PAGE} | |||
| component={SelectionProcessPage} | |||
| /> | |||
| <PrivateRoute | |||
| exact | |||
| path={SELECTION_PROCESS_OF_APPLICANT_PAGE} | |||
| component={SelectionProcessOfApplicantPage} | |||
| /> | |||
| <PrivateRoute | |||
| exact | |||
| path={PATTERN_DETAILS_PAGE} | |||
| component={PatternDetailsPage} | |||
| /> | |||
| <PrivateRoute exact path={PATTERNS_PAGE} component={PatternsPage} /> | |||
| <PrivateRoute exact path={SCHEDULE_PAGE} component={SchedulePage} /> | |||
| <PrivateRoute exact path={STATS_PAGE} component={StatsPage} /> | |||
| <Redirect from="*" to={NOT_FOUND_PAGE} /> | |||
| @@ -0,0 +1,627 @@ | |||
| .patterns { | |||
| padding: 0 72px; | |||
| @include media-below($bp-xl) { | |||
| padding: 0 18px; | |||
| } | |||
| } | |||
| .patterns-header { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| margin-top: 16px; | |||
| padding-left: calc(144px - 72px); | |||
| margin-bottom: 18px !important; | |||
| @include media-below($bp-xl) { | |||
| padding-left: 18px; | |||
| } | |||
| } | |||
| .pattern-header-active-button { | |||
| background-color: $mainBlueLight !important; | |||
| } | |||
| .patterns-header button { | |||
| margin-left: 14px; | |||
| } | |||
| .patterns-cards { | |||
| padding: 0 calc(138px - 72px) 0 calc(144px - 72px); | |||
| display: flex; | |||
| flex-wrap: wrap; | |||
| width: 100% !important; | |||
| margin-bottom: 18px !important; | |||
| @include media-below($bp-xl) { | |||
| padding: 0 18px !important; | |||
| flex-direction: column !important; | |||
| } | |||
| } | |||
| .pattern-card-parent { | |||
| width: calc(100% / 3) !important; | |||
| margin-bottom: 36px; | |||
| padding-right: 36px; | |||
| @include media-below($bp-xl) { | |||
| width: 100% !important; | |||
| padding: 0 !important; | |||
| } | |||
| } | |||
| /* PATTERN CARD */ | |||
| .pattern-card-with-icon { | |||
| position: relative; | |||
| } | |||
| .pattern-card { | |||
| position: relative; | |||
| box-sizing: border-box; | |||
| padding: 72px !important; | |||
| display: flex; | |||
| flex-direction: column; | |||
| align-items: center; | |||
| justify-content: center; | |||
| gap: 18px; | |||
| isolation: isolate; | |||
| background: #ffffff; | |||
| border: 1px solid #e4e4e4; | |||
| border-radius: 18px; | |||
| width: 100% !important; | |||
| transition: 0.3s; | |||
| cursor: pointer; | |||
| } | |||
| .pattern-card:hover { | |||
| scale: 1.05; | |||
| border-color: $mainBlue !important; | |||
| background-color: $mainBlueLight !important; | |||
| } | |||
| .pattern-card-edit { | |||
| position: absolute; | |||
| top: 9px !important; | |||
| right: 9px !important; | |||
| border-radius: 50% !important; | |||
| width: 40px !important; | |||
| height: 40px !important; | |||
| z-index: 100; | |||
| } | |||
| .pattern-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; | |||
| z-index: 0; | |||
| } | |||
| .pattern-card-title p { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| font-size: 24px; | |||
| line-height: 32px; | |||
| letter-spacing: 0.02em; | |||
| color: $mainBlue; | |||
| flex: none; | |||
| order: 1; | |||
| flex-grow: 0; | |||
| z-index: 1; | |||
| } | |||
| .pattern-card-selection-proccess { | |||
| box-sizing: border-box; | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: center; | |||
| align-items: center; | |||
| padding: 9px; | |||
| gap: 10px; | |||
| border: 1px solid #e4e4e4; | |||
| border-radius: 9px; | |||
| flex: none; | |||
| order: 2; | |||
| flex-grow: 0; | |||
| z-index: 2; | |||
| } | |||
| .pattern-card-date, | |||
| .pattern-card-title, | |||
| .pattern-card-selection-proccess { | |||
| display: flex !important; | |||
| justify-content: center !important; | |||
| } | |||
| .patterns-button { | |||
| padding-bottom: 18px; | |||
| display: flex; | |||
| justify-content: flex-end; | |||
| } | |||
| /* AD DETAILS */ | |||
| .pattern-details { | |||
| padding: 42px 36px 72px 36px !important; | |||
| @include media-below($bp-xl) { | |||
| margin-top: 9px; | |||
| } | |||
| } | |||
| .pattern-details-header { | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: flex-end; | |||
| @include media-below($bp-xl) { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| font-size: 14px; | |||
| justify-content: flex-start; | |||
| } | |||
| } | |||
| .pattern-details-header p { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| font-size: 16px; | |||
| line-height: 20px; | |||
| text-align: right; | |||
| } | |||
| .pattern-details-header p span { | |||
| color: #9d9d9d; | |||
| } | |||
| .pattern-details-card { | |||
| margin: 41px auto 0 auto !important; | |||
| padding: 0 405px !important; | |||
| @include media-below($bp-xl) { | |||
| margin: 9px auto 0 auto !important; | |||
| padding: 0 !important; | |||
| } | |||
| } | |||
| .pattern-details-card-title { | |||
| display: flex; | |||
| align-items: center; | |||
| margin-bottom: 7px; | |||
| @include media-below($bp-xl) { | |||
| margin-top: 9px; | |||
| } | |||
| } | |||
| .pattern-details-card-title-title h1 { | |||
| font-size: 36px; | |||
| margin-right: 4px; | |||
| @include media-below($bp-xl) { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| font-size: 18px; | |||
| line-height: 32px; | |||
| letter-spacing: 0.02em; | |||
| } | |||
| } | |||
| .pattern-details-card-title-sub { | |||
| font-size: 24px; | |||
| color: $mainBlue; | |||
| font-weight: 600; | |||
| @include media-below($bp-xl) { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| font-size: 18px; | |||
| line-height: 32px; | |||
| letter-spacing: 0.02em; | |||
| } | |||
| } | |||
| .pattern-details-card-sub-card { | |||
| margin-bottom: 18px; | |||
| } | |||
| .pattern-details-card-sub-card-title { | |||
| margin-bottom: 10px; | |||
| } | |||
| .pattern-details-card-screening-title p { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| font-size: 16px; | |||
| line-height: 20px; | |||
| color: #272727; | |||
| } | |||
| .pattern-details-card-sub-card-emails { | |||
| display: flex; | |||
| flex-wrap: wrap; | |||
| } | |||
| .pattern-details-card-sub-card-emails > div { | |||
| margin-right: 9px; | |||
| margin-bottom: 4px; | |||
| } | |||
| .pattern-details-card-sub-card-emails-email { | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: center; | |||
| align-items: center; | |||
| padding: 9px; | |||
| gap: 10px; | |||
| background: #ffffff; | |||
| border: 1px solid #e4e4e4; | |||
| border-radius: 9px; | |||
| flex: none; | |||
| order: 0; | |||
| flex-grow: 0; | |||
| } | |||
| .pattern-details-card-sub-card-add-email { | |||
| display: flex; | |||
| } | |||
| .pattern-details-card-sub-card-add-email input { | |||
| margin-right: 18px; | |||
| flex: 50; | |||
| 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; | |||
| outline: none; | |||
| } | |||
| .pattern-details-card-sub-card-add-email button { | |||
| box-sizing: border-box; | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: center; | |||
| align-items: center; | |||
| padding: 18px; | |||
| gap: 10px; | |||
| background: #ffffff; | |||
| border: 1px solid #226cb0; | |||
| border-radius: 9px; | |||
| flex: 1; | |||
| cursor: pointer; | |||
| transition: 0.3s; | |||
| } | |||
| .pattern-details-card-sub-card-add-email button:hover { | |||
| background-color: $mainBlueLight; | |||
| } | |||
| .pattern-details-card-sub-card-add-email button img { | |||
| width: 12px; | |||
| height: 12px; | |||
| } | |||
| .pattern-details-card-sub-card-message-pattern textarea { | |||
| resize: none; | |||
| width: 100%; | |||
| box-sizing: border-box; | |||
| display: flex; | |||
| flex-direction: row; | |||
| align-items: flex-start; | |||
| padding: 18px; | |||
| gap: 10px; | |||
| background: #f4f4f4; | |||
| border: 1px solid #e4e4e4; | |||
| border-radius: 7px; | |||
| min-height: 256px; | |||
| } | |||
| .pattern-details-card-buttons { | |||
| display: flex; | |||
| justify-content: flex-end; | |||
| align-items: center; | |||
| } | |||
| .pattern-details-card-buttons > * { | |||
| margin-left: 18px !important; | |||
| } | |||
| .custom-modal { | |||
| padding: 36px !important; | |||
| border: none !important; | |||
| border-radius: 18px; | |||
| } | |||
| .add-pattern-modal { | |||
| width: 512px; | |||
| min-height: 618px; | |||
| } | |||
| .add-pattern-modal-header { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| margin-bottom: 18px; | |||
| } | |||
| .add-pattern-modal-header-title { | |||
| display: flex; | |||
| align-items: center; | |||
| } | |||
| .add-pattern-modal-header-title > * { | |||
| margin-right: 4px; | |||
| } | |||
| .add-pattern-modal-header-title-image img { | |||
| width: 18px; | |||
| height: 18px; | |||
| } | |||
| .add-pattern-modal-header-title-title p { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| font-size: 24px; | |||
| line-height: 32px; | |||
| letter-spacing: 0.02em; | |||
| color: #272727; | |||
| } | |||
| .add-pattern-modal-header-title-sub sub { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| line-height: 32px; | |||
| font-size: 18px; | |||
| letter-spacing: 0.02em; | |||
| color: $mainBlue; | |||
| } | |||
| .add-pattern-modal-header-close img { | |||
| width: 9px; | |||
| height: 10.5px; | |||
| cursor: pointer; | |||
| } | |||
| .add-pattern-modal-form-control { | |||
| display: flex; | |||
| flex-direction: column; | |||
| margin-bottom: 9px; | |||
| } | |||
| .add-pattern-modal-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; | |||
| } | |||
| .add-pattern-modal-form-control input, | |||
| .add-pattern-modal-form-control select, | |||
| .add-pattern-modal-form-control textarea { | |||
| box-sizing: border-box; | |||
| display: flex; | |||
| flex-direction: row; | |||
| align-items: center; | |||
| padding: 18px; | |||
| gap: 10px; | |||
| border: 1px solid #e4e4e4; | |||
| border-radius: 7px; | |||
| outline: none; | |||
| } | |||
| .add-pattern-modal-form-control textarea { | |||
| resize: none; | |||
| } | |||
| .add-pattern-modal-form-control input[type="submit"] { | |||
| box-sizing: border-box; | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: center; | |||
| align-items: center; | |||
| padding: 18px 72px; | |||
| gap: 10px; | |||
| background: #226cb0; | |||
| color: white; | |||
| border-radius: 9px; | |||
| cursor: pointer; | |||
| } | |||
| .edit-pattern-modal { | |||
| width: 512px; | |||
| min-height: 618px; | |||
| } | |||
| .edit-pattern-modal-header { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| margin-bottom: 18px; | |||
| } | |||
| .edit-pattern-modal-header-title { | |||
| display: flex; | |||
| align-items: center; | |||
| } | |||
| .edit-pattern-modal-header-title > * { | |||
| margin-right: 4px; | |||
| } | |||
| .edit-pattern-modal-header-title-image img { | |||
| width: 18px; | |||
| height: 18px; | |||
| } | |||
| .edit-pattern-modal-header-title-title p { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| font-size: 24px; | |||
| line-height: 32px; | |||
| letter-spacing: 0.02em; | |||
| color: #272727; | |||
| } | |||
| .edit-pattern-modal-header-title-sub sub { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| line-height: 32px; | |||
| font-size: 18px; | |||
| letter-spacing: 0.02em; | |||
| color: $mainBlue; | |||
| } | |||
| .edit-pattern-modal-header-close img { | |||
| width: 9px; | |||
| height: 10.5px; | |||
| cursor: pointer; | |||
| } | |||
| .edit-pattern-modal-form-control { | |||
| display: flex; | |||
| flex-direction: column; | |||
| margin-bottom: 9px; | |||
| } | |||
| .edit-pattern-modal-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; | |||
| } | |||
| .edit-pattern-modal-form-control input, | |||
| .edit-pattern-modal-form-control select, | |||
| .edit-pattern-modal-form-control textarea { | |||
| box-sizing: border-box; | |||
| display: flex; | |||
| flex-direction: row; | |||
| align-items: center; | |||
| padding: 18px; | |||
| gap: 10px; | |||
| border: 1px solid #e4e4e4; | |||
| border-radius: 7px; | |||
| outline: none; | |||
| } | |||
| .edit-pattern-modal-form-control textarea { | |||
| resize: none; | |||
| } | |||
| .edit-pattern-modal-form-control input[type="submit"] { | |||
| box-sizing: border-box; | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: center; | |||
| align-items: center; | |||
| padding: 18px 72px; | |||
| gap: 10px; | |||
| background: #226cb0; | |||
| color: white; | |||
| border-radius: 9px; | |||
| cursor: pointer; | |||
| } | |||
| /* CUSTOM-FILTER-DRAWER */ | |||
| .custom-drawer { | |||
| display: flex; | |||
| height: 100% !important; | |||
| flex-direction: column; | |||
| justify-content: space-between; | |||
| } | |||
| .custom-filter-drawer-header-container { | |||
| display: flex; | |||
| justify-content: space-between; | |||
| } | |||
| .custom-filter-drawer-header { | |||
| display: flex; | |||
| align-items: center; | |||
| } | |||
| .custom-filter-drawer-header-close { | |||
| cursor: pointer; | |||
| } | |||
| .custom-filter-drawer-header > * { | |||
| margin-right: 0.25rem; | |||
| } | |||
| .custom-filter-drawer-header img { | |||
| width: 18px; | |||
| height: 15.75px; | |||
| } | |||
| .custom-filter-drawer-header sub { | |||
| color: #226cb0; | |||
| } | |||
| .custom-filter-drawer-content { | |||
| margin-top: 18px !important; | |||
| box-sizing: border-box; | |||
| } | |||
| .custom-drawer-sub-card { | |||
| margin-bottom: 18px; | |||
| } | |||
| .custom-drawer-sub-card-label { | |||
| margin-bottom: 10px; | |||
| } | |||
| .custom-drawer-sub-card-label p { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 600; | |||
| font-size: 16px; | |||
| line-height: 20px; | |||
| color: #272727; | |||
| } | |||
| .custom-drawer-sub-card-content-sub-label { | |||
| font-family: "Source Sans Pro"; | |||
| font-style: normal; | |||
| font-weight: 400; | |||
| font-size: 16px; | |||
| line-height: 20px; | |||
| color: #272727; | |||
| margin-bottom: 4.5px; | |||
| } | |||
| .custom-drawer-sub-card-content input[type="date"] { | |||
| box-sizing: border-box; | |||
| display: flex; | |||
| flex-direction: row; | |||
| justify-content: space-between; | |||
| align-items: center; | |||
| padding: 18px; | |||
| gap: 10px; | |||
| border: 1px solid #e4e4e4; | |||
| border-radius: 9px; | |||
| } | |||
| .custom-drawer-submit { | |||
| margin-top: 18px; | |||
| padding-bottom: 18px; | |||
| } | |||
| .custom-drawer-submit > * { | |||
| width: 100%; | |||
| } | |||
| @@ -56,7 +56,7 @@ const NavbarComponent = () => { | |||
| let btnRef = useRef(); | |||
| // get authenticated user | |||
| const {user} = useSelector(s => s.user); | |||
| const { user } = useSelector((s) => s.user); | |||
| const { t } = useTranslation(); | |||
| @@ -216,11 +216,11 @@ const NavbarComponent = () => { | |||
| display: "flex", | |||
| justifyContent: "center", | |||
| alignItems: "center", | |||
| width: matches ? '100%' : 'auto' | |||
| width: matches ? "100%" : "auto", | |||
| }} | |||
| > | |||
| {matches ? ( | |||
| <Box | |||
| <Box | |||
| className="responsive-nav-cont" | |||
| style={{ | |||
| display: "flex", | |||
| @@ -231,14 +231,14 @@ const NavbarComponent = () => { | |||
| <img | |||
| style={{ height: "37px", width: "37px", marginLeft: "0px" }} | |||
| src={HrLogo} | |||
| className='responsive-logo' | |||
| className="responsive-logo" | |||
| /> | |||
| <div | |||
| style={{ | |||
| display: "flex", | |||
| alignItems: "center", | |||
| }} | |||
| className='icons-cont' | |||
| className="icons-cont" | |||
| > | |||
| <img src={searchIcon} /> | |||
| <IconButton | |||
| @@ -296,7 +296,7 @@ const NavbarComponent = () => { | |||
| padding: "0", | |||
| textDecoration: "none", | |||
| }} | |||
| className={pathname === `/${n}` ? 'text-blue' : 'text-black'} | |||
| className={pathname === `/${n}` ? "text-blue" : "text-black"} | |||
| as={Link} | |||
| to={`/${n}`} | |||
| > | |||
| @@ -326,4 +326,4 @@ const NavbarComponent = () => { | |||
| ); | |||
| }; | |||
| export default NavbarComponent; | |||
| export default NavbarComponent; | |||
| @@ -0,0 +1,53 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import editIcon from "../../assets/images/edit.png"; | |||
| import { IconButton } from "@mui/material"; | |||
| const PatternCard = ({ | |||
| createdAt, | |||
| title, | |||
| selectionProcess, | |||
| isShownEdit, | |||
| onShowPatternDetails, | |||
| onOpenEditModal, | |||
| }) => { | |||
| return ( | |||
| <div className="pattern-card-with-icon"> | |||
| {isShownEdit && ( | |||
| <div className="pattern-card-edit"> | |||
| <IconButton | |||
| onClick={onOpenEditModal} | |||
| className={`c-btn--primary-outlined c-btn pattern-card-edit`} | |||
| > | |||
| <img | |||
| style={{ width: "16px !important", height: "16px !important" }} | |||
| src={editIcon} | |||
| /> | |||
| </IconButton> | |||
| </div> | |||
| )} | |||
| <div className="pattern-card" onClick={onShowPatternDetails}> | |||
| <div className="pattern-card-date"> | |||
| <p>{new Date(createdAt).toLocaleDateString()}</p> | |||
| </div> | |||
| <div className="pattern-card-title"> | |||
| <p>{title}</p> | |||
| </div> | |||
| <div className="pattern-card-selection-proccess"> | |||
| <p>{selectionProcess}</p> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| ); | |||
| }; | |||
| PatternCard.propTypes = { | |||
| createdAt: PropTypes.any, | |||
| title: PropTypes.string, | |||
| selectionProcess: PropTypes.string, | |||
| isShownEdit: PropTypes.bool, | |||
| onShowPatternDetails: PropTypes.func, | |||
| onOpenEditModal: PropTypes.func, | |||
| }; | |||
| export default PatternCard; | |||
| @@ -0,0 +1,57 @@ | |||
| import React from "react"; | |||
| import PropType from "prop-types"; | |||
| import Box from "@mui/material/Box"; | |||
| import Drawer from "@mui/material/Drawer"; | |||
| import filterIcon from "../../assets/images/filters.png"; | |||
| import x from "../../assets/images/x.png"; | |||
| const CustomDrawer = ({ title, open, onCloseDrawer, children }) => { | |||
| const list = () => ( | |||
| <Box | |||
| sx={{ | |||
| width: 360, | |||
| height: "100%", | |||
| borderRadius: "18px 0 0 18px", | |||
| padding: "36px", | |||
| }} | |||
| role="presentation" | |||
| onKeyDown={onCloseDrawer} | |||
| > | |||
| <div> | |||
| <div className="custom-filter-drawer-header-container"> | |||
| <div className="custom-filter-drawer-header"> | |||
| <img src={filterIcon} alt="filter_icon" /> | |||
| <h3>Filteri</h3> | |||
| <p> | |||
| <sub>| {title}</sub> | |||
| </p> | |||
| </div> | |||
| <div | |||
| className="custom-filter-drawer-header-close" | |||
| onClick={onCloseDrawer} | |||
| > | |||
| <img src={x} alt="x" /> | |||
| </div> | |||
| </div> | |||
| <div className="custom-filter-drawer-content">{children}</div> | |||
| </div> | |||
| </Box> | |||
| ); | |||
| return ( | |||
| <div> | |||
| <Drawer anchor="right" open={open} onClose={onCloseDrawer}> | |||
| {list()} | |||
| </Drawer> | |||
| </div> | |||
| ); | |||
| }; | |||
| CustomDrawer.propTypes = { | |||
| title: PropType.string, | |||
| open: PropType.bool, | |||
| onCloseDrawer: PropType.func, | |||
| children: PropType.any, | |||
| }; | |||
| export default CustomDrawer; | |||
| @@ -0,0 +1,42 @@ | |||
| import * as React from "react"; | |||
| import PropTypes 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, | |||
| p: 4, | |||
| }; | |||
| const CustomModal = ({ open, onCloseModal, children, classes }) => { | |||
| const handleClose = () => onCloseModal(); | |||
| return ( | |||
| <Modal | |||
| open={open} | |||
| onClose={handleClose} | |||
| aria-labelledby="modal-modal-title" | |||
| aria-describedby="modal-modal-description" | |||
| > | |||
| <Box sx={style} className={`custom-modal ${classes}`}> | |||
| {children} | |||
| </Box> | |||
| </Modal> | |||
| ); | |||
| }; | |||
| CustomModal.propTypes = { | |||
| open: PropTypes.bool, | |||
| onCloseModal: PropTypes.func, | |||
| children: PropTypes.any, | |||
| classes: PropTypes.string, | |||
| }; | |||
| export default CustomModal; | |||
| @@ -13,5 +13,7 @@ export const FORGOT_PASSWORD_CONFIRMATION_PAGE = '/forgot-password-confirmation' | |||
| export const RESET_PASSWORD_PAGE = '/reset-password'; | |||
| export const SELECTION_PROCESS_PAGE = '/selectionFlow'; | |||
| export const SELECTION_PROCESS_OF_APPLICANT_PAGE = '/selectionflow/:id'; | |||
| export const PATTERNS_PAGE = '/patterns'; | |||
| export const PATTERN_DETAILS_PAGE = '/patterns/:id'; | |||
| export const SCHEDULE_PAGE = '/schedule' | |||
| export const STATS_PAGE = '/statistics'; | |||
| @@ -32,6 +32,7 @@ | |||
| @import './assets/styles/layout'; | |||
| @import './assets/styles/overwrite'; | |||
| @import './assets/styles/utility'; | |||
| @import './assets/styles/components/patterns'; | |||
| .flex-center{ | |||
| @@ -9,7 +9,10 @@ import { useTranslation } from "react-i18next"; | |||
| import AddAdModal from "../../components/Ads/AddAdModal"; | |||
| import AdFilters from "../../components/Ads/AdFilters"; | |||
| import { useDispatch } from "react-redux"; | |||
| import { setAdsReq, setFilteredAdsReq } from "../../store/actions/ads/adsAction"; | |||
| import { | |||
| setAdsReq, | |||
| setFilteredAdsReq, | |||
| } from "../../store/actions/ads/adsAction"; | |||
| import { useSelector } from "react-redux"; | |||
| import { selectAds } from "../../store/selectors/adsSelectors"; | |||
| import { AD_DETAILS_PAGE } from "../../constants/pages"; | |||
| @@ -48,30 +51,32 @@ const AdsPage = ({ history }) => { | |||
| useEffect(() => { | |||
| if (search) { | |||
| // history.push("/ads"); | |||
| const searchParams = search.split('?')[1] | |||
| const technologyParams = searchParams.split('&').filter(x => x.includes('technologies')) | |||
| const technologies = [] | |||
| technologyParams.forEach(p => { | |||
| const tech = p.split('=') | |||
| technologies.push(tech[1]) | |||
| const searchParams = search.split("?")[1]; | |||
| const technologyParams = searchParams | |||
| .split("&") | |||
| .filter((x) => x.includes("technologies")); | |||
| const technologies = []; | |||
| technologyParams.forEach((p) => { | |||
| const tech = p.split("="); | |||
| technologies.push(tech[1]); | |||
| }); | |||
| const params = new URLSearchParams(search); | |||
| dispatch( | |||
| setFilteredAdsReq({ | |||
| minExperience: params.get('minExperience'), | |||
| maxExperience: params.get('maxExperience'), | |||
| minExperience: params.get("minExperience"), | |||
| maxExperience: params.get("maxExperience"), | |||
| technologies, | |||
| workHour: params.get('workHour'), | |||
| employmentType: params.get('employmentType'), | |||
| workHour: params.get("workHour"), | |||
| employmentType: params.get("employmentType"), | |||
| }) | |||
| ); | |||
| } else { | |||
| dispatch(setAdsReq()); | |||
| } | |||
| }, []) | |||
| }, []); | |||
| const handleToggleFiltersDrawer = () => { | |||
| setToggleFiltersDrawer((oldState) => !oldState); | |||
| @@ -0,0 +1,130 @@ | |||
| import React, { useEffect, useState } from "react"; | |||
| import { IconButton } from "@mui/material"; | |||
| import { Link } from "react-router-dom"; | |||
| import plusIcon from "../../assets/images/plus.png"; | |||
| import sendMessage from "../../assets/images/send_message.png"; | |||
| import { useParams } from "react-router-dom"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { setPatternReq } from "../../store/actions/pattern/patternActions"; | |||
| import { selectPattern } from "../../store/selectors/patternSelectors"; | |||
| const PatternDetailsPage = () => { | |||
| const [emails, setEmails] = useState([]); | |||
| const [email, setEmail] = useState(""); | |||
| const pattern = useSelector(selectPattern); | |||
| const { id } = useParams(); | |||
| const dispatch = useDispatch(); | |||
| const regex = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/ | |||
| useEffect(() => { | |||
| dispatch(setPatternReq({ id })); | |||
| }, []); | |||
| const addNewEmailHandler = () => { | |||
| setEmails((oldState) => [...oldState, email]); | |||
| setEmail(""); | |||
| }; | |||
| return ( | |||
| <> | |||
| {!pattern && ( | |||
| <div> | |||
| <p>Loading...</p> | |||
| </div> | |||
| )} | |||
| {pattern && ( | |||
| <div className="pattern-details"> | |||
| <div className="pattern-details-header"> | |||
| <p> | |||
| <span>Napravljen:</span>{" "} | |||
| {new Date(pattern.createdAt).toLocaleDateString()} | |||
| </p> | |||
| </div> | |||
| <div className="pattern-details-card"> | |||
| <div className="pattern-details-card-title"> | |||
| <div className="pattern-details-card-title-title"> | |||
| <h1>Šablon</h1> | |||
| </div> | |||
| <div className="pattern-details-card-title-sub"> | |||
| <sub> | Zakazivanje termina</sub> | |||
| </div> | |||
| </div> | |||
| <div className="pattern-details-card-sub-card"> | |||
| <div className="pattern-details-card-sub-card-title"> | |||
| <p>Screening test</p> | |||
| </div> | |||
| <div className="pattern-details-card-sub-card-emails"> | |||
| {emails && | |||
| emails.map((email, index) => ( | |||
| <div | |||
| key={index} | |||
| className="pattern-details-card-sub-card-emails-email" | |||
| > | |||
| {email} | |||
| </div> | |||
| ))} | |||
| </div> | |||
| </div> | |||
| <div className="pattern-details-card-sub-card"> | |||
| <div className="pattern-details-card-sub-card-title"> | |||
| <p>Screening test</p> | |||
| </div> | |||
| <div className="pattern-details-card-sub-card-add-email"> | |||
| <input | |||
| type="text" | |||
| onChange={(e) => setEmail(e.target.value)} | |||
| value={email} | |||
| placeholder="ex. petar.petrovic@mail.com" | |||
| /> | |||
| <button | |||
| onClick={addNewEmailHandler} | |||
| disabled={!regex.test(email)} | |||
| > | |||
| <img src={plusIcon} alt="plus" /> | |||
| </button> | |||
| </div> | |||
| </div> | |||
| <div className="pattern-details-card-sub-card"> | |||
| <div className="pattern-details-card-sub-card-title"> | |||
| <p>Teskt poruke</p> | |||
| </div> | |||
| <div className="pattern-details-card-sub-card-message-pattern"> | |||
| <textarea | |||
| disabled | |||
| // value={`Postovani, | |||
| // Ovom prilikom Vas obavestavamo da je datum Screening testa zakazan za [selected Date] | |||
| // Srdacan pozdrav, | |||
| // Diligent HR Team`} | |||
| value={pattern.message} | |||
| ></textarea> | |||
| </div> | |||
| </div> | |||
| <div className="pattern-details-card-buttons"> | |||
| <Link className="ad-details-buttons-link" to="/patterns"> | |||
| Nazad na sve šablone | |||
| </Link> | |||
| <IconButton className="c-btn c-btn--primary add-ad-btn"> | |||
| <img | |||
| style={{ | |||
| marginRight: "5px", | |||
| width: "12px", | |||
| height: "12px", | |||
| }} | |||
| src={sendMessage} | |||
| /> | |||
| PORUKU | |||
| </IconButton> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| )} | |||
| </> | |||
| ); | |||
| }; | |||
| export default PatternDetailsPage; | |||
| @@ -0,0 +1,371 @@ | |||
| import React, { useState, useEffect } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import FilterButton from "../../components/Button/FilterButton"; | |||
| import { useTheme } from "@mui/system"; | |||
| import { | |||
| Checkbox, | |||
| FormControlLabel, | |||
| FormGroup, | |||
| IconButton, | |||
| useMediaQuery, | |||
| } from "@mui/material"; | |||
| import userPageBtnIcon from "../../assets/images/userPageBtnIcon.png"; | |||
| import PatternCard from "../../components/Patterns/PatternCard"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { setPatternsReq } from "../../store/actions/patterns/patternsActions"; | |||
| import { selectPatterns } from "../../store/selectors/patternsSelectors"; | |||
| import { selectProcesses } from "../../store/selectors/processesSelectors"; | |||
| import { PATTERN_DETAILS_PAGE } from "../../constants/pages"; | |||
| import CustomModal from "../../components/UI/CustomModal"; | |||
| import plusIcon from "../../assets/images/plus.png"; | |||
| import xIcon from "../../assets/images/x.png"; | |||
| import { setProcessesReq } from "../../store/actions/processes/processesAction"; | |||
| import { createPatternReq } from "../../store/actions/createPattern/createPatternActions"; | |||
| import { updatePatternReq } from "../../store/actions/updatePattern/updatePatternActions"; | |||
| import CustomDrawer from "../../components/UI/CustomDrawer"; | |||
| const PatternsPage = ({ history }) => { | |||
| const theme = useTheme(); | |||
| const matches = useMediaQuery(theme.breakpoints.down("sm")); | |||
| const [isShownEdit, setIsShownEdit] = useState(false); | |||
| const [openFilterDrawer, setOpenFilterDrawer] = useState(false); | |||
| const [openAddPatternModal, setOpenAddPatternModal] = useState(false); | |||
| const [openEditPatternModal, setOpenEditPatternModal] = useState(false); | |||
| const [editPattern, setEditPattern] = useState(null); | |||
| const [addPatternTitle, setAddPatternTitle] = useState(""); | |||
| const [addPatternCategory, setAddPatternCategory] = useState(1); | |||
| const [addPatternMessage, setAddPatternMessage] = useState(""); | |||
| const patterns = useSelector(selectPatterns); | |||
| const processes = useSelector(selectProcesses); | |||
| const dispatch = useDispatch(); | |||
| useEffect(() => { | |||
| dispatch(setPatternsReq()); | |||
| dispatch(setProcessesReq()); | |||
| }, []); | |||
| useEffect(() => { | |||
| if (processes.length > 0) { | |||
| setAddPatternCategory(processes[0].id); | |||
| } | |||
| }, [processes]); | |||
| const closeAddPatternModalHandler = () => { | |||
| setOpenAddPatternModal(false); | |||
| }; | |||
| const openEditModalHandler = (pattern) => { | |||
| setEditPattern({ | |||
| id: pattern.id, | |||
| title: pattern.title, | |||
| createdAt: pattern.createdAt, | |||
| selectionLevelId: pattern.selectionLevel.id, | |||
| message: pattern.message, | |||
| }); | |||
| setOpenEditPatternModal(true); | |||
| }; | |||
| const closeEditPatternModalHandler = () => { | |||
| setEditPattern(null); | |||
| setOpenEditPatternModal(false); | |||
| }; | |||
| const closeFilterDrawerHandler = () => { | |||
| setOpenFilterDrawer(false); | |||
| }; | |||
| const submitAddPatternHandler = (e) => { | |||
| e.preventDefault(); | |||
| if (addPatternTitle.length === 0 || addPatternMessage.length === 0) { | |||
| return; | |||
| } | |||
| dispatch( | |||
| createPatternReq({ | |||
| title: addPatternTitle, | |||
| selectionLevelId: addPatternCategory, | |||
| message: addPatternMessage, | |||
| }) | |||
| ); | |||
| setOpenAddPatternModal(false); | |||
| setAddPatternTitle(""); | |||
| setAddPatternCategory(processes[0].id); | |||
| setAddPatternMessage(""); | |||
| }; | |||
| const submitEditPatternHandler = (e) => { | |||
| e.preventDefault(); | |||
| if (editPattern.title.length === 0 || editPattern.message.length === 0) { | |||
| return; | |||
| } | |||
| dispatch(updatePatternReq(editPattern)); | |||
| setOpenEditPatternModal(false); | |||
| }; | |||
| return ( | |||
| <> | |||
| <CustomDrawer | |||
| title="Šabloni" | |||
| open={openFilterDrawer} | |||
| onCloseDrawer={closeFilterDrawerHandler} | |||
| > | |||
| <form> | |||
| <div className="custom-drawer"> | |||
| <div> | |||
| <div className="custom-drawer-sub-card"> | |||
| <div className="custom-drawer-sub-card-label"> | |||
| <p>Kategorija</p> | |||
| </div> | |||
| <div className="custom-drawer-sub-card-content"> | |||
| <FormGroup> | |||
| {processes.map((process) => ( | |||
| <FormControlLabel | |||
| key={process.id} | |||
| control={<Checkbox value={process.name} />} | |||
| label={process.name} | |||
| /> | |||
| ))} | |||
| </FormGroup> | |||
| </div> | |||
| </div> | |||
| <div className="custom-drawer-sub-card"> | |||
| <div className="custom-drawer-sub-card-label"> | |||
| <p>Datum kreiranja</p> | |||
| </div> | |||
| <div className="custom-drawer-sub-card-content"> | |||
| <FormGroup style={{ marginBottom: "9px" }}> | |||
| <label className="custom-drawer-sub-card-content-sub-label"> | |||
| Od | |||
| </label> | |||
| <input type="date" /> | |||
| </FormGroup> | |||
| <FormGroup> | |||
| <label className="custom-drawer-sub-card-content-sub-label"> | |||
| Do | |||
| </label> | |||
| <input type="date" /> | |||
| </FormGroup> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div className="custom-drawer-submit"> | |||
| <button className="c-btn c-btn--primary">Pretrazi</button> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </CustomDrawer> | |||
| <CustomModal | |||
| open={openAddPatternModal} | |||
| onCloseModal={closeAddPatternModalHandler} | |||
| classes="add-pattern-modal" | |||
| > | |||
| <div className="add-pattern-modal-header"> | |||
| <div className="add-pattern-modal-header-title"> | |||
| <div className="add-pattern-modal-header-title-image"> | |||
| <img src={plusIcon} alt="plus" /> | |||
| </div> | |||
| <div className="add-pattern-modal-header-title-title"> | |||
| <p>Dodavanje</p> | |||
| </div> | |||
| <div className="add-pattern-modal-header-title-sub"> | |||
| <sub> | Šablon</sub> | |||
| </div> | |||
| </div> | |||
| <div | |||
| className="add-pattern-modal-header-close" | |||
| onClick={closeAddPatternModalHandler} | |||
| > | |||
| <img src={xIcon} alt="close" /> | |||
| </div> | |||
| </div> | |||
| <form onSubmit={submitAddPatternHandler}> | |||
| <div className="add-pattern-modal-form-control"> | |||
| <label>Naslov</label> | |||
| <input | |||
| type="text" | |||
| placeholder="ex. Datum HR intervjua" | |||
| onChange={(e) => setAddPatternTitle(e.target.value)} | |||
| value={addPatternTitle} | |||
| /> | |||
| </div> | |||
| <div className="add-pattern-modal-form-control"> | |||
| <label>Kategorija</label> | |||
| <select | |||
| name="add-pattern-category" | |||
| value={addPatternCategory} | |||
| onChange={(e) => setAddPatternCategory(e.target.value)} | |||
| > | |||
| {processes.map((process) => ( | |||
| <option key={process.id} value={process.id}> | |||
| {process.name} | |||
| </option> | |||
| ))} | |||
| </select> | |||
| </div> | |||
| <div className="add-pattern-modal-form-control"> | |||
| <label>Tekst poruke</label> | |||
| <textarea | |||
| rows="11" | |||
| value={addPatternMessage} | |||
| onChange={(e) => setAddPatternMessage(e.target.value)} | |||
| ></textarea> | |||
| </div> | |||
| <div className="add-pattern-modal-form-control"> | |||
| <input type="submit" value="DODAJ ŠABLON" /> | |||
| </div> | |||
| </form> | |||
| </CustomModal> | |||
| <CustomModal | |||
| open={openEditPatternModal} | |||
| onCloseModal={closeEditPatternModalHandler} | |||
| classes="edit-pattern-modal" | |||
| > | |||
| <div className="edit-pattern-modal-header"> | |||
| <div className="edit-pattern-modal-header-title"> | |||
| <div className="edit-pattern-modal-header-title-image"> | |||
| <img src={userPageBtnIcon} alt="plus" /> | |||
| </div> | |||
| <div className="edit-pattern-modal-header-title-title"> | |||
| <p>Uređivanje</p> | |||
| </div> | |||
| <div className="edit-pattern-modal-header-title-sub"> | |||
| <sub> | Šablon</sub> | |||
| </div> | |||
| </div> | |||
| <div | |||
| className="edit-pattern-modal-header-close" | |||
| onClick={closeEditPatternModalHandler} | |||
| > | |||
| <img src={xIcon} alt="close" /> | |||
| </div> | |||
| </div> | |||
| <form onSubmit={submitEditPatternHandler}> | |||
| <div className="edit-pattern-modal-form-control"> | |||
| <label>Naslov</label> | |||
| <input | |||
| type="text" | |||
| placeholder="ex. Datum HR intervjua" | |||
| onChange={(e) => | |||
| setEditPattern((oldState) => ({ | |||
| ...oldState, | |||
| title: e.target.value, | |||
| })) | |||
| } | |||
| value={editPattern ? editPattern.title : ""} | |||
| /> | |||
| </div> | |||
| <div className="edit-pattern-modal-form-control"> | |||
| <label>Kategorija</label> | |||
| <select | |||
| name="edit-pattern-category" | |||
| value={editPattern ? editPattern.selectionLevelId : 1} | |||
| onChange={(e) => | |||
| setEditPattern((oldState) => ({ | |||
| ...oldState, | |||
| selectionLevelId: e.target.value, | |||
| })) | |||
| } | |||
| > | |||
| {processes.map((process) => ( | |||
| <option key={process.id} value={process.id}> | |||
| {process.name} | |||
| </option> | |||
| ))} | |||
| </select> | |||
| </div> | |||
| <div className="edit-pattern-modal-form-control"> | |||
| <label>Tekst poruke</label> | |||
| <textarea | |||
| rows="11" | |||
| onChange={(e) => | |||
| setEditPattern((oldState) => ({ | |||
| ...oldState, | |||
| message: e.target.value, | |||
| })) | |||
| } | |||
| value={editPattern ? editPattern.message : ""} | |||
| ></textarea> | |||
| </div> | |||
| <div className="edit-pattern-modal-form-control"> | |||
| <input type="submit" value="UREDI ŠABLON" /> | |||
| </div> | |||
| </form> | |||
| </CustomModal> | |||
| <div className="patterns"> | |||
| <div className="patterns-header"> | |||
| <div> | |||
| <h1>Napravljeni Šabloni</h1> | |||
| </div> | |||
| <div> | |||
| <IconButton | |||
| onClick={() => setIsShownEdit((oldState) => !oldState)} | |||
| className={`c-btn--primary-outlined editEnableBtn c-btn userPageBtn ${ | |||
| isShownEdit && "pattern-header-active-button" | |||
| }`} | |||
| > | |||
| {!matches && "Režim uređivanja"} | |||
| <img | |||
| style={{ | |||
| position: "relative", | |||
| top: -0.25, | |||
| paddingLeft: matches ? "0px" : "10px", | |||
| }} | |||
| src={userPageBtnIcon} | |||
| /> | |||
| </IconButton> | |||
| <FilterButton onShowFilters={() => setOpenFilterDrawer(true)} /> | |||
| </div> | |||
| </div> | |||
| <div className="patterns-cards"> | |||
| {!patterns && ( | |||
| <div> | |||
| <p>Loading...</p> | |||
| </div> | |||
| )} | |||
| {patterns && patterns.length === 0 && ( | |||
| <div> | |||
| <p>Trenutno nema dodatih sablona</p> | |||
| </div> | |||
| )} | |||
| {patterns && | |||
| patterns.length > 0 && | |||
| patterns.map((pattern) => ( | |||
| <div className="pattern-card-parent" key={pattern.id}> | |||
| <PatternCard | |||
| createdAt={pattern.createdAt} | |||
| title={pattern.title} | |||
| selectionProcess={pattern.selectionLevel.name} | |||
| onOpenEditModal={openEditModalHandler.bind(this, pattern)} | |||
| isShownEdit={isShownEdit} | |||
| onShowPatternDetails={() => | |||
| history.push( | |||
| PATTERN_DETAILS_PAGE.replace(":id", pattern.id) | |||
| ) | |||
| } | |||
| /> | |||
| </div> | |||
| ))} | |||
| </div> | |||
| <div className="patterns-button"> | |||
| <IconButton | |||
| className="c-btn c-btn--primary add-ad-btn" | |||
| onClick={() => setOpenAddPatternModal(true)} | |||
| > | |||
| Dodaj Šablon | |||
| </IconButton> | |||
| </div> | |||
| </div> | |||
| </> | |||
| ); | |||
| }; | |||
| PatternsPage.propTypes = { | |||
| history: PropTypes.any, | |||
| }; | |||
| export default PatternsPage; | |||
| @@ -29,8 +29,8 @@ export default { | |||
| technologies: { | |||
| allTechnologies: base + "/technologies", | |||
| }, | |||
| comments:{ | |||
| addComment:base + '/comments' | |||
| comments: { | |||
| addComment: base + "/comments", | |||
| }, | |||
| processes: { | |||
| allLevels: base + "/selectionlevels", | |||
| @@ -39,6 +39,12 @@ export default { | |||
| getApplicantProcesses: base + "/applicants/processes", | |||
| // allProcesses: base + "/selectionprocesses", | |||
| }, | |||
| patterns: { | |||
| allPatterns: base + "/patterns", | |||
| patternById: base + "/patterns/:id", | |||
| createPattern: base + "/patterns", | |||
| updatePattern: base + "/patterns/:id", | |||
| }, | |||
| stats:{ | |||
| stats: base + "/stats" | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| import { getRequest, postRequest, putRequest } from "."; | |||
| import apiEndpoints from "./apiEndpoints"; | |||
| export const getAllPatterns = () => | |||
| getRequest(apiEndpoints.patterns.allPatterns); | |||
| export const getPatternById = (id) => | |||
| getRequest(apiEndpoints.patterns.patternById.replace(":id", id)); | |||
| export const createPatternRequest = (payload) => | |||
| postRequest(apiEndpoints.patterns.createPattern, payload); | |||
| export const updatePatternRequest = (payload) => | |||
| putRequest( | |||
| apiEndpoints.patterns.updatePattern.replace(":id", payload.id), | |||
| payload | |||
| ); | |||
| @@ -0,0 +1,11 @@ | |||
| import { | |||
| createFetchType, | |||
| createSuccessType, | |||
| createErrorType, | |||
| } from "../actionHelpers"; | |||
| const CREATE_PATTERN_SCOPE = "CREATE_PATTERN"; | |||
| export const CREATE_PATTERN_REQ = createFetchType(CREATE_PATTERN_SCOPE); | |||
| export const CREATE_PATTERN_ERR = createErrorType(CREATE_PATTERN_SCOPE); | |||
| export const CREATE_PATTERN_SUCCESS = createSuccessType(CREATE_PATTERN_SCOPE); | |||
| @@ -0,0 +1,20 @@ | |||
| import { | |||
| CREATE_PATTERN_REQ, | |||
| CREATE_PATTERN_ERR, | |||
| CREATE_PATTERN_SUCCESS, | |||
| } from "./createPatternActionConstants"; | |||
| export const createPatternReq = (payload) => ({ | |||
| type: CREATE_PATTERN_REQ, | |||
| payload, | |||
| }); | |||
| export const createPatternError = (payload) => ({ | |||
| type: CREATE_PATTERN_ERR, | |||
| payload, | |||
| }); | |||
| export const createPattern = (payload) => ({ | |||
| type: CREATE_PATTERN_SUCCESS, | |||
| payload, | |||
| }); | |||
| @@ -0,0 +1,11 @@ | |||
| import { | |||
| createFetchType, | |||
| createSuccessType, | |||
| createErrorType, | |||
| } from "../actionHelpers"; | |||
| const FETCH_PATTERN_SCOPE = "FETCH_PATTERN"; | |||
| export const FETCH_PATTERN_REQ = createFetchType(FETCH_PATTERN_SCOPE); | |||
| export const FETCH_PATTERN_ERR = createErrorType(FETCH_PATTERN_SCOPE); | |||
| export const FETCH_PATTERN_SUCCESS = createSuccessType(FETCH_PATTERN_SCOPE); | |||
| @@ -0,0 +1,20 @@ | |||
| import { | |||
| FETCH_PATTERN_REQ, | |||
| FETCH_PATTERN_ERR, | |||
| FETCH_PATTERN_SUCCESS, | |||
| } from "./patternActionConstants"; | |||
| export const setPatternReq = (payload) => ({ | |||
| type: FETCH_PATTERN_REQ, | |||
| payload, | |||
| }); | |||
| export const setPatternError = (payload) => ({ | |||
| type: FETCH_PATTERN_ERR, | |||
| payload, | |||
| }); | |||
| export const setPattern = (payload) => ({ | |||
| type: FETCH_PATTERN_SUCCESS, | |||
| payload, | |||
| }); | |||
| @@ -0,0 +1,11 @@ | |||
| import { | |||
| createFetchType, | |||
| createSuccessType, | |||
| createErrorType, | |||
| } from "../actionHelpers"; | |||
| const FETCH_PATTERNS_SCOPE = "FETCH_PATTERNS"; | |||
| export const FETCH_PATTERNS_REQ = createFetchType(FETCH_PATTERNS_SCOPE); | |||
| export const FETCH_PATTERNS_ERR = createErrorType(FETCH_PATTERNS_SCOPE); | |||
| export const FETCH_PATTERNS_SUCCESS = createSuccessType(FETCH_PATTERNS_SCOPE); | |||
| @@ -0,0 +1,19 @@ | |||
| import { | |||
| FETCH_PATTERNS_REQ, | |||
| FETCH_PATTERNS_ERR, | |||
| FETCH_PATTERNS_SUCCESS, | |||
| } from "./patternsActionConstants"; | |||
| export const setPatternsReq = () => ({ | |||
| type: FETCH_PATTERNS_REQ, | |||
| }); | |||
| export const setPatternsError = (payload) => ({ | |||
| type: FETCH_PATTERNS_ERR, | |||
| payload, | |||
| }); | |||
| export const setPatterns = (payload) => ({ | |||
| type: FETCH_PATTERNS_SUCCESS, | |||
| payload, | |||
| }); | |||
| @@ -0,0 +1,11 @@ | |||
| import { | |||
| createFetchType, | |||
| createSuccessType, | |||
| createErrorType, | |||
| } from "../actionHelpers"; | |||
| const UPDATE_PATTERN_SCOPE = "UPDATE_PATTERN"; | |||
| export const UPDATE_PATTERN_REQ = createFetchType(UPDATE_PATTERN_SCOPE); | |||
| export const UPDATE_PATTERN_ERR = createErrorType(UPDATE_PATTERN_SCOPE); | |||
| export const UPDATE_PATTERN_SUCCESS = createSuccessType(UPDATE_PATTERN_SCOPE); | |||
| @@ -0,0 +1,20 @@ | |||
| import { | |||
| UPDATE_PATTERN_REQ, | |||
| UPDATE_PATTERN_ERR, | |||
| UPDATE_PATTERN_SUCCESS, | |||
| } from "./updatePatternActionConstants"; | |||
| export const updatePatternReq = (payload) => ({ | |||
| type: UPDATE_PATTERN_REQ, | |||
| payload, | |||
| }); | |||
| export const updatePatternError = (payload) => ({ | |||
| type: UPDATE_PATTERN_ERR, | |||
| payload, | |||
| }); | |||
| export const updatePattern = (payload) => ({ | |||
| type: UPDATE_PATTERN_SUCCESS, | |||
| payload, | |||
| }); | |||
| @@ -18,6 +18,10 @@ import applicantWithProcessesReducer from "./processes/applicantWithProcessesRed | |||
| import userDetailsReducer from "./user/userDetailsReducer"; | |||
| import inviteUserReducer from "./user/inviteUserReducer"; | |||
| import statusReducer from "./processes/statusReducer"; | |||
| import patternsReducer from "./pattern/patternsReducer"; | |||
| import patternReducer from "./pattern/patternReducer"; | |||
| import createPatternReducer from "./pattern/createPatternReducer"; | |||
| import updatePatternReducer from "./pattern/updatePatternReducer"; | |||
| import statsReducer from "./stats/statsReducer"; | |||
| export default combineReducers({ | |||
| @@ -40,5 +44,9 @@ export default combineReducers({ | |||
| userDetails: userDetailsReducer, | |||
| invite: inviteUserReducer, | |||
| statuses: statusReducer, | |||
| patterns: patternsReducer, | |||
| pattern: patternReducer, | |||
| createPattern: createPatternReducer, | |||
| updatePattern: updatePatternReducer, | |||
| stats: statsReducer, | |||
| }); | |||
| @@ -0,0 +1,32 @@ | |||
| import { | |||
| CREATE_PATTERN_ERR, | |||
| CREATE_PATTERN_SUCCESS, | |||
| } from "../../actions/createPattern/createPatternActionConstants"; | |||
| import createReducer from "../../utils/createReducer"; | |||
| const initialState = { | |||
| pattern: null, | |||
| errorMessage: "", | |||
| }; | |||
| export default createReducer( | |||
| { | |||
| [CREATE_PATTERN_SUCCESS]: setCreatePattern, | |||
| [CREATE_PATTERN_ERR]: setCreatePatternErrorMessage, | |||
| }, | |||
| initialState | |||
| ); | |||
| function setCreatePattern(state, action) { | |||
| return { | |||
| ...state, | |||
| pattern: action.payload, | |||
| }; | |||
| } | |||
| function setCreatePatternErrorMessage(state, action) { | |||
| return { | |||
| ...state, | |||
| errorMessage: action.payload, | |||
| }; | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| import createReducer from "../../utils/createReducer"; | |||
| import { | |||
| FETCH_PATTERN_SUCCESS, | |||
| FETCH_PATTERN_ERR, | |||
| } from "../../actions/pattern/patternActionConstants"; | |||
| const initialState = { | |||
| pattern: null, | |||
| errorMessage: "", | |||
| }; | |||
| export default createReducer( | |||
| { | |||
| [FETCH_PATTERN_SUCCESS]: setStatePattern, | |||
| [FETCH_PATTERN_ERR]: setStateErrorMessage, | |||
| }, | |||
| initialState | |||
| ); | |||
| function setStatePattern(state, action) { | |||
| return { ...state, pattern: action.payload }; | |||
| } | |||
| function setStateErrorMessage(state, action) { | |||
| return { ...state, errorMessage: action.payload }; | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| import { | |||
| FETCH_PATTERNS_ERR, | |||
| FETCH_PATTERNS_SUCCESS, | |||
| } from "../../actions/patterns/patternsActionConstants"; | |||
| import createReducer from "../../utils/createReducer"; | |||
| const initialState = { | |||
| patterns: [], | |||
| errorMessage: "", | |||
| }; | |||
| export default createReducer( | |||
| { | |||
| [FETCH_PATTERNS_SUCCESS]: setStatePatterns, | |||
| [FETCH_PATTERNS_ERR]: setPatternsErrorMessage, | |||
| }, | |||
| initialState | |||
| ); | |||
| function setStatePatterns(state, action) { | |||
| return { | |||
| ...state, | |||
| patterns: action.payload, | |||
| }; | |||
| } | |||
| function setPatternsErrorMessage(state, action) { | |||
| return { | |||
| ...state, | |||
| errorMessage: action.payload, | |||
| }; | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| import { | |||
| UPDATE_PATTERN_ERR, | |||
| UPDATE_PATTERN_SUCCESS, | |||
| } from "../../actions/updatePattern/updatePatternActionConstants"; | |||
| import createReducer from "../../utils/createReducer"; | |||
| const initialState = { | |||
| pattern: null, | |||
| errorMessage: "", | |||
| }; | |||
| export default createReducer( | |||
| { | |||
| [UPDATE_PATTERN_SUCCESS]: setUpdatePattern, | |||
| [UPDATE_PATTERN_ERR]: setUpdatePatternErrorMessage, | |||
| }, | |||
| initialState | |||
| ); | |||
| function setUpdatePattern(state, action) { | |||
| return { | |||
| ...state, | |||
| pattern: action.payload, | |||
| }; | |||
| } | |||
| function setUpdatePatternErrorMessage(state, action) { | |||
| return { | |||
| ...state, | |||
| errorMessage: action.payload, | |||
| }; | |||
| } | |||
| @@ -68,6 +68,10 @@ export function* createAd({ payload }) { | |||
| const result = yield call(createNewAd, payload); | |||
| const ad = result.data; | |||
| yield put(setCreateAd(ad)); | |||
| const resultAds = yield call(getAllAds); | |||
| yield put(setAds(resultAds.data)); | |||
| const resultArchiveAds = yield call(getAllArchiveAds); | |||
| yield put(setArchiveAds(resultArchiveAds.data)); | |||
| } catch (error) { | |||
| yield put(setCreateAdError(error)); | |||
| } | |||
| @@ -1,10 +1,11 @@ | |||
| import { all } from "redux-saga/effects"; | |||
| import adsSaga from "./adsSaga"; | |||
| import candidatesSaga from './candidatesSaga'; | |||
| import candidatesSaga from "./candidatesSaga"; | |||
| import loginSaga from "./loginSaga"; | |||
| import technologiesSaga from "./technologiesSaga"; | |||
| import usersSaga from "./usersSaga"; | |||
| import processesSaga from "./processSaga"; | |||
| import patternsSage from "./patternsSaga"; | |||
| import statsSaga from "./statsSaga"; | |||
| export default function* rootSaga() { | |||
| @@ -15,6 +16,7 @@ export default function* rootSaga() { | |||
| technologiesSaga(), | |||
| candidatesSaga(), | |||
| processesSaga(), | |||
| patternsSage(), | |||
| statsSaga() | |||
| ]); | |||
| } | |||
| @@ -0,0 +1,73 @@ | |||
| import { all, call, put, takeLatest } from "redux-saga/effects"; | |||
| import { | |||
| createPatternRequest, | |||
| getAllPatterns, | |||
| getPatternById, | |||
| updatePatternRequest, | |||
| } from "../../request/patternsRequest"; | |||
| import { | |||
| setPatterns, | |||
| setPatternsError, | |||
| } from "../actions/patterns/patternsActions"; | |||
| import { setPattern, setPatternError } from "../actions/pattern/patternActions"; | |||
| import { | |||
| createPattern, | |||
| createPatternError, | |||
| } from "../actions/createPattern/createPatternActions"; | |||
| import { FETCH_PATTERNS_REQ } from "../actions/patterns/patternsActionConstants"; | |||
| import { FETCH_PATTERN_REQ } from "../actions/pattern/patternActionConstants"; | |||
| import { CREATE_PATTERN_REQ } from "../actions/createPattern/createPatternActionConstants"; | |||
| import { UPDATE_PATTERN_REQ } from "../actions/updatePattern/updatePatternActionConstants"; | |||
| import { | |||
| updatePattern, | |||
| updatePatternError, | |||
| } from "../actions/updatePattern/updatePatternActions"; | |||
| export function* getPatterns() { | |||
| try { | |||
| const result = yield call(getAllPatterns); | |||
| yield put(setPatterns(result.data)); | |||
| } catch (error) { | |||
| yield put(setPatternsError(error)); | |||
| } | |||
| } | |||
| export function* getPattern({ payload }) { | |||
| try { | |||
| const result = yield call(getPatternById, payload.id); | |||
| yield put(setPattern(result.data)); | |||
| } catch (error) { | |||
| yield put(setPatternError(error)); | |||
| } | |||
| } | |||
| export function* createPatternSaga({ payload }) { | |||
| try { | |||
| const result = yield call(createPatternRequest, payload); | |||
| yield put(createPattern(result.data)); | |||
| const resultPatterns = yield call(getAllPatterns); | |||
| yield put(setPatterns(resultPatterns.data)); | |||
| } catch (error) { | |||
| yield put(createPatternError(error)); | |||
| } | |||
| } | |||
| export function* updatePatternSaga({ payload }) { | |||
| try { | |||
| const result = yield call(updatePatternRequest, payload); | |||
| yield put(updatePattern(result.data)); | |||
| const resultPatterns = yield call(getAllPatterns); | |||
| yield put(setPatterns(resultPatterns.data)); | |||
| } catch (error) { | |||
| yield put(updatePatternError(error)); | |||
| } | |||
| } | |||
| export default function* adsSaga() { | |||
| yield all([ | |||
| takeLatest(FETCH_PATTERNS_REQ, getPatterns), | |||
| takeLatest(FETCH_PATTERN_REQ, getPattern), | |||
| takeLatest(CREATE_PATTERN_REQ, createPatternSaga), | |||
| takeLatest(UPDATE_PATTERN_REQ, updatePatternSaga), | |||
| ]); | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| import { createSelector } from "@reduxjs/toolkit"; | |||
| export const patternSelector = (state) => state.pattern; | |||
| export const selectPattern = createSelector( | |||
| patternSelector, | |||
| (state) => state.pattern | |||
| ); | |||
| export const selectPatternError = createSelector( | |||
| patternSelector, | |||
| (state) => state.errorMessage | |||
| ); | |||
| @@ -0,0 +1,13 @@ | |||
| import { createSelector } from "@reduxjs/toolkit"; | |||
| export const patternsSelector = (state) => state.patterns; | |||
| export const selectPatterns = createSelector( | |||
| patternsSelector, | |||
| (state) => state.patterns | |||
| ); | |||
| export const selectPatternsError = createSelector( | |||
| patternsSelector, | |||
| (state) => state.errorMessage | |||
| ); | |||