├── .gitignore ├── Diagram.png ├── README.md ├── package-lock.json ├── package.json ├── public ├── index.html └── manifest.json └── src ├── App.css ├── App.js ├── App.test.js ├── index.css ├── index.js ├── layouts ├── Dashboard.jsx ├── Footer.jsx ├── Navi.jsx ├── SignedIn.jsx └── SignedOut.jsx ├── logo.svg ├── pages ├── ChoosingSignInMethod.jsx ├── ChoosingSignUpMethod.jsx ├── Employers │ ├── EmployerDetailPage.jsx │ ├── EmployerList.jsx │ ├── EmployerSignUpPage.jsx │ └── JobAddPage.jsx ├── JobPositionList.jsx ├── JobSeekers │ ├── JobSeekerDetail.jsx │ ├── JobSeekerList.jsx │ └── JobSeekerSignUpPage.jsx ├── Jobs │ ├── JobDetailPage.jsx │ └── JobList.jsx ├── MainPage.jsx ├── SignInPage.jsx └── SystemUsers │ ├── JobConfirmationPanel.jsx │ └── SystemUserSignUpPage.jsx ├── reportWebVitals.js ├── services ├── ActivationPanelService.js ├── CityService.js ├── EmployerService.js ├── JobPositionService.js ├── JobSeekerService.js ├── JobService.js ├── WorkPlaceService.js └── WorkTimeService.js ├── setupTests.js └── store ├── configureStore.js └── rootReducer.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | node_modules 25 | -------------------------------------------------------------------------------- /Diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrPand-21/HRMS_Frontend/1732033d87311d30ef0ee3f81a3390a53d2688e7/Diagram.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 |
6 |
7 | 8 | HRMS 9 | 10 |

HRMS_Frontend

11 | 12 |

13 | About • 14 | How To Use • 15 | Installation • 16 | Credits • 17 | Learn More • 18 | Report Bug • 19 | Request Feature 20 |

21 |

HRMS Frontend project which was bootstrapped with Create React App and Semantic UI. 22 |

23 |
24 | 25 | # About the Project 26 | 27 | [![JavaScript](https://img.shields.io/badge/JavaScript-F7DF1E?style=for-the-badge&logo=javascript&logoColor=black)]() 28 | [![Node.js](https://img.shields.io/badge/Node.js-43853D?style=for-the-badge&logo=node-dot-js&logoColor=white)](https://nodejs.org/) 29 | [![React](https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB)](https://tr.reactjs.org/) 30 | [![Bootstrap](https://img.shields.io/badge/Bootstrap-563D7C?style=for-the-badge&logo=bootstrap&logoColor=white)](https://getbootstrap.com/) 31 | [![Scss](https://img.shields.io/badge/Scss-CC6699?style=for-the-badge&logo=sass&logoColor=white)](https://sass-lang.com/) 32 | [![Create React App](https://img.shields.io/badge/Create-ReactApp-AA6699?style=for-the-badge&logo=create-react-app&logoColor=white)](https://github.com/facebook/create-react-app) 33 | [![forthebadge](http://forthebadge.com/images/badges/built-with-love.svg)](http://forthebadge.com) 34 | 35 | HRMS Frontend project. (You can access the backend [here](https://github.com/MrPand-21/HRMS_Backend) and use them together to work with real database and backend) 36 | 37 | ### UML Component Diagram 38 | 39 | ![UML Component Diagram](../Diagram.png) 40 | 41 | ## Installation 42 | 43 | To get this project you can run the following comments in your terminal which is opened in the folder where you want to copy to. 44 | 45 | ```bash 46 | # clones the selected repository 47 | $ git clone https://github.com/MrPand-21/HRMS_Frontend 48 | ``` 49 | 50 | After opened the project in the terminal, you can write 51 | 52 | ``` bash 53 | # installs the dependencies 54 | $ npm i 55 | ``` 56 | 57 | ## Usage 58 | 59 | In the project directory, you can run: 60 | 61 | ```bash 62 | # runs the app in the development mode. 63 | $ npm start 64 | ``` 65 | 66 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 67 | 68 | The page will reload if you make edits.\ 69 | You will also see any lint errors in the console. 70 | 71 | ```bash 72 | # lunches the test runner in the interactive watch mode. 73 | $ npm test 74 | ``` 75 | 76 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 77 | 78 | ```bash 79 | # builds the app for production to the `build` folder. 80 | $ npm run build 81 | ``` 82 | 83 | It correctly bundles React in production mode and optimizes the build for the best performance. 84 | 85 | The build is minified and the filenames include the hashes.\ 86 | Your app is ready to be deployed! 87 | 88 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 89 | 90 | ```bash 91 | #you may want to omit "eject" 92 | $ npm run eject 93 | ``` 94 | 95 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 96 | 97 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 98 | 99 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 100 | 101 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 102 | 103 | ## Contributing 104 | 105 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. Right now there is only one branch which is master. 106 | 107 | 1. Fork the Project 108 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 109 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 110 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 111 | 5. Open a Pull Request 112 | 113 | ## Learn More 114 | 115 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 116 | 117 | To learn React, check out the [React documentation](https://reactjs.org/). 118 | 119 | * [Code Splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 120 | * [Analyzing the Bundle Size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 121 | * [Making a Progressive Web App](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 122 | * [Advanced Configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 123 | * [Deployment](https://facebook.github.io/create-react-app/docs/deployment) 124 | * [npm run build fails to minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 125 | 126 | ## Credits 127 | 128 | [Carabelli](MrPanda+HRMS_Frontend@protonmail.ch) 129 | 130 | #### Acknowledgements 131 | 132 | - [Engin Demirog](https://www.linkedin.com/in/engindemirog) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hrms-project", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^11.2.7", 8 | "@testing-library/user-event": "^12.8.3", 9 | "axios": "^0.21.1", 10 | "formik": "^2.2.9", 11 | "react": "^17.0.2", 12 | "react-dom": "^17.0.2", 13 | "react-router-dom": "^5.2.0", 14 | "react-scripts": "4.0.3", 15 | "react-toastify": "^7.0.4", 16 | "semantic-ui-css": "^2.4.1", 17 | "semantic-ui-react": "^2.0.3", 18 | "web-vitals": "^1.1.2", 19 | "yup": "^0.32.9" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "eslintConfig": { 28 | "extends": [ 29 | "react-app", 30 | "react-app/jest" 31 | ] 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | HRMS Project 12 | 13 | 14 | 15 |
16 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "HRMS Project", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | #jobSeekerDetail{ 6 | background-color: #f09595c0; 7 | } 8 | 9 | #menu-blank{ 10 | padding-bottom: 500px; 11 | } 12 | .blue{ 13 | background: blue; 14 | } 15 | .marginLeft{ 16 | margin-left: 2.7em; 17 | } 18 | 19 | .labelForJobAddFields{ 20 | margin-right: 2em; 21 | } 22 | 23 | .paddingRightSmall{ 24 | padding-right:1em; 25 | } 26 | 27 | .paddingRight{ 28 | padding-right:2em; 29 | } 30 | 31 | .paddingRightMedium{ 32 | padding-right:3em; 33 | } 34 | 35 | .paddingRightLarge{ 36 | padding-right:5em; 37 | } 38 | 39 | #headerForJob{ 40 | margin-bottom: 2em; 41 | } 42 | 43 | #deadlineField{ 44 | margin-right: -54px; 45 | } 46 | 47 | .clearMargins{ 48 | margin-top: 0px; 49 | margin-bottom: 0px; 50 | } 51 | 52 | .marginBottom{ 53 | margin-bottom: 1em; 54 | 55 | } 56 | .marginTopSmall{ 57 | margin-top: 1em; 58 | 59 | } 60 | 61 | .marginBottomMedium{ 62 | margin-bottom: 2em; 63 | } 64 | .marginTop{ 65 | margin-top: 5em; 66 | } 67 | #bg-for-menu{ 68 | 69 | position: absolute; 70 | z-index: 0; 71 | height: 100%; 72 | width:100px; 73 | box-sizing: border-box; 74 | background: linear-gradient(180deg,#8686d5,#00d4ff); 75 | clip-path: polygon(0 0, 56% 0, 100% 0%, 0% 100%); 76 | 77 | } 78 | 79 | div [class*="left floated"] { 80 | float: left; 81 | margin-left: 0.25em; 82 | } 83 | 84 | div [class*="right floated"] { 85 | float: right; 86 | margin-right: 0.25em; 87 | } 88 | 89 | .labelForEmployerSignUp{ 90 | margin-right: 10px; 91 | font-size:22px; 92 | color: rgb(13, 158, 255); 93 | text-align: center; 94 | } 95 | 96 | .labelForSystemUserSignUp{ 97 | margin-right: 10px; 98 | font-size:22px; 99 | color: rgb(13, 158, 255); 100 | text-align: center; 101 | } 102 | 103 | .employer-header{ 104 | margin-top: 3em !important; 105 | text-align: center; 106 | } 107 | 108 | .jobSeeker-header{ 109 | margin-top: 3em !important; 110 | text-align: center; 111 | } 112 | 113 | #menu{ 114 | 115 | background-color: #1B1C1D; 116 | display: flex; 117 | justify-content: center; 118 | align-items: top; 119 | min-height: 100vh; 120 | height:110%; 121 | 122 | } 123 | 124 | #footer{ 125 | background-color: #1B1C1D; 126 | } 127 | 128 | #content-area{ 129 | margin-top: 3em; 130 | margin-right: 4em; 131 | } 132 | 133 | #white-jobseeker{ 134 | color:white; 135 | } 136 | 137 | #white-employername{ 138 | color:white; 139 | } 140 | 141 | * { 142 | box-sizing: border-box; 143 | margin: 0; 144 | padding: 0; 145 | text-rendering: optimizeLegibility; 146 | -webkit-font-smoothing: antialiased; 147 | -moz-osx-font-smoothing: grayscale; 148 | -webkit-tap-highlight-color: transparent; 149 | } 150 | 151 | html { 152 | font-size: 16px; 153 | margin: 0; 154 | padding: 0; 155 | text-decoration-skip-ink: "auto"; 156 | } 157 | 158 | :root { 159 | --gray-900: #1a202c; 160 | --gray-800: #2d3748; 161 | --gray-700: #4a5568; 162 | --gray-600: #718096; 163 | --gray-500: #a0aec0; 164 | --gray-400: #cbd5e0; 165 | --gray-300: #e2e8f0; 166 | --gray-200: #edf2f7; 167 | --gray-100: #f7fafc; 168 | --red-100: #fff5f5; 169 | --red-200: #fed7d7; 170 | --red-300: #feb2b2; 171 | --red-400: #fc8181; 172 | --red-500: #f56565; 173 | --red-600: #e53e3e; 174 | --red-700: #c53030; 175 | --red-800: #9b2c2c; 176 | --red-900: #742a2a; 177 | --blue-100: #ebf8ff; 178 | --blue-200: #bee3f8; 179 | --blue-300: #90cdf4; 180 | --blue-400: #63b3ed; 181 | --blue-500: #4299e1; 182 | --blue-600: #3182ce; 183 | --blue-700: #2b6cb0; 184 | --blue-800: #2c5282; 185 | --blue-900: #2a4365; 186 | --white: white; 187 | --red: #f56565; 188 | --text-decoration-color: var(--gray-400); 189 | --text-color: var(--gray-800); 190 | --focus-ring-color: var(--blue-500); 191 | } 192 | 193 | body { 194 | padding: 0; 195 | margin: calc((100vh / 25) * 1.563) calc((100vw / 25) * 1.563); 196 | background-color: white; 197 | font-weight: 400; 198 | line-height: 1.563; 199 | color: var(--text-color); 200 | caret-color: var(--text-color); 201 | } 202 | 203 | @media (prefers-color-scheme: dark) { 204 | body { 205 | color: white; 206 | background-color: var(--gray-900); 207 | caret-color: white; 208 | } 209 | } 210 | 211 | /* Typography 212 | ––––––––––––––––––––––––––––––––– */ 213 | 214 | h1, 215 | h2, 216 | h3, 217 | h4, 218 | h5, 219 | h6 { 220 | margin-bottom: 1rem; 221 | margin-top: 1em; 222 | font-weight: bold; 223 | } 224 | 225 | h1 { 226 | font-size: 3.052rem !important; 227 | line-height: 1; 228 | } 229 | 230 | h2 { 231 | font-size: 2.441rem; 232 | line-height: 1.2; 233 | } 234 | 235 | h3 { 236 | font-size: 1.953rem; 237 | line-height: 1.2; 238 | } 239 | 240 | h4 { 241 | font-size: 1.563rem; 242 | line-height: 1.3; 243 | } 244 | 245 | h5 { 246 | font-size: 1.25rem; 247 | line-height: 1.4; 248 | } 249 | 250 | h6 { 251 | font-size: 1rem; 252 | 253 | line-height: 1.5; 254 | } 255 | 256 | p { 257 | margin-bottom: 1.563rem; 258 | } 259 | 260 | p > *:last-child { 261 | margin-bottom: 0; 262 | } 263 | 264 | a { 265 | color: inherit; 266 | text-decoration: underline; 267 | text-decoration-color: var(--text-decoration-color); 268 | -webkit-text-decoration-color: var(--text-decoration-color); 269 | border-radius: 5px; 270 | text-underline-offset: 2px; 271 | text-decoration-thickness: 2px; 272 | } 273 | 274 | @media (hover: hover) { 275 | a:hover { 276 | text-decoration-color: var(--gray-800); 277 | } 278 | } 279 | 280 | @media (hover: hover) and (prefers-color-scheme: dark) { 281 | a:hover { 282 | text-decoration-color: white; 283 | } 284 | } 285 | 286 | @media (prefers-color-scheme: dark) { 287 | a { 288 | text-decoration-color: var(--gray-700); 289 | -webkit-text-decoration-color: var(--gray-700); 290 | } 291 | } 292 | 293 | a:focus-visible { 294 | box-shadow: 0 0 0 2px var(--focus-ring-color); 295 | outline: none; 296 | } 297 | 298 | small { 299 | font-size: 0.888rem; 300 | } 301 | 302 | hr { 303 | border: 1px solid var(--gray-700); 304 | margin: 3.052rem 0; 305 | } 306 | 307 | /* Form 308 | ––––––––––––––––––––––––––––––––– */ 309 | label { 310 | font-weight: bold; 311 | display: flex; 312 | } 313 | 314 | input[type="email"], 315 | input[type="text"], 316 | input[type="number"] { 317 | padding: 0.65rem 0.5rem; 318 | border: 2px solid var(--gray-200); 319 | background-color: var(--gray-100); 320 | color: var(--gray-800); 321 | border-radius: 10px; 322 | -webkit-appearance: none; 323 | -moz-appearance: none; 324 | appearance: none; 325 | } 326 | 327 | @media (prefers-color-scheme: dark) { 328 | input[type="email"], 329 | input[type="text"], 330 | input[type="number"] { 331 | background-color: var(--gray-800); 332 | border-color: var(--gray-700); 333 | color: white; 334 | } 335 | 336 | input[type="email"]::placeholder, 337 | input[type="text"]::placeholder, 338 | input[type="number"]::placeholder, 339 | select, 340 | textarea { 341 | color: var(--gray-500); 342 | } 343 | } 344 | 345 | input[type="email"], 346 | input[type="text"], 347 | input[type="number"], 348 | textarea, 349 | select { 350 | width: 400px; 351 | } 352 | 353 | select { 354 | margin: 0; 355 | -webkit-appearance: none; 356 | box-sizing: border-box; 357 | padding: 0.65rem 0.5rem; 358 | font-size: 1rem; 359 | border: 2px solid var(--gray-200); 360 | border-radius: 10px; 361 | color: var(--gray-700); 362 | height: auto; 363 | background-color: var(--gray-100); 364 | background-image: url('data:image/svg+xml;utf8,'); 365 | background-repeat: no-repeat; 366 | background-size: 1rem; 367 | background-position: center right 0.5rem; 368 | } 369 | 370 | @media (prefers-color-scheme: dark) { 371 | select { 372 | background-color: var(--gray-800); 373 | background-image: url('data:image/svg+xml;utf8,'); 374 | border-color: var(--gray-700); 375 | color: white; 376 | } 377 | } 378 | 379 | textarea { 380 | -webkit-appearance: none; 381 | -moz-appearance: none; 382 | appearance: none; 383 | border: 2px solid var(--gray-200); 384 | color: var(--gray-700); 385 | border-radius: 10px; 386 | resize: vertical; 387 | background-color: var(--gray-100); 388 | box-sizing: border-box; 389 | padding: 0.65rem 0.5rem; 390 | } 391 | 392 | @media (prefers-color-scheme: dark) { 393 | textarea { 394 | background-color: var(--gray-800); 395 | border-color: var(--gray-700); 396 | color: white; 397 | } 398 | } 399 | 400 | input:focus, 401 | select:focus, 402 | textarea:focus { 403 | outline: none; 404 | border: 2px solid var(--focus-ring-color); 405 | } 406 | 407 | input:invalid, 408 | select:invalid, 409 | textarea:invalid { 410 | border: 2px solid #ff7d87; 411 | box-shadow: none; 412 | } 413 | 414 | input[type="checkbox"] { 415 | display: inline-block; 416 | height: 1rem; 417 | border-radius: 5px; 418 | -webkit-appearance: none; 419 | -moz-appearance: none; 420 | appearance: none; 421 | border: 2px solid var(--gray-300); 422 | width: 1rem; 423 | background-color: white; 424 | align-self: center; 425 | margin-right: 0.5rem; 426 | } 427 | 428 | @media (prefers-color-scheme: dark) { 429 | input[type="checkbox"] { 430 | background-color: var(--gray-800); 431 | border-color: var(--gray-600); 432 | } 433 | } 434 | 435 | input[type="checkbox"]:hover { 436 | cursor: pointer; 437 | } 438 | 439 | input[type="checkbox"]:checked { 440 | background-image: url('data:image/svg+xml;utf8,'); 441 | background-size: contain; 442 | background-color: var(--gray-700); 443 | border: 2px solid var(--gray-700); 444 | } 445 | 446 | input[type="checkbox"]:focus-visible, 447 | input[type="checkbox"]:checked:focus-visible { 448 | border-color: var(--focus-ring-color); 449 | } 450 | 451 | @media (prefers-color-scheme: dark) { 452 | input[type="checkbox"]:checked { 453 | background-image: url('data:image/svg+xml;utf8,'); 454 | background-color: white; 455 | border: 2px solid white; 456 | } 457 | } 458 | 459 | input[type="radio"] { 460 | -webkit-appearance: none; 461 | -moz-appearance: none; 462 | appearance: none; 463 | border-radius: 50%; 464 | border: 2px solid var(--gray-300); 465 | height: 1rem; 466 | width: 1rem; 467 | margin-right: 0.5rem; 468 | align-self: center; 469 | justify-content: center; 470 | position: relative; 471 | display: flex; 472 | } 473 | 474 | @media (prefers-color-scheme: dark) { 475 | input[type="radio"] { 476 | border: 2px solid white; 477 | } 478 | } 479 | 480 | @media (hover: hover) { 481 | input[type="radio"]:hover { 482 | cursor: pointer; 483 | } 484 | } 485 | 486 | input[type="radio"]:checked { 487 | border: 2px solid var(--gray-700); 488 | } 489 | 490 | input[type="radio"]:focus-visible, 491 | input[type="radio"]:checked:focus-visible { 492 | border-color: var(--focus-ring-color); 493 | } 494 | 495 | @media (prefers-color-scheme: dark) { 496 | input[type="radio"]:checked { 497 | border: 2px solid white; 498 | } 499 | } 500 | 501 | input[type="radio"]:checked::before { 502 | content: ""; 503 | width: calc(100% - 4px); 504 | height: calc(100% - 4px); 505 | background-color: var(--gray-700); 506 | align-self: center; 507 | border-radius: 50%; 508 | } 509 | 510 | @media (prefers-color-scheme: dark) { 511 | input[type="radio"]:checked::before { 512 | background-color: white; 513 | } 514 | } 515 | 516 | input[type="submit"], 517 | input[type="reset"], 518 | input[type="button"], 519 | button { 520 | padding: 0.5rem 1.25rem; 521 | border-radius: 10px; 522 | background-color: var(--gray-700); 523 | border: 2px solid var(--gray-700); 524 | color: white; 525 | text-decoration: none; 526 | font-weight: bold; 527 | margin-bottom: 1rem; 528 | -webkit-appearance: none; 529 | -moz-appearance: none; 530 | appearance: none; 531 | display: inline-block; 532 | transition: background-color 200ms ease-in-out, border 200ms ease-in-out, 533 | transform 200ms ease-in-out; 534 | -webkit-touch-callout: none; 535 | -webkit-user-select: none; 536 | user-select: none; 537 | } 538 | 539 | @media (prefers-color-scheme: dark) { 540 | input[type="submit"], 541 | input[type="reset"], 542 | input[type="button"], 543 | button { 544 | background-color: white; 545 | border: 2px solid white; 546 | color: var(--gray-800); 547 | } 548 | } 549 | 550 | @media (hover: hover) { 551 | input[type="submit"]:hover, 552 | input[type="reset"]:hover, 553 | input[type="button"]:hover, 554 | button:hover { 555 | cursor: pointer; 556 | background-color: var(--gray-800); 557 | } 558 | } 559 | 560 | @media (hover: hover) and (prefers-color-scheme: dark) { 561 | input[type="submit"]:hover, 562 | input[type="reset"]:hover, 563 | input[type="button"]:hover, 564 | button:hover { 565 | cursor: pointer; 566 | background-color: var(--gray-300); 567 | } 568 | } 569 | 570 | button:focus-visible, 571 | input[type="submit"]:focus-visible, 572 | input[type="reset"]:focus-visible, 573 | input[type="button"]:focus-visible { 574 | border-color: var(--focus-ring-color); 575 | outline: none; 576 | } 577 | 578 | /* Tables 579 | ––––––––––––––––––––––––––––––––– */ 580 | table { 581 | width: 100%; 582 | border-spacing: 0; 583 | margin-bottom: 1.563rem; 584 | font-variant-numeric: tabular-nums; 585 | } 586 | 587 | th, 588 | td { 589 | padding: 0.5rem 0.5rem 0.5rem 0; 590 | margin: 0; 591 | } 592 | 593 | th { 594 | font-weight: bold; 595 | text-align: left; 596 | border-bottom: 2px solid var(--gray-300); 597 | color: var(--gray-300); 598 | } 599 | 600 | @media (prefers-color-scheme: dark) { 601 | th { 602 | border-color: var(--gray-700); 603 | color: var(--gray-700); 604 | } 605 | } 606 | 607 | td { 608 | border-bottom: 2px solid var(--gray-300); 609 | } 610 | 611 | @media (prefers-color-scheme: dark) { 612 | td { 613 | border-color: var(--gray-700); 614 | } 615 | } 616 | 617 | /* Code 618 | ––––––––––––––––––––––––––––––––– */ 619 | code { 620 | background: var(--gray-100); 621 | padding: 0 0.328rem; 622 | display: inline-block; 623 | vertical-align: middle; 624 | border-radius: 10px; 625 | } 626 | 627 | @media (prefers-color-scheme: dark) { 628 | code { 629 | background-color: var(--gray-700); 630 | } 631 | } 632 | 633 | p > code { 634 | white-space: normal; 635 | } 636 | 637 | pre > code { 638 | line-height: 1.563em; 639 | display: block; 640 | padding: 1rem; 641 | white-space: pre; 642 | margin-bottom: 1.563rem; 643 | overflow: scroll; 644 | } 645 | 646 | /* Forces a new-line at the end of a code block for layout purposes. */ 647 | pre > code::after { 648 | content: " "; 649 | } 650 | 651 | /* Blockquote 652 | ––––––––––––––––––––––––––––––––– */ 653 | blockquote { 654 | border-left: 0.25rem solid var(--gray-100); 655 | padding: 0 1rem; 656 | margin-bottom: 1.563rem; 657 | } 658 | 659 | @media (prefers-color-scheme: dark) { 660 | blockquote { 661 | border-left: 0.25rem solid var(--gray-700); 662 | } 663 | } 664 | 665 | /* List 666 | ––––––––––––––––––––––––––––––––– */ 667 | ul { 668 | margin: 0; 669 | padding: 0 1px; 670 | list-style: disc outside; 671 | } 672 | 673 | ol { 674 | list-style: decimal outside; 675 | } 676 | 677 | ol, 678 | ul { 679 | padding-left: 0; 680 | margin-top: 0; 681 | margin-bottom: 1.563rem; 682 | } 683 | 684 | li { 685 | list-style-position: inside; 686 | } 687 | 688 | /* Keyboard 689 | ––––––––––––––––––––––––––––––––– */ 690 | kbd { 691 | display: inline-block; 692 | padding: 0 0.328rem; 693 | color: var(--gray-700); 694 | vertical-align: middle; 695 | background-color: var(--gray-100); 696 | border: solid 1px var(--gray-300); 697 | border-bottom: solid 2px var(--gray-500); 698 | border-radius: 5px; 699 | } 700 | 701 | /* Abbreviation 702 | ––––––––––––––––––––––––––––––––– */ 703 | abbr { 704 | text-decoration: none; 705 | border-bottom: 2px dashed var(--gray-600); 706 | } 707 | 708 | @media (hover: hover) { 709 | abbr:hover { 710 | cursor: help; 711 | } 712 | } 713 | 714 | .error { 715 | font-size: 12px; 716 | color: var(--red-600); 717 | margin-top: 0.25rem; 718 | } 719 | 720 | @media (prefers-color-scheme: dark) { 721 | .error { 722 | color: var(--red-400); 723 | } 724 | } 725 | 726 | button:disabled { 727 | opacity: 0.5; 728 | cursor: not-allowed !important; 729 | } 730 | 731 | button + button { 732 | margin-left: 0.5rem; 733 | } 734 | 735 | button.secondary, 736 | button[type="reset"] { 737 | background-color: var(--gray-300); 738 | border: 2px solid var(--gray-300); 739 | color: var(--gray-900); 740 | } 741 | 742 | button.secondary:hover, 743 | button[type="reset"]:hover { 744 | background-color: var(--gray-400); 745 | } 746 | 747 | /* custom */ 748 | input[type="email"], 749 | input[type="text"], 750 | input[type="number"], 751 | textarea, 752 | select { 753 | width: 400px; 754 | } 755 | 756 | label { 757 | margin-top: 1rem; 758 | } 759 | 760 | button[type="submit"] { 761 | display: block; 762 | margin-top: 1em; 763 | text-align: center; 764 | } 765 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import Navi from './layouts/Navi'; 3 | import Dashboard from './layouts/Dashboard'; 4 | import {Grid} from 'semantic-ui-react'; 5 | import Footer from './layouts/Footer'; 6 | 7 | 8 | 9 | function App() { 10 | return ( 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
24 | ); 25 | } 26 | 27 | export default App; 28 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | import 'semantic-ui-css/semantic.min.css'; 7 | import { BrowserRouter } from 'react-router-dom'; 8 | 9 | ReactDOM.render( 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /src/layouts/Dashboard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import ChoosingSignInMethod from '../pages/ChoosingSignInMethod' 4 | import ChoosingSignUpMethod from '../pages/ChoosingSignUpMethod' 5 | import EmployerList from '../pages/Employers/EmployerList' 6 | import JobList from '../pages/Jobs/JobList' 7 | import JobPositionList from '../pages/JobPositionList' 8 | import JobSeekerList from '../pages/JobSeekers/JobSeekerList' 9 | import { Route } from 'react-router' 10 | import EmployerSignUpPage from '../pages/Employers/EmployerSignUpPage' 11 | import JobAddPage from '../pages/Employers/JobAddPage' 12 | import SystemUserSignUpPage from '../pages/SystemUsers/SystemUserSignUpPage' 13 | import JobConfirmationPanel from '../pages/SystemUsers/JobConfirmationPanel' 14 | import JobSeekerSignUpPage from '../pages/JobSeekers/JobSeekerSignUpPage' 15 | import MainPage from '../pages/MainPage' 16 | import JobSeekerDetail from '../pages/JobSeekers/JobSeekerDetail' 17 | import { ToastContainer } from "react-toastify"; 18 | import EmployerDetailPage from '../pages/Employers/EmployerDetailPage' 19 | import JobDetailPage from '../pages/Jobs/JobDetailPage' 20 | 21 | export default function Dashboard() { 22 | return ( 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /src/layouts/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Footer() { 4 | return ( 5 | 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/layouts/Navi.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Dropdown, Icon, Input, Menu } from 'semantic-ui-react' 3 | import SignedIn from './SignedIn'; 4 | import SignedOut from './SignedOut'; 5 | import {Link} from 'react-router-dom'; 6 | 7 | export default function Navi() { 8 | 9 | 10 | 11 | const [isAuthenticated, setIsAuthanticated] = useState(false) 12 | 13 | function handleSignOut(params) { 14 | setIsAuthanticated(false) 15 | } 16 | 17 | function handleSignIn(params) { 18 | setIsAuthanticated(true) 19 | } 20 | 21 | 22 | return ( 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Home 32 | 33 | 36 | Jobs 37 | 38 | 41 | Job Seekers 42 | 43 | 46 | Employers 47 | 48 | 51 | Job Positions 52 | 53 | 54 | 55 | 56 | 59 | 60 | Browse 61 | 62 | 65 | Messages 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | {isAuthenticated ? : } 77 | 78 | 79 | 80 | 81 |
82 | ); 83 | } 84 | -------------------------------------------------------------------------------- /src/layouts/SignedIn.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function SignedIn() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/layouts/SignedOut.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // import { useState } from 'react'; 3 | import {Button} from 'semantic-ui-react'; 4 | import { NavLink } from 'react-router-dom' 5 | 6 | export default function SignedOutSignedOut(params) { 7 | 8 | // const [signUpOrIn, setSignUpOrIn] = useState() 9 | 10 | 11 | 12 | return ( 13 |
14 | 15 | 16 | 17 | 18 | 19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/pages/ChoosingSignInMethod.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Button, Header} from 'semantic-ui-react'; 3 | 4 | export default function ChoosingSignInMethod() { 5 | return ( 6 |
7 |
8 | Sign In As a ... 9 |
10 | 11 | 12 | 13 | 14 | 15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/pages/ChoosingSignUpMethod.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import {Button, Header} from 'semantic-ui-react'; 3 | import { NavLink } from 'react-router-dom'; 4 | 5 | 6 | export default function ChoosingSignUpMethod() { 7 | 8 | const [colorOfTitle, setcolorOfTitle] = useState("black") 9 | 10 | return ( 11 |
12 |
13 | Sign Up As a ... 14 |
15 | 16 | 17 | 18 | 19 | 20 |
21 | ); 22 | } -------------------------------------------------------------------------------- /src/pages/Employers/EmployerDetailPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { useParams } from 'react-router'; 3 | import { Table, Header, Grid, Segment,Icon, Label, Divider } from 'semantic-ui-react'; 4 | import { Link } from 'react-router-dom'; 5 | import EmployerService from '../../services/EmployerService'; 6 | 7 | export default function EmployerDetailPage() { 8 | const { employerId } = useParams() 9 | // const history = useHistory(); 10 | const [employer, setEmployer] = useState({}) 11 | useEffect(() => { 12 | let employerService = new EmployerService(); 13 | employerService.getEmployerById(employerId).then((result) => setEmployer(result.data.data)).catch() 14 | 15 | }, [employerId]) 16 | 17 | return ( 18 | 19 | 20 | 21 | {employer.webSites?.length < 1 ? null : 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Company 31 | Website 32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | {employer.companyName} 41 | {} 42 | 43 |
44 |
45 |
{employer.webSites}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | } 54 | 55 | {employer.webSites?.length < 1 ? null : 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | BETA 65 | BETA 66 | 67 | 68 | 69 | 70 | 71 |
72 | 73 | 74 | BETA 75 | BETA 76 | 77 |
78 |
79 |
BETA
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | } 88 | 89 |
90 | 91 | 92 | 93 | 94 |
{employer.user?.email}
95 | 96 |
Email
97 |
98 |
{employer.phone}
99 | 100 |
Phone Number
101 |
102 | 103 |
104 |
105 |
106 |
107 |
108 | ) 109 | } -------------------------------------------------------------------------------- /src/pages/Employers/EmployerList.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import EmployerService from '../../services/EmployerService' 3 | import { Table, Header, Button, Icon } from 'semantic-ui-react'; 4 | import { Link } from 'react-router-dom'; 5 | 6 | export default function EmployerList() { 7 | 8 | const [employers, setEmployers] = useState([]) 9 | useEffect(() => { 10 | let employerService = new EmployerService(); 11 | 12 | employerService.getEmployers().then((result) => setEmployers(result.data.data)).catch(); 13 | }, []) 14 | 15 | return ( 16 |
17 | 18 | 19 | 20 | Company Name 21 | Website(-s) 22 | Phone Number 23 | Email 24 | Joined Date 25 | Other infos 26 | 27 | 28 | 29 | 30 | {employers.map(employer => ( 31 | 32 | 33 | 34 |
35 | {employer.companyName} 36 |
37 |
38 | {employer.webSites} 39 | {employer.phone} 40 | {employer.user.email} 41 | {employer.user.createdDate} 42 | 43 | 49 | 50 |
51 | ))} 52 |
53 |
54 |
55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /src/pages/Employers/EmployerSignUpPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Header, Button} from 'semantic-ui-react'; 3 | import {Formik, Form, useField} from 'formik'; 4 | import * as Yup from 'yup'; 5 | import {useHistory} from 'react-router-dom'; 6 | 7 | export default function EmployerSignUpPage() { 8 | const history = useHistory(); 9 | 10 | const TextField = ({label, ...props}) => { 11 | // useField() returns [formik.getFieldProps(), formik.getFieldMeta()] 12 | // which we can spread on . We can use field meta to show an error 13 | // message if the field is invalid and it has been touched (i.e. visited) 14 | const [field, meta] = useField(props); 15 | return ( 16 |
17 |
18 | 23 | 24 | 25 |
26 |
27 | {meta.touched && meta.error ? ( 28 |
29 |
{meta.error}
30 |
31 | ) : null} 32 |
33 | ); 34 | }; 35 | 36 | return ( 37 |
38 |
39 | Employer 40 |
41 | 42 | { 50 | 51 | history.push('/employer/' + values.id); 52 | }}> 53 |
54 | 61 |
62 | 63 | 66 | 67 |
68 |
69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /src/pages/Employers/JobAddPage.jsx: -------------------------------------------------------------------------------- 1 | import {Formik, Form, useField} from 'formik'; 2 | import React, {useEffect, useState} from 'react'; 3 | import {useHistory, useParams} from 'react-router'; 4 | import JobService from '../../services/JobService'; 5 | import JobPositionService from '../../services/JobPositionService'; 6 | import CityService from '../../services/CityService'; 7 | import WorkPlaceService from '../../services/WorkPlaceService'; 8 | import WorkTimeService from '../../services/WorkTimeService'; 9 | import * as Yup from 'yup'; 10 | import { 11 | Button, 12 | Dropdown, 13 | Grid, 14 | Header, 15 | Label, 16 | Checkbox, 17 | } from 'semantic-ui-react'; 18 | 19 | export default function JobAddPage() { 20 | let {employerId} = useParams(); 21 | 22 | let jobService = new JobService(); 23 | const history = useHistory(); 24 | 25 | const [workTimes, setWorkTimes] = useState([]); 26 | const [workPlaces, setWorkPlaces] = useState([]); 27 | const [cities, setCities] = useState([]); 28 | const [jobPositions, setJobPositions] = useState([]); 29 | 30 | useEffect(() => { 31 | let workPlaceService = new WorkPlaceService(); 32 | let workTimeService = new WorkTimeService(); 33 | let cityService = new CityService(); 34 | let jobPositionService = new JobPositionService(); 35 | 36 | workPlaceService 37 | .getWorkPlaces() 38 | .then((result) => setWorkPlaces(result.data.data)); 39 | 40 | workTimeService 41 | .getWorkTimes() 42 | .then((result) => setWorkTimes(result.data.data)); 43 | 44 | cityService 45 | .getCities() 46 | .then((result) => setCities(result.data.data)) 47 | .catch(); 48 | jobPositionService 49 | .getJobPositions() 50 | .then((result) => setJobPositions(result.data.data)) 51 | .catch(); 52 | }, []); 53 | 54 | const workTimeOptions = workTimes.map((workTime, index) => ({ 55 | key: index, 56 | text: workTime.workTimeName, 57 | value: workTime.id, 58 | })); 59 | 60 | const WorkPlaceOptions = workPlaces.map((workPlace, index) => ({ 61 | key: index, 62 | text: workPlace.workPlaceName, 63 | value: workPlace.id, 64 | })); 65 | 66 | const CityOptions = cities.map((city, index) => ({ 67 | key: index, 68 | text: city.cityName, 69 | value: city.id, 70 | })); 71 | 72 | const JobPositionOptions = jobPositions.map((jobPosition, index) => ({ 73 | key: index, 74 | text: jobPosition.position_name, 75 | value: jobPosition.id, 76 | })); 77 | 78 | const InputField = ({label, ...props}) => { 79 | // useField() returns [formik.getFieldProps(), formik.getFieldMeta()] 80 | // which we can spread on . We can use field meta to show an error 81 | // message if the field is invalid and it has been touched (i.e. visited) 82 | const [field, meta] = useField(props); 83 | return ( 84 |
85 |
86 | 90 | 91 | 92 |
93 |
94 | {meta.touched && meta.error ? ( 95 |
96 |
{meta.error}
97 |
98 | ) : null} 99 |
100 | ); 101 | }; 102 | 103 | const TrueFalse = ({children, ...props}) => { 104 | // React treats radios and checkbox inputs differently other input types, select, and textarea. 105 | // Formik does this too! When you specify `type` to useField(), it will 106 | // return the correct bag of props for you -- a `checked` prop will be included 107 | // in `field` alongside `name`, `value`, `onChange`, and `onBlur` 108 | const [field, meta] = useField({...props, type: 'checkbox'}); 109 | return ( 110 |
111 | 112 | 113 | {meta.touched && meta.error ? ( 114 | 117 | ) : null} 118 |
119 | ); 120 | }; 121 | 122 | const SelectionFields = ({label, ...props}) => { 123 | const [field, meta] = useField(props); 124 | return ( 125 |
126 | 127 | 128 | 129 | {meta.touched && meta.error ? ( 130 |
{meta.error}
131 | ) : null} 132 |
133 | ); 134 | }; 135 | 136 | const handleChangeSemantic = (prop, value, fieldName) => { 137 | prop.setFieldValue(fieldName, value); 138 | }; 139 | 140 | const handleOnSubmit = (values) => { 141 | values.employerId = employerId; 142 | values.createdDate = new Date(); 143 | jobService.addJob(values); 144 | alert("Job added. After system user's confirmation, it will be listed."); 145 | history.push('/'); 146 | }; 147 | return ( 148 |
149 |
155 | Add Job 156 |
157 | { 193 | handleOnSubmit(values); 194 | }}> 195 | {(formikprops) => ( 196 |
197 | 198 | 199 | 200 | 206 | 207 | 208 |
209 | 217 |
218 |
219 |
220 | 221 | 222 | 228 | 229 | 230 | 231 | 237 | 238 | 239 | 240 | 241 | 242 | 249 | handleChangeSemantic( 250 | formikprops, 251 | data.value, 252 | 'jobPositionId' 253 | ) 254 | } 255 | /> 256 | 257 | 258 | 264 | handleChangeSemantic(formikprops, data.value, 'cityId') 265 | } 266 | value={formikprops.values.cityId} 267 | item 268 | options={CityOptions} 269 | /> 270 | 271 | 272 | 273 | 274 | 275 | 283 | handleChangeSemantic( 284 | formikprops, 285 | data.value, 286 | 'workTimeId' 287 | ) 288 | } 289 | /> 290 | 291 | 292 | 300 | handleChangeSemantic( 301 | formikprops, 302 | data.value, 303 | 'workPlaceId' 304 | ) 305 | } 306 | /> 307 | 308 | 309 | 310 | 311 | 312 | 320 | 321 | 322 |
323 | 324 | 325 | 330 | 331 | 332 | 333 |
334 | 335 |
347 | ); 348 | } 349 | -------------------------------------------------------------------------------- /src/pages/JobPositionList.jsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import {Table, Header} from 'semantic-ui-react'; 3 | import JobPositionService from '../services/JobPositionService'; 4 | 5 | export default function JobPositionList() { 6 | const [jobPositions, setJobPositions] = useState([]); 7 | useEffect(() => { 8 | let jobPositionService = new JobPositionService(); 9 | 10 | jobPositionService 11 | .getJobPositions() 12 | .then((result) => setJobPositions(result.data.data)) 13 | .catch(); 14 | }); 15 | 16 | return ( 17 |
18 | 19 | 20 | 21 | Position Name 22 | Id of Position 23 | 24 | 25 | 26 | {jobPositions.map((jobPosition) => ( 27 | 28 | 29 |
30 | {jobPosition.position_name} 31 |
32 |
33 | 34 | {jobPosition.id} 35 | 36 |
37 | ))} 38 |
39 |
40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/pages/JobSeekers/JobSeekerDetail.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { useParams } from 'react-router'; 3 | // import { useHistory } from 'react-router'; 4 | import { Table, Header, Grid, Segment, Image, Card, Label, Divider } from 'semantic-ui-react'; 5 | import JobSeekerService from '../../services/JobSeekerService' 6 | 7 | export default function JobSeekerDetail() { 8 | 9 | const { jobSeekerId } = useParams() 10 | // const history = useHistory(); 11 | const [jobSeeker, setJobSeeker] = useState({}) 12 | useEffect(() => { 13 | let jobSeekerService = new JobSeekerService(); 14 | jobSeekerService.getJobSeekerById(jobSeekerId).then((result) => setJobSeeker(result.data.data)).catch() 15 | 16 | }, [jobSeekerId]) 17 | 18 | 19 | return ( 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | 34 | 35 | 36 | 37 | {!!jobSeeker.info ?

{jobSeeker.info}

:

There is no informaiton about {jobSeeker.firstName + " " + jobSeeker.lastName}...

} 38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {jobSeeker.attendedSchools?.length === 0 ? null : 51 | 52 | 53 | 54 | 55 | 56 | 57 | {jobSeeker.attendedSchools?.map((school) => 58 | 59 | 60 | 61 | {school.school.schoolName} 62 | {school.dateOfStarting} - {school.dateOfGraduation} 63 | 64 | 65 | 66 | 67 | 68 | )} 69 | 70 | 71 | 72 | 73 | 74 | } 75 | {jobSeeker.languages?.length === 0 ? null : 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | Language Name 85 | Level 86 | 87 | 88 | 89 | {jobSeeker.languages?.map((language) => 90 | 91 | 92 |
93 | 94 | 95 | {language.languageName} 96 | {language.languageLevel.levelDescription} 97 | 98 |
99 |
100 | {language.languageLevel.id} 101 |
102 | )} 103 |
104 |
105 |
106 |
107 |
108 |
109 | } 110 | {jobSeeker.workExperiences?.length === 0 ? null : 111 | 112 | 113 | 114 | 115 | 116 | 117 | {jobSeeker.workExperiences?.map((experience) => 118 | 119 | 120 | {experience.workplaceName} 121 | {experience.startingDate} - {experience.quitDate} 122 | 123 | {experience.jobPosition.position_name} 124 | 125 | 126 | 127 | )} 128 | 129 | 130 | 131 | 132 | 133 | } 134 |
135 | 136 | 137 | 138 | 139 |
{jobSeeker.user?.email}
140 | 141 |
Email
142 |
143 |
{jobSeeker.linkedInAccount}
144 | 145 |
LinkedIn
146 |
147 |
{jobSeeker.githubAccount}
148 | 149 |
Github
150 |
151 |
152 |
153 |
154 |
155 | 156 | ) 157 | } 158 | -------------------------------------------------------------------------------- /src/pages/JobSeekers/JobSeekerList.jsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import {Table, Header, Button, Icon} from 'semantic-ui-react'; 3 | import JobSeekerService from '../../services/JobSeekerService'; 4 | import { Link } from 'react-router-dom'; 5 | 6 | export default function JobSeekerList() { 7 | const [jobSeekers, setJobSeekers] = useState([]); 8 | useEffect(() => { 9 | let jobSeekerService = new JobSeekerService(); 10 | jobSeekerService 11 | .getJobSeekers() 12 | .then((result) => setJobSeekers(result.data.data)) 13 | .catch(); 14 | }, []); 15 | 16 | return ( 17 |
18 | 19 | 20 | 21 | images 22 | Name 23 | languages 24 | work experiences 25 | attended Schools 26 | 27 | description 28 | Other infos 29 | 30 | 31 | 32 | 33 | {jobSeekers.map((jobseeker) => ( 34 | 35 | 36 | {jobseeker.images.imagePath == null ? ( 37 | 38 | 39 | 40 | ) : ( 41 | 42 | )} 43 | 44 | 45 |
50 | {jobseeker.firstName + ' ' + jobseeker.lastName} 51 |
52 |
53 | 54 | {jobseeker.languages.length === 0 55 | ? 'null' 56 | : jobseeker.languages.map( 57 | (language) => 58 | language.languageName + 59 | ' level : ' + 60 | language.languageLevel.id 61 | )} 62 | 63 | 64 | {!(!!jobseeker.workExperiences) 65 | ? 'null' 66 | : jobseeker.workExperiences.map( 67 | (workExperience) => 68 | 'Work place : ' + 69 | workExperience.workplaceName + 70 | ' Position : ' + 71 | workExperience.jobPosition.position_name 72 | )} 73 | 74 | 75 | {jobseeker.attendedSchools.length === 0 76 | ? 'null' 77 | : jobseeker.attendedSchools.map( 78 | (school) => 79 | 'School : ' + 80 | school.school.schoolName + 81 | ' Department : ' + 82 | null 83 | )} 84 | 85 | 86 | 87 | {jobseeker.info === null 88 | ? 'nothing has been written' 89 | : jobseeker.info} 90 | 91 | 92 | 98 | 99 |
100 | ))} 101 |
102 |
103 |
104 | ); 105 | } 106 | -------------------------------------------------------------------------------- /src/pages/JobSeekers/JobSeekerSignUpPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Header, Button} from 'semantic-ui-react'; 3 | import {Formik, Form, useField} from 'formik'; 4 | import * as Yup from 'yup'; 5 | import {useHistory} from 'react-router-dom'; 6 | 7 | export default function EmployerSignUpPage() { 8 | const history = useHistory(); 9 | 10 | const TextField = ({label, ...props}) => { 11 | 12 | const [field, meta] = useField(props); 13 | return ( 14 |
15 |
16 | 21 | 22 | 23 |
24 |
25 | {meta.touched && meta.error ? ( 26 |
27 |
{meta.error}
28 |
29 | ) : null} 30 |
31 | ); 32 | }; 33 | 34 | return ( 35 |
36 |
37 | Job Seeker 38 |
39 | 40 | { 48 | history.push('/jobseeker/' + values.id); 49 | }}> 50 |
51 | 58 |
59 | 60 | 63 | 64 |
65 |
66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /src/pages/Jobs/JobDetailPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { useParams } from 'react-router'; 3 | // import { useHistory } from 'react-router'; 4 | import { Container, Header, Grid, Segment, List, Icon, Label, Card, Button, Divider } from 'semantic-ui-react'; 5 | import JobService from '../../services/JobService' 6 | import EmployerService from '../../services/EmployerService' 7 | import { Link } from 'react-router-dom'; 8 | 9 | export default function JobDetailPage() { 10 | const { jobId } = useParams() 11 | const [job, setJob] = useState({}) 12 | const [employer, setEmployer] = useState({}) 13 | 14 | useEffect(() => { 15 | let jobService = new JobService(); 16 | jobService.getJobById(jobId).then((result) => setJob(result.data.data)).catch() 17 | }, [jobId]) 18 | 19 | useEffect(() => { 20 | let employerService = new EmployerService(); 21 | employerService.getEmployerById(job.employer?.id).then((result) => setEmployer(result.data.data)) 22 | }, [job.employer?.id]) 23 | console.log(employer) 24 | 25 | 26 | return ( 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | {job.jobPosition?.position_name} 35 | 36 | {"Company Name : " + employer.companyName} 37 | 38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | Number of empty positions 46 | 47 | 48 | {job.empty_positions} 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | Min - Max salary 57 | 58 | 59 | {job.minimumSalary} - {job.maximumSalary} 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Deadline 68 | 69 | 70 | {job.deadline} 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | Location 79 | 80 | 81 | {job.city?.cityName} 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | Work Place 90 | 91 | 92 | {job.workPlace?.workPlaceName} 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | Work Time 101 | 102 | 103 | {job.workTime?.workTimeName} 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | {job.description} 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 |
121 |
122 | 123 | 124 |
125 | 126 | 127 | 128 | 129 | 130 | 131 |
132 | 133 | {employer.companyName} 134 | 135 | 136 | {employer.user?.email} 137 | 138 |
139 | 140 | 146 | 147 | 148 |
149 |
150 |
151 |
152 |
153 | ) 154 | } -------------------------------------------------------------------------------- /src/pages/Jobs/JobList.jsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import {Table, Header, Button, Icon} from 'semantic-ui-react'; 3 | import JobService from '../../services/JobService'; 4 | import { Link } from 'react-router-dom'; 5 | 6 | export default function JobList() { 7 | const [jobs, setJobs] = useState([]); 8 | useEffect(() => { 9 | let jobService = new JobService(); 10 | 11 | jobService 12 | .getApprovedJobs() 13 | .then((result) => setJobs(result.data.data)) 14 | .catch(); 15 | }, []); 16 | 17 | 18 | 19 | return ( 20 |
21 | 22 | 23 | 24 | Postion 25 | Company Name 26 | Number of Empty Position 27 | City 28 | Created date 29 | Deadline 30 | Other infos 31 | 32 | 33 | 34 | 35 | {jobs.map((job) => ( 36 | 37 | 38 |
39 | {job.positionName} 40 |
41 |
42 | {job.companyName} 43 | 44 | {job.number_of_empty_positions == null 45 | ? '0' 46 | : job.number_of_empty_positions} 47 | 48 | {job.cityName} 49 | {job.createdDate} 50 | {job.deadLine} 51 | 52 | 58 | 59 |
60 | ))} 61 |
62 |
63 |
64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /src/pages/MainPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function MainPage() { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/pages/SignInPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Button, Divider, Form, Grid, Segment} from 'semantic-ui-react'; 3 | 4 | export default function SignInModal() { 5 | 6 | 7 | return ( 8 |
9 | 10 | 11 | 12 |
13 | 19 | 25 | 26 |
38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /src/pages/SystemUsers/JobConfirmationPanel.jsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react'; 2 | import JobService from '../../services/JobService'; 3 | import ActivationPanelService from '../../services/ActivationPanelService'; 4 | import { 5 | Button, 6 | Checkbox, 7 | Segment, 8 | Table, 9 | Header, 10 | Icon, 11 | Input, 12 | } from 'semantic-ui-react'; 13 | import {useParams} from 'react-router-dom'; 14 | 15 | export default function JobConfirmationPanel() { 16 | 17 | let jobService = new JobService(); 18 | let activationPanelService = new ActivationPanelService(); 19 | let {systemUserId} = useParams(); 20 | 21 | const [unapprovedJobs, setUnapprovedJobs] = useState([]); 22 | useEffect(() => { 23 | let jobService = new JobService(); 24 | jobService 25 | .getUnapprovedJobs() 26 | .then((result) => { 27 | setUnapprovedJobs(result.data.data); 28 | }) 29 | .catch(); 30 | }, []); 31 | 32 | const [approvedJobs, setApprovedJobs] = useState([]); 33 | useEffect(() => { 34 | let jobService = new JobService(); 35 | jobService 36 | .getApprovedJobs() 37 | .then((result) => { 38 | setApprovedJobs(result.data.data); 39 | }) 40 | .catch(); 41 | }, []); 42 | 43 | function handleOnClickApprove() { 44 | unapprovedJobs.forEach((job) => { 45 | if (document.getElementById(job.id.toString() + 'checkbox').checked) { 46 | let panelForJob = {}; 47 | panelForJob.jobId = job.id; 48 | panelForJob.detail = document.getElementById('detailInput').value; 49 | panelForJob.systemUserId = systemUserId; 50 | activationPanelService.setApproved(panelForJob); 51 | } 52 | }); 53 | window.location.reload(); 54 | } 55 | function handleOnClickApproveAll() { 56 | unapprovedJobs.forEach((job) => { 57 | let panelForJob = {}; 58 | panelForJob.jobId = job.id; 59 | panelForJob.detail = document.getElementById('detailInput').value; 60 | panelForJob.systemUserId = systemUserId; 61 | activationPanelService.setApproved(panelForJob); 62 | }); 63 | window.location.reload(); 64 | } 65 | function handleOnClickDelete() { 66 | unapprovedJobs.forEach((job) => { 67 | if (document.getElementById(job.id.toString() + 'checkbox').checked) { 68 | jobService.deleteJob(job.id); 69 | } 70 | }); 71 | window.location.reload(); 72 | } 73 | function handleOnClickUnapprove() { 74 | approvedJobs.forEach((job) => { 75 | if (document.getElementById(job.id.toString() + 'checkbox').checked) { 76 | let panelForJob = {}; 77 | panelForJob.jobId = job.id; 78 | panelForJob.detail = document.getElementById('detailInput').value; 79 | panelForJob.systemUserId = systemUserId; 80 | activationPanelService.setUnapproved(panelForJob); 81 | } 82 | }); 83 | window.location.reload(); 84 | } 85 | 86 | 87 | return ( 88 |
89 | 90 | 91 |
92 | Jobs Pending Approval 93 |
94 |
95 | 96 | 97 | 98 | 99 | 100 | 101 | Company Name 102 | Job Position 103 | City 104 | Salary 105 | Work Place 106 | Work Time 107 | DeadLine 108 | Description 109 | 110 | 111 | 112 | 113 | {unapprovedJobs.map((job) => ( 114 | 115 | 116 | 117 | 118 | 119 | 120 |
{job.companyName}
121 |
122 | {job.positionName} 123 | {job.cityName} 124 | 125 | {job.minimumSalary + '-' + job.maximumSalary} 126 | 127 | {job.workPlaceName} 128 | {job.workTimeName} 129 | {job.deadLine} 130 | {job.description} 131 |
132 | ))} 133 |
134 | 135 | 136 | 137 | 138 | 139 | 148 | 154 | 160 | 167 | 168 | 169 | 170 |
171 |
172 |
173 |
174 | 175 | 176 | 177 |
178 | Approved Jobs 179 |
180 |
181 | 182 | 183 | 184 | 185 | 186 | 187 | Company Name 188 | Job Position 189 | City 190 | Salary 191 | Work Place 192 | Work Time 193 | DeadLine 194 | Description 195 | 196 | 197 | 198 | 199 | {approvedJobs.map((job) => ( 200 | 201 | 202 | 203 | 204 | 205 | 206 |
{job.companyName}
207 |
208 | {job.positionName} 209 | {job.cityName} 210 | 211 | {job.minimumSalary + '-' + job.maximumSalary} 212 | 213 | {job.workPlaceName} 214 | {job.workTimeName} 215 | {job.deadLine} 216 | {job.description} 217 |
218 | ))} 219 |
220 | 221 | 222 | 223 | 224 | 225 | 234 | 241 | 248 | 249 | 250 | 251 |
252 |
253 |
254 |
255 |
256 | ); 257 | } 258 | -------------------------------------------------------------------------------- /src/pages/SystemUsers/SystemUserSignUpPage.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Header, Button} from 'semantic-ui-react'; 3 | import {Formik, Form, useField} from 'formik'; 4 | import * as Yup from 'yup'; 5 | import {useHistory} from 'react-router-dom'; 6 | 7 | export default function SystemUserSignUpPage() { 8 | const history = useHistory(); 9 | 10 | const TextField = ({label, ...props}) => { 11 | // useField() returns [formik.getFieldProps(), formik.getFieldMeta()] 12 | // which we can spread on . We can use field meta to show an error 13 | // message if the field is invalid and it has been touched (i.e. visited) 14 | const [field, meta] = useField(props); 15 | return ( 16 |
17 |
18 | 23 | 24 | 25 |
26 |
27 | {meta.touched && meta.error ? ( 28 |
29 |
{meta.error}
30 |
31 | ) : null} 32 |
33 | ); 34 | }; 35 | 36 | return ( 37 |
38 |
39 | System User 40 |
41 | 42 | { 50 | 51 | history.push('/systemuser/' + values.id); 52 | }}> 53 |
54 | 61 |
62 | 63 | 66 | 67 |
68 |
69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/services/ActivationPanelService.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export default class ActivationPanelService { 4 | setApproved(panelForJob) { 5 | return axios.post("http://localhost:8080/api/activationpanel/setapproved",panelForJob); 6 | } 7 | setUnapproved(panelForJob) { 8 | return axios.post("http://localhost:8080/api/activationpanel/setunapproved",panelForJob); 9 | } 10 | } -------------------------------------------------------------------------------- /src/services/CityService.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export default class CityService { 4 | getCities() { 5 | return axios.get("http://localhost:8080/api/cities/getAll"); 6 | } 7 | } -------------------------------------------------------------------------------- /src/services/EmployerService.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export default class EmployerService { 4 | getEmployers() { 5 | return axios.get("http://localhost:8080/api/employers/getall"); 6 | } 7 | 8 | getEmployerById(id) { 9 | return axios.get("http://localhost:8080/api/employers/getbyid?id="+id) 10 | } 11 | } -------------------------------------------------------------------------------- /src/services/JobPositionService.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export default class JobPositionService { 4 | getJobPositions() { 5 | return axios.get('http://localhost:8080/api/jobpositions/getall'); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/services/JobSeekerService.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export default class JobSeekersService { 4 | getJobSeekers() { 5 | return axios.get("http://localhost:8080/api/jobseekers/getall"); 6 | } 7 | 8 | getJobSeekerById(id){ 9 | 10 | return axios.get("http://localhost:8080/api/jobseekers/getbyid?id="+id) 11 | } 12 | } -------------------------------------------------------------------------------- /src/services/JobService.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export default class JobService { 4 | getJobs() { 5 | return axios.get("http://localhost:8080/api/jobs/getall"); 6 | } 7 | addJob(job) { 8 | return axios.post("http://localhost:8080/api/jobs/add",job) 9 | } 10 | 11 | deleteJob(id) { 12 | return axios.post("http://localhost:8080/api/jobs/delete?id="+id) 13 | } 14 | 15 | getApprovedJobs() { 16 | return axios.get("http://localhost:8080/api/jobs/getallapprovedjobs"); 17 | } 18 | 19 | getUnapprovedJobs() { 20 | return axios.get("http://localhost:8080/api/jobs/getallunapprovedjobs"); 21 | } 22 | 23 | setActive(id){ 24 | return axios.post("http://localhost:8080/api/jobs/setactivetojobvisibility?id="+id) 25 | } 26 | 27 | getJobById(id){ 28 | return axios.get("http://localhost:8080/api/jobs/getjobbyid?id="+id); 29 | } 30 | } -------------------------------------------------------------------------------- /src/services/WorkPlaceService.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export default class WorkPlaceService { 4 | getWorkPlaces() { 5 | return axios.get("http://localhost:8080/api/workplaces/getall"); 6 | } 7 | } -------------------------------------------------------------------------------- /src/services/WorkTimeService.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export default class WorkTimeService { 4 | getWorkTimes() { 5 | return axios.get("http://localhost:8080/api/worktimes/getall"); 6 | } 7 | } -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/store/configureStore.js: -------------------------------------------------------------------------------- 1 | import { createStore } from "redux"; 2 | import { devToolsEnhancer } from "redux-devtools-extension"; 3 | import rootReducer from './rootReducer' 4 | 5 | export function configureStore(){ 6 | return createStore(rootReducer,devToolsEnhancer()) 7 | } -------------------------------------------------------------------------------- /src/store/rootReducer.js: -------------------------------------------------------------------------------- 1 | 2 | import { combineReducers } from "redux"; 3 | 4 | 5 | const rootReducer=combineReducers({ 6 | 7 | }) 8 | 9 | export default rootReducer; --------------------------------------------------------------------------------