Преглед на файлове

applicant details page

pull/38/head
Dzenis Hadzifejzovic преди 3 години
родител
ревизия
2bc126e595
променени са 52 файла, в които са добавени 2298 реда и са изтрити 450 реда
  1. 184
    59
      package-lock.json
  2. 2
    0
      package.json
  3. 7
    0
      src/AppRoutes.js
  4. Двоични данни
      src/assets/images/addUser.png
  5. Двоични данни
      src/assets/images/facebook.png
  6. Двоични данни
      src/assets/images/instagram.png
  7. Двоични данни
      src/assets/images/linkedin.png
  8. 4
    0
      src/assets/styles/_layout.scss
  9. 308
    16
      src/assets/styles/components/_ads.scss
  10. 5
    2
      src/assets/styles/components/_icon-button.scss
  11. 9
    0
      src/assets/styles/components/_rules.scss
  12. 43
    37
      src/assets/styles/components/_usersPage.scss
  13. 54
    10
      src/components/Ads/Ad.js
  14. 48
    0
      src/components/Ads/AdDetailsCandidateCard.js
  15. 30
    5
      src/components/Ads/ArchiveAd.js
  16. 34
    0
      src/components/Button/FilterButton.js
  17. 1
    1
      src/components/MUI/DialogComponent copy.js
  18. 79
    0
      src/components/MUI/InviteDialog.js
  19. 2
    2
      src/components/MUI/NavbarComponent.js
  20. 13
    6
      src/components/Section/MainContainer.js
  21. 2
    0
      src/constants/pages.js
  22. 89
    79
      src/i18n/resources/en.js
  23. 90
    80
      src/i18n/resources/rs.js
  24. 222
    0
      src/pages/AdsPage/AdDetailsPage.js
  25. 202
    45
      src/pages/AdsPage/AdsPage.js
  26. 196
    0
      src/pages/UsersPage/UserDetails.js
  27. 112
    19
      src/pages/UsersPage/UsersPage.js
  28. 7
    0
      src/request/adsRequest.js
  29. 13
    0
      src/request/apiEndpoints.js
  30. 15
    1
      src/request/usersRequest.js
  31. 3
    0
      src/store/actions/ad/adActionConstants.js
  32. 20
    0
      src/store/actions/ad/adActions.js
  33. 19
    0
      src/store/actions/ads/adsAction.js
  34. 3
    0
      src/store/actions/ads/adsActionConstants.js
  35. 3
    0
      src/store/actions/archiveAds/archiveAdsActionConstants.js
  36. 19
    0
      src/store/actions/archiveAds/archiveAdsActions.js
  37. 1
    1
      src/store/actions/login/loginActionConstants.js
  38. 9
    1
      src/store/actions/users/usersActionConstants.js
  39. 54
    19
      src/store/actions/users/usersActions.js
  40. 26
    0
      src/store/reducers/ad/adReducer.js
  41. 32
    0
      src/store/reducers/ad/adsReducer.js
  42. 32
    0
      src/store/reducers/ad/archiveAdsReducer.js
  43. 21
    1
      src/store/reducers/index.js
  44. 41
    2
      src/store/reducers/user/usersReducer.js
  45. 50
    0
      src/store/saga/adsSaga.js
  46. 5
    3
      src/store/saga/index.js
  47. 6
    2
      src/store/saga/loginSaga.js
  48. 59
    6
      src/store/saga/usersSaga.js
  49. 10
    0
      src/store/selectors/adSelectors.js
  50. 10
    0
      src/store/selectors/adsSelectors.js
  51. 13
    0
      src/store/selectors/archiveAdsSelectors.js
  52. 91
    53
      yarn.lock

+ 184
- 59
package-lock.json Целия файл

@@ -38,9 +38,11 @@
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"react-select": "^4.3.1",
"react-slick": "^0.29.0",
"redux": "^4.1.0",
"redux-saga": "^1.1.3",
"sass": "^1.34.1",
"slick-carousel": "^1.8.1",
"web-vitals": "^1.1.2",
"yup": "^0.32.9"
},
@@ -3487,6 +3489,7 @@
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz",
"integrity": "sha512-6YWYPPpxG3e/xOo6HIWwB/58HukkwIVTOaZ0VwdMVjhRUX/01E4FtQbck9GazOOj7MXHc5RBzMrU86iBJHbI+A==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
@@ -3505,6 +3508,7 @@
"version": "7.14.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz",
"integrity": "sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.13.4"
}
@@ -3513,6 +3517,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -3527,6 +3532,7 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.1.tgz",
"integrity": "sha512-4cPQjOYM2mqq7mZG8CSxkUvL2Yv/x29VhGq5LKehTsxRnoVQps1YGt9NyjcNQsznEsD4rr8a6zGxqeNTqJWjpA==",
"dev": true,
"dependencies": {
"deep-equal": "^2.0.5"
}
@@ -3535,6 +3541,7 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
"dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -3550,6 +3557,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -3560,12 +3568,14 @@
"node_modules/@testing-library/dom/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"node_modules/@testing-library/dom/node_modules/deep-equal": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz",
"integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.0",
"es-get-iterator": "^1.1.1",
@@ -3591,6 +3601,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"engines": {
"node": ">=8"
}
@@ -3598,12 +3609,14 @@
"node_modules/@testing-library/dom/node_modules/isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true
},
"node_modules/@testing-library/dom/node_modules/pretty-format": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -3617,6 +3630,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"engines": {
"node": ">=10"
},
@@ -3628,6 +3642,7 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -3805,7 +3820,8 @@
"node_modules/@types/aria-query": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.1.tgz",
"integrity": "sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg=="
"integrity": "sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg==",
"dev": true
},
"node_modules/@types/babel__core": {
"version": "7.1.14",
@@ -4963,6 +4979,7 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -6428,6 +6445,11 @@
"node": ">=0.10.0"
}
},
"node_modules/classnames": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
},
"node_modules/clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
@@ -7911,7 +7933,8 @@
"node_modules/dom-accessibility-api": {
"version": "0.5.14",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz",
"integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg=="
"integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==",
"dev": true
},
"node_modules/dom-converter": {
"version": "0.2.0",
@@ -8217,6 +8240,11 @@
"safe-buffer": "~5.1.0"
}
},
"node_modules/enquire.js": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
"integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw=="
},
"node_modules/enquirer": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
@@ -8316,6 +8344,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz",
"integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.1.0",
@@ -8333,7 +8362,8 @@
"node_modules/es-get-iterator/node_modules/isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true
},
"node_modules/es-to-primitive": {
"version": "1.2.1",
@@ -10094,6 +10124,7 @@
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
"dev": true,
"dependencies": {
"is-callable": "^1.1.3"
}
@@ -11874,6 +11905,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
"integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -12031,6 +12063,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
"integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -12089,6 +12122,7 @@
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz",
"integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==",
"dev": true,
"dependencies": {
"available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2",
@@ -12112,6 +12146,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
"integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
"dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -12131,6 +12166,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz",
"integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.1.1"
@@ -14501,6 +14537,14 @@
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
},
"node_modules/json2mq": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
"integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
"dependencies": {
"string-convert": "^0.2.0"
}
},
"node_modules/json3": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz",
@@ -14952,6 +14996,7 @@
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
"integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=",
"dev": true,
"bin": {
"lz-string": "bin/bin.js"
}
@@ -17853,6 +17898,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz",
"integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
@@ -18811,6 +18857,22 @@
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-slick": {
"version": "0.29.0",
"resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.29.0.tgz",
"integrity": "sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA==",
"dependencies": {
"classnames": "^2.2.5",
"enquire.js": "^2.1.6",
"json2mq": "^0.2.0",
"lodash.debounce": "^4.0.8",
"resize-observer-polyfill": "^1.5.0"
},
"peerDependencies": {
"react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-test-renderer": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz",
@@ -19257,6 +19319,11 @@
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.6.tgz",
"integrity": "sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ=="
},
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"node_modules/resolve": {
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
@@ -20374,6 +20441,14 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/slick-carousel": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz",
"integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==",
"peerDependencies": {
"jquery": ">=1.8.0"
}
},
"node_modules/snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -20990,6 +21065,11 @@
}
]
},
"node_modules/string-convert": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
"integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A=="
},
"node_modules/string-length": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
@@ -21930,19 +22010,6 @@
"is-typedarray": "^1.0.0"
}
},
"node_modules/typescript": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/typescript-compare": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz",
@@ -23975,6 +24042,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
"integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
"dev": true,
"dependencies": {
"is-map": "^2.0.1",
"is-set": "^2.0.1",
@@ -23994,6 +24062,7 @@
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz",
"integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==",
"dev": true,
"dependencies": {
"available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2",
@@ -25823,8 +25892,7 @@
"@emotion/use-insertion-effect-with-fallbacks": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz",
"integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==",
"requires": {}
"integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A=="
},
"@emotion/utils": {
"version": "1.2.0",
@@ -26615,8 +26683,7 @@
"@mui/types": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.0.tgz",
"integrity": "sha512-lGXtFKe5lp3UxTBGqKI1l7G8sE2xBik8qCfrLHD5olwP/YU0/ReWoWT7Lp1//ri32dK39oPMrJN8TgbkCSbsNA==",
"requires": {}
"integrity": "sha512-lGXtFKe5lp3UxTBGqKI1l7G8sE2xBik8qCfrLHD5olwP/YU0/ReWoWT7Lp1//ri32dK39oPMrJN8TgbkCSbsNA=="
},
"@mui/utils": {
"version": "5.10.9",
@@ -26985,6 +27052,7 @@
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz",
"integrity": "sha512-6YWYPPpxG3e/xOo6HIWwB/58HukkwIVTOaZ0VwdMVjhRUX/01E4FtQbck9GazOOj7MXHc5RBzMrU86iBJHbI+A==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
@@ -27000,6 +27068,7 @@
"version": "7.14.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz",
"integrity": "sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.13.4"
}
@@ -27008,6 +27077,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"color-convert": "^2.0.1"
}
@@ -27016,6 +27086,7 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.1.tgz",
"integrity": "sha512-4cPQjOYM2mqq7mZG8CSxkUvL2Yv/x29VhGq5LKehTsxRnoVQps1YGt9NyjcNQsznEsD4rr8a6zGxqeNTqJWjpA==",
"dev": true,
"requires": {
"deep-equal": "^2.0.5"
}
@@ -27024,6 +27095,7 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -27033,6 +27105,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
@@ -27040,12 +27113,14 @@
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"deep-equal": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz",
"integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==",
"dev": true,
"requires": {
"call-bind": "^1.0.0",
"es-get-iterator": "^1.1.1",
@@ -27067,17 +27142,20 @@
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true
},
"pretty-format": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"requires": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -27087,7 +27165,8 @@
"ansi-styles": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true
}
}
},
@@ -27095,6 +27174,7 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
@@ -27233,7 +27313,8 @@
"@types/aria-query": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.1.tgz",
"integrity": "sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg=="
"integrity": "sha512-S6oPal772qJZHoRZLFc/XoZW2gFvwXusYUmXPXkgxJLuEk2vOt7jc4Yo6z/vtI0EBkbPBVrJJ0B+prLIKiWqHg==",
"dev": true
},
"@types/babel__core": {
"version": "7.1.14",
@@ -27851,8 +27932,7 @@
"acorn-jsx": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"requires": {}
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="
},
"acorn-walk": {
"version": "7.2.0",
@@ -27904,14 +27984,12 @@
"ajv-errors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
"integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
"requires": {}
"integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ=="
},
"ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"requires": {}
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="
},
"alphanum-sort": {
"version": "1.0.2",
@@ -28185,7 +28263,8 @@
"available-typed-arrays": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw=="
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
"dev": true
},
"axe-core": {
"version": "4.2.2",
@@ -28383,8 +28462,7 @@
"babel-plugin-named-asset-import": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.7.tgz",
"integrity": "sha512-squySRkf+6JGnvjoUtDEjSREJEBirnXi9NqP6rjSYsylxQxqBTz+pkmf395i9E2zsvmYUaI40BHo6SqZUdydlw==",
"requires": {}
"integrity": "sha512-squySRkf+6JGnvjoUtDEjSREJEBirnXi9NqP6rjSYsylxQxqBTz+pkmf395i9E2zsvmYUaI40BHo6SqZUdydlw=="
},
"babel-plugin-polyfill-corejs2": {
"version": "0.2.2",
@@ -29349,6 +29427,11 @@
}
}
},
"classnames": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
},
"clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
@@ -30519,7 +30602,8 @@
"dom-accessibility-api": {
"version": "0.5.14",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz",
"integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg=="
"integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==",
"dev": true
},
"dom-converter": {
"version": "0.2.0",
@@ -30790,6 +30874,11 @@
}
}
},
"enquire.js": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
"integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw=="
},
"enquirer": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
@@ -30871,6 +30960,7 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz",
"integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.1.0",
@@ -30885,7 +30975,8 @@
"isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true
}
}
},
@@ -31167,8 +31258,7 @@
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz",
"integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==",
"dev": true,
"requires": {}
"dev": true
},
"eslint-config-react-app": {
"version": "6.0.0",
@@ -31402,8 +31492,7 @@
"eslint-plugin-react-hooks": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz",
"integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==",
"requires": {}
"integrity": "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ=="
},
"eslint-plugin-security": {
"version": "1.4.0",
@@ -32197,6 +32286,7 @@
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
"dev": true,
"requires": {
"is-callable": "^1.1.3"
}
@@ -33576,7 +33666,8 @@
"is-map": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
"integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg=="
"integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
"dev": true
},
"is-module": {
"version": "1.0.0",
@@ -33679,7 +33770,8 @@
"is-set": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
"integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g=="
"integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
"dev": true
},
"is-shared-array-buffer": {
"version": "1.0.2",
@@ -33714,6 +33806,7 @@
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz",
"integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==",
"dev": true,
"requires": {
"available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2",
@@ -33730,7 +33823,8 @@
"is-weakmap": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
"integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA=="
"integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
"dev": true
},
"is-weakref": {
"version": "1.0.2",
@@ -33744,6 +33838,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz",
"integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==",
"dev": true,
"requires": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.1.1"
@@ -34533,8 +34628,7 @@
"jest-pnp-resolver": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
"integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==",
"requires": {}
"integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w=="
},
"jest-regex-util": {
"version": "26.0.0",
@@ -35497,6 +35591,14 @@
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
},
"json2mq": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
"integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==",
"requires": {
"string-convert": "^0.2.0"
}
},
"json3": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz",
@@ -35871,7 +35973,8 @@
"lz-string": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz",
"integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY="
"integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=",
"dev": true
},
"magic-string": {
"version": "0.25.7",
@@ -38202,7 +38305,8 @@
"prettier": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz",
"integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA=="
"integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==",
"dev": true
},
"prettier-linter-helpers": {
"version": "1.0.0",
@@ -38961,6 +39065,18 @@
"react-is": "^16.12.0 || ^17.0.0 || ^18.0.0"
}
},
"react-slick": {
"version": "0.29.0",
"resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.29.0.tgz",
"integrity": "sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA==",
"requires": {
"classnames": "^2.2.5",
"enquire.js": "^2.1.6",
"json2mq": "^0.2.0",
"lodash.debounce": "^4.0.8",
"resize-observer-polyfill": "^1.5.0"
}
},
"react-test-renderer": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz",
@@ -39133,8 +39249,7 @@
"redux-thunk": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
"integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
"requires": {}
"integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q=="
},
"regenerate": {
"version": "1.4.2",
@@ -39312,6 +39427,11 @@
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.6.tgz",
"integrity": "sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ=="
},
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"resolve": {
"version": "1.18.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
@@ -40197,6 +40317,11 @@
}
}
},
"slick-carousel": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz",
"integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA=="
},
"snapdragon": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -40723,6 +40848,11 @@
}
}
},
"string-convert": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
"integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A=="
},
"string-length": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
@@ -41441,12 +41571,6 @@
"is-typedarray": "^1.0.0"
}
},
"typescript": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
"peer": true
},
"typescript-compare": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz",
@@ -43068,6 +43192,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
"integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
"dev": true,
"requires": {
"is-map": "^2.0.1",
"is-set": "^2.0.1",
@@ -43084,6 +43209,7 @@
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz",
"integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==",
"dev": true,
"requires": {
"available-typed-arrays": "^1.0.5",
"call-bind": "^1.0.2",
@@ -43371,8 +43497,7 @@
"ws": {
"version": "7.4.6",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
"requires": {}
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A=="
},
"xdg-basedir": {
"version": "4.0.0",

+ 2
- 0
package.json Целия файл

@@ -33,9 +33,11 @@
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"react-select": "^4.3.1",
"react-slick": "^0.29.0",
"redux": "^4.1.0",
"redux-saga": "^1.1.3",
"sass": "^1.34.1",
"slick-carousel": "^1.8.1",
"web-vitals": "^1.1.2",
"yup": "^0.32.9"
},

+ 7
- 0
src/AppRoutes.js Целия файл

@@ -4,6 +4,7 @@ import { Redirect, Route, Switch } from "react-router-dom";
import {
HOME_PAGE,
ADS_PAGE,
AD_DETAILS_PAGE,
FORGOT_PASSWORD_PAGE,
FORGOT_PASSWORD_CONFIRMATION_PAGE,
NOT_FOUND_PAGE,
@@ -12,6 +13,8 @@ import {
RESET_PASSWORD_PAGE,
USERS_PAGE,
CANDIDATES_PAGE,
USER_DETAILS_PAGE
CANDIDATES_PAGE,
CANDIDATES_DETAILS_PAGE
} from "./constants/pages";

@@ -29,6 +32,8 @@ import ForgotPasswordConfirmationPage from "./pages/ForgotPasswordPage/ForgotPas
import ResetPasswordPage from "./pages/ForgotPasswordPage/ResetPasswordPageMUI";
import UsersPage from "./pages/UsersPage/UsersPage";
import CandidatesPage from './pages/CandidatesPage/CandidatesPage'
import AdDetailsPage from "./pages/AdsPage/AdDetailsPage";
import UserDetails from "./pages/UsersPage/UserDetails";
import CandidateDetailsPage from "./pages/CandidateDetailsPage/CandidateDetailsPage";

const AppRoutes = () => (
@@ -45,6 +50,8 @@ const AppRoutes = () => (
<Route path={RESET_PASSWORD_PAGE} component={ResetPasswordPage} />
<PrivateRoute exact path={HOME_PAGE} component={HomePage} />
<PrivateRoute exact path={ADS_PAGE} component={AdsPage} />
<PrivateRoute exact path={AD_DETAILS_PAGE} component={AdDetailsPage} />
<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} />

Двоични данни
src/assets/images/addUser.png Целия файл


Двоични данни
src/assets/images/facebook.png Целия файл


Двоични данни
src/assets/images/instagram.png Целия файл


Двоични данни
src/assets/images/linkedin.png Целия файл


+ 4
- 0
src/assets/styles/_layout.scss Целия файл

@@ -29,3 +29,7 @@
.ml-20px {
margin-left: 30px;
}
.divider{
display: flex;
justify-content: space-between;
}

+ 308
- 16
src/assets/styles/components/_ads.scss Целия файл

@@ -7,6 +7,9 @@ h3 {
.ads {
margin-top: 36px;
padding-left: 72px;
@include media-below($bp-xl) {
padding-left: 36px !important;
}
}

.active-ads-header {
@@ -15,6 +18,9 @@ h3 {
justify-content: space-between;
align-items: center;
padding-right: 5rem;
@include media-below($bp-xl) {
padding: 0 0.75rem !important;
}
}

.filter-vector {
@@ -25,6 +31,7 @@ h3 {
display: flex;
margin-top: 39px;
position: relative;
width: 100%;
}

.active-ads-ads-a {
@@ -38,6 +45,12 @@ h3 {
flex-direction: column;
align-items: center;
justify-content: center;
@include media-below($bp-xl) {
flex-direction: row;
gap: 10px;
justify-content: flex-start;
padding-left: 0.75rem !important;
}
}

.active-ads-ads-arrows button {
@@ -54,36 +67,50 @@ h3 {
.active-ads-ads-ad {
padding-left: 81px;
display: flex;
flex-direction: row;
width: 100%;
@include media-below($bp-xl) {
padding-left: 0;
}
}

.archived-ads {
.archived-ads,
.ad-details-applicants {
margin-top: 56px;
}

.archived-ads-header {
.archived-ads-header,
.ad-details-applicants-header {
padding-left: 81px;
@include media-below($bp-xl) {
padding: 0 0.75rem;
}
}

.archived-ads-ads {
.archived-ads-ads,
.ad-details-applicants-applicants {
display: flex;
margin-top: 27px;
position: relative;
}

.archived-ads-ads-a {
.archived-ads-ads-a,
.ad-details-applicants-applicants-a {
position: absolute;
top: 50%;
transform: translate(0, -50%);
}

.archived-ads-ads-arrows {
.archived-ads-ads-arrows,
.ad-details-applicants-applicants-arrows {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.archived-ads-ads-arrows button {
.archived-ads-ads-arrows button,
.ad-details-applicants-applicants-arrows button {
margin: 9px 0;
box-sizing: border-box;
width: 45px;
@@ -94,9 +121,15 @@ h3 {
cursor: pointer;
}

.archived-ads-ads-ad {
.archived-ads-ads-ad,
.ad-details-applicants-applicants-applicant {
padding-left: 81px;
display: flex;
flex-direction: row;
width: 100%;
@include media-below($bp-xl) {
padding: 0;
}
}

.archive-ad {
@@ -107,14 +140,42 @@ h3 {
align-items: center;
padding: 36px;
gap: 18px;
width: 247px;
height: 215px;
left: 1px;
left: 0px;
top: 0px;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 12px;
margin-right: 27px;
transition: 0.3s;
cursor: pointer;
}

.ad-details-candidate {
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 36px;
left: 0px;
top: 0px;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 12px;
margin-right: 27px;
transition: 0.3s;
cursor: pointer;
}

.ad-details-candidate {
padding: 54px 72px !important;
}

.archive-ad:hover,
.ad-details-candidate:hover {
scale: 1.05;
border-color: $mainBlue !important;
background-color: $mainBlueLight !important;
}

.archive-ad-date p {
@@ -129,7 +190,21 @@ h3 {
flex-grow: 0;
}

.archive-ad-title h3 {
.ad-details-candidate-date p {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 15px;
color: #272727;
margin-bottom: 18px !important;
flex: none;
order: 0;
flex-grow: 0;
}

.archive-ad-title h3,
.ad-details-candidate-title h3 {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 600;
@@ -149,7 +224,12 @@ h3 {
flex-grow: 0;
}

.archive-ad-experience p {
.ad-details-candidate-experience {
margin-bottom: 9px;
}

.archive-ad-experience p,
.ad-details-candidate-experience p {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
@@ -161,19 +241,61 @@ h3 {
flex-grow: 0;
}

.slick-list {
padding: 0.75rem !important;
overflow-y: visible !important;
@include media-below($bp-xl) {
}
}

.slick-dots {
display: none !important;
}

.slick-arrow {
display: none !important;
}

.ad-card {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 425px;
height: 370px;
padding: 72px;
background: #ffffff;
border: 1px solid #e4e4e4;
border-radius: 18px;
gap: 18px;
margin-right: 36px;
cursor: pointer;
transition: 0.3s;
@include media-below($bp-xl) {
margin-right: 20px !important;
padding: 36px !important;
}
}

.ad-card:hover {
scale: 1.05;
border-color: $mainBlue !important;
background-color: $mainBlueLight !important;
}

.ad-card-buttons-button{
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 9px !important;
gap: 10px !important;
width: 36px !important;
height: 36px !important;
border: 1px solid #e4e4e4 !important;
background-color: white;
border-radius: 9px !important;
flex: none;
order: 0;
flex-grow: 0;
}

.ad-card-date p {
@@ -222,17 +344,46 @@ h3 {
}

.ad-card-buttons {
overflow: hidden;
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: center;
padding: 0px;
gap: 18px;
width: 281px;
height: 38px;
flex: none;
order: 4;
flex-grow: 0;
@include media-below($bp-xl) {
gap: 9px !important;
}
}

.ad-details-candidate-buttons {
display: flex;
align-items: center;
justify-content: space-between !important;
margin-bottom: 9px;
width: 205px !important;
flex-wrap: wrap;
}

.ad-details-candidate-cv a {
color: $mainBlue;
}

.ad-details-candidate-buttons button {
box-sizing: border-box;
margin: 0 4.5px;
padding: 9px;
gap: 10px;
border: 1px solid #e4e4e4;
border-radius: 9px;
flex: none;
order: 0;
flex-grow: 0;
background-color: white;
margin-bottom: 9px;
}

.ad-card-buttons button {
@@ -252,6 +403,10 @@ h3 {
flex-grow: 0;
}

.ad-details-candidate-technologies button {
background-color: white !important;
}

.add-ad {
margin-top: 49px;
display: flex;
@@ -259,6 +414,9 @@ h3 {
align-items: center;
padding-right: 5rem !important;
padding-bottom: 49px;
@include media-below($bp-xl) {
padding-right: 18px !important;
}
}

.add-ad-btn {
@@ -272,6 +430,9 @@ h3 {
height: 51px;
background: #226cb0;
border-radius: 9px;
@include media-below($bp-xl) {
width: 147px;
}
}

.ad-filters-header-container {
@@ -339,4 +500,135 @@ h3 {

.ad-filters-search > * {
width: 100%;
}

.ad-details {
padding: 45px 72px 18px 223px;
@include media-below($bp-xl) {
padding: 18px 36px !important;
}
}

.ad-details-tech-logo {
position: relative;
left: -80px;
display: flex;
align-items: center;
justify-content: space-between;
@include media-below($bp-xl) {
left: 0;
}
}

.ad-details-applicants {
position: relative;
left: -80px !important;
@include media-below($bp-xl) {
left: 0 !important;
}
}

.ad-details-tech-logo-title {
display: flex;
align-items: center;
}

.ad-details-tech-logo-title-img {
margin-right: 18px;
}

.ad-details-tech-logo-title-sub sub {
margin-left: 9px;
font-size: 1.25rem;
color: $mainBlue !important;
font-weight: 600;
}

.ad-details-tech-logo-date {
@include media-below($bp-xl) {
margin-bottom: 18px;
}
}

.ad-details-tech-logo-date p span {
color: #9d9d9d;
}

.ad-details-content-experience {
margin-top: 18px;
}

.ad-details-content-experience p {
color: #272727;
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 20px;
}

.ad-details-content-work-time {
margin-top: 18px;
}

.ad-details-content-work-time button {
box-sizing: border-box;
flex-direction: row;
padding: 9px;
gap: 10px;
width: 76px;
height: 38px;
border: 1px solid #e4e4e4;
border-radius: 9px;
flex: none;
order: 0;
flex-grow: 0;
margin-right: 18px;
cursor: pointer;
}

.ad-details-content-content {
margin-top: 18px;
}

.ad-details-content-conten-description {
margin-top: 18px;
}

.ad-details-content-conten-description h3 {
margin-bottom: 9px;
}

.ad-details-content-conten-description ul {
list-style: circle;
padding-left: 36px;
}

.ad-details-buttons {
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 36px;
@include media-below($bp-xl) {
justify-content: center;
}
}

.ad-details-buttons > button {
margin-left: 36px;
}

.ad-details-buttons-link {
color: $mainBlue;
}

.ad-details-candidate-technologies {
display: flex;
justify-content: space-between;
max-width: 208px;
flex-wrap: wrap;
}

.hiddenAd {
visibility: hidden !important;
}

+ 5
- 2
src/assets/styles/components/_icon-button.scss Целия файл

@@ -10,6 +10,9 @@
height: 34px !important;
min-width: none !important;
}
.td-btn:hover{
background-color: white !important;
.td-btn.active{
background-color: rgba(100,255,100,0.75) !important;
}
.td-btn.inactive{
background-color: rgba(255,100,100,0.75) !important;
}

+ 9
- 0
src/assets/styles/components/_rules.scss Целия файл

@@ -6,4 +6,13 @@
}
.hr-mid{
padding: 2.5px 7.5px;
}
.vr{
height: 20px;
position: relative;
top: 2.5px;
margin-left: 5px;
margin-right: 5px;
width: 2px;
background: $mainBlue;
}

+ 43
- 37
src/assets/styles/components/_usersPage.scss Целия файл

@@ -1,66 +1,72 @@
.f-1{
flex: 1;
flex: 1;
}
.usersTable {
border-radius: 12px;
overflow: hidden;
background-color: white;
@include media-below($bp-xl) {
overflow-x: auto !important;
}
border-radius: 12px;
overflow: hidden;
background-color: white;
@include media-below($bp-xl) {
overflow-x: auto !important;
}
}
.table-cont{
@include media-below($bp-xl) {
overflow-x: auto !important;
}
@include media-below($bp-xl) {
overflow-x: auto !important;
}
}
.usersTable .headingRow {
background-color: $mainBlueLight;
background-color: $mainBlueLight;
}
.usersTable .headingRow th {
font-weight: 600 !important;
font-weight: 600 !important;
}
.usersTable tr {
border: 1px solid #f4f4f4;
border: 1px solid #f4f4f4;
}
.usersTable td,
.usersTable th {
width: 20% !important;
height: 60px;
padding-left: 35px;
width: 20% !important;
height: 60px;
padding-left: 35px;
}
.usersTable > tbody > tr > td:last-of-type{
display: flex !important;
flex-direction: row !important;
// justify-content: space-between;
align-items: center;
gap: 9px;
display: flex !important;
flex-direction: row !important;
// justify-content: space-between;
align-items: center;
gap: 9px;
}
.inviteBtn {
font-size: 12px;
letter-spacing: 1px;
text-transform: uppercase;
padding: 18px 70px !important;
margin-top: 60px;
font-size: 12px;
letter-spacing: 1px;
text-transform: uppercase;
padding: 18px 70px !important;
margin-top: 60px;
}
.secondaryRow:hover {
background-color: $mainBlueLight;
background-color: $mainBlueLight;
}
.cvLink {
text-decoration: underline;
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 14px;
text-decoration: underline;
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
font-size: 14px;
// line-height: 15px;
/* identical to box height */
text-decoration-line: underline;
/* Main Blue */
color: #226cb0;
/* identical to box height */
text-decoration-line: underline;
/* Main Blue */
color: #226cb0;
// display: flex;
// justify-content: center;
// align-items: center;
// text-align: center;
// padding-left: 35px;
}
.dialog-subtitle{
position: relative;
top: 2.5px;
color: $mainBlue;
margin-right: 100px;
}

+ 54
- 10
src/components/Ads/Ad.js Целия файл

@@ -1,15 +1,34 @@
import React from "react";
import PropTypes from "prop-types";
import logoReact from "../../assets/images/logo_react.png";
import { useTheme } from "@emotion/react";
import { useMediaQuery } from "@mui/material";
import linkedin from "../../assets/images/linkedin.png";
import facebook from "../../assets/images/facebook.png";
import instagram from "../../assets/images/instagram.png";

const Ad = ({
title,
minimumExperience,
createdAt,
expiredAt,
onShowAdDetails,
className,
}) => {
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down("sm"));

const Ad = () => {
return (
<div className="ad-card">
<div className={`ad-card ${className}`} onClick={onShowAdDetails}>
<div className="ad-card-date">
<p>30.09.22 - 30.10.22</p>
<p>
{new Date(createdAt).toLocaleDateString()} -{" "}
{new Date(expiredAt).toLocaleDateString()}
</p>
</div>

<div className="ad-card-title">
<h3>React Developer</h3>
<h3>{title}</h3>
</div>

<div className="ad-card-logo">
@@ -17,16 +36,41 @@ const Ad = () => {
</div>

<div className="ad-card-experience">
<p>3+ years of experience</p>
<p>{minimumExperience}+ years of experience</p>
</div>

<div className="ad-card-buttons">
<button>LinkedIn</button>
<button>Facebook</button>
<button disabled>Instagram</button>
</div>
{!matches && (
<div className="ad-card-buttons">
<button>LinkedIn</button>
<button>Facebook</button>
<button disabled>Instagram</button>
</div>
)}
{matches && (
<div className="ad-card-buttons">
<button className="ad-card-buttons-button">
<img src={linkedin} />
</button>
<button className="ad-card-buttons-button">
<img src={facebook} />
</button>
<button disabled className="ad-card-buttons-button">
<img src={instagram} />
</button>
</div>
)}
</div>
);
};

Ad.propTypes = {
id: PropTypes.number,
title: PropTypes.string,
minimumExperience: PropTypes.number,
createdAt: PropTypes.any,
expiredAt: PropTypes.any,
onShowAdDetails: PropTypes.func,
className: PropTypes.any,
};

export default Ad;

+ 48
- 0
src/components/Ads/AdDetailsCandidateCard.js Целия файл

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

const AdDetailsCandidateCard = ({
className,
firstName,
lastName,
experience,
cv,
}) => {
return (
<div className={`ad-details-candidate ${className}`}>
<div className="ad-details-candidate-date">
<p>{new Date().toLocaleDateString()}</p>
</div>
<div className="ad-details-candidate-title">
<h3>
{firstName} {lastName}
</h3>
</div>
<div className="ad-details-candidate-experience">
{experience > 0 ? (
<p>{experience}+ years of experience</p>
) : (
<p>No experience</p>
)}
</div>
<div className="ad-details-candidate-buttons">
<button>React</button>
<button>.NET</button>
<button>Angular</button>
</div>
<div className="ad-details-candidate-cv">
<a href="#">{cv}</a>
</div>
</div>
);
};

AdDetailsCandidateCard.propTypes = {
className: PropTypes.any,
firstName: PropTypes.string,
lastName: PropTypes.string,
experience: PropTypes.number,
cv: PropTypes.string,
};

export default AdDetailsCandidateCard;

+ 30
- 5
src/components/Ads/ArchiveAd.js Целия файл

@@ -1,23 +1,48 @@
import React from "react";
import PropTypes from "prop-types";
import net_icon from "../../assets/images/.net_icon.png";

const ArchiveAd = () => {
const ArchiveAd = ({
className,
title,
minimumExperience,
createdAt,
expiredAt,
onShowAdDetails,
}) => {
return (
<div className="archive-ad">
<div className={`archive-ad ${className}`} onClick={onShowAdDetails}>
<div className="archive-ad-date">
<p>05.07.22 - 20.08.22</p>
<p>
{new Date(createdAt).toLocaleDateString()} -{" "}
{new Date(expiredAt).toLocaleDateString()}
</p>
</div>
<div className="archive-ad-title">
<h3>.NET Intern</h3>
<h3>{title}</h3>
</div>
<div className="archive-ad-image">
<img src={net_icon} alt=".net icon" />
</div>
<div className="archive-ad-experience">
<p>No experience needed</p>
{minimumExperience > 0 ? (
<p>{minimumExperience}+ years of experience</p>
) : (
<p>No experience needed</p>
)}
</div>
</div>
);
};

ArchiveAd.propTypes = {
id: PropTypes.number,
title: PropTypes.string,
minimumExperience: PropTypes.number,
createdAt: PropTypes.any,
expiredAt: PropTypes.any,
onShowAdDetails: PropTypes.func,
className: PropTypes.any,
};

export default ArchiveAd;

+ 34
- 0
src/components/Button/FilterButton.js Целия файл

@@ -0,0 +1,34 @@
import { IconButton } from "@mui/material";
import PropTypes from "prop-types";
import React from "react";
import filters from "../../assets/images/filters.png";
import { useTheme } from "@mui/system";
import { useMediaQuery } from "@mui/material";

const FilterButton = ({ onShowFilters }) => {
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down("sm"));

return (
<IconButton
className={"c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding"}
onClick={onShowFilters}
>
{!matches && "Filteri"}
<img
style={{
position: "relative",
top: -0.25,
paddingLeft: matches ? "0px" : "10px",
}}
src={filters}
/>
</IconButton>
);
};

FilterButton.propTypes = {
onShowFilters: PropTypes.func,
};

export default FilterButton;

src/components/MUI/DialogComponent.js → src/components/MUI/DialogComponent copy.js Целия файл

@@ -45,7 +45,7 @@ const DialogComponent = ({
};

DialogComponent.propTypes = {
title: PropTypes.string,
title: PropTypes.any,
open: PropTypes.bool.isRequired,
content: PropTypes.any,
onClose: PropTypes.func.isRequired,

+ 79
- 0
src/components/MUI/InviteDialog.js Целия файл

@@ -0,0 +1,79 @@
import React from "react";
import planeVector from "../../assets/images/planeVector.png";
import PropTypes from "prop-types";
import {
Dialog,
DialogContent,
DialogTitle,
DialogActions,
// Button,
useMediaQuery,
useTheme,
IconButton,
} from "@mui/material";

const InviteDialog = ({
title,
content,
onClose,
open,
maxWidth,
fullWidth,
responsive,
}) => {
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down("md"));

const handleClose = () => {
onClose();
};

return (
<Dialog
maxWidth={maxWidth}
fullWidth={fullWidth}
fullScreen={responsive && fullScreen}
onClose={handleClose}
open={open}
style={{
padding: "36px",
}}
>
<div style={{ padding: "36px" }}>
<DialogTitle style={{padding: 0}}>{title}</DialogTitle>
{content && <DialogContent style={{padding: '50px 0px'}}>{content}</DialogContent>}
<DialogActions style={{padding: 0}}>
{<IconButton
className={"c-btn--primary c-btn inviteBtn"}
sx={{
width:'100%'
}}
onClick={handleClose}
>
<img
style={{
position: "relative",
top: 1.25,
paddingRight: "15px",
}}
src={planeVector}
/>{" "}
Registracioni link
</IconButton>}
</DialogActions>
</div>
</Dialog>
);
};

InviteDialog.propTypes = {
title: PropTypes.any,
open: PropTypes.bool.isRequired,
content: PropTypes.any,
onClose: PropTypes.func.isRequired,
maxWidth: PropTypes.oneOf(["xs", "sm", "md", "lg", "xl"]),
fullWidth: PropTypes.bool,
responsive: PropTypes.bool,
};

export default InviteDialog;

+ 2
- 2
src/components/MUI/NavbarComponent.js Целия файл

@@ -140,7 +140,7 @@ const NavbarComponent = () => {
}}
className="text-black"
as={Link}
to={n}
to={`/${n}`}
>
{t("nav." + n)}
</ListItemText>
@@ -297,7 +297,7 @@ const NavbarComponent = () => {
}}
className="text-black"
as={Link}
to={n}
to={`/${n}`}
>
{t("nav." + n)}
</Typography>

+ 13
- 6
src/components/Section/MainContainer.js Целия файл

@@ -4,18 +4,25 @@ import { useLocation } from "react-router-dom";
import Navbar from "../../components/MUI/NavbarComponent";
// import AppRoutes from "../../AppRoutes";

const urls = [
"/",
"/login",
"/forgot-password",
"/reset-password",
"/forgot-password-confirmation",
"/error-page",
];

const MainContainer = ({ children }) => {
const { pathname } = useLocation();

return pathname !== "/" && pathname !== "/login" ? (
return urls.includes(pathname) ? (
<div className="">{children}</div>
) : (
<div className="">
<Navbar />
<div className="h-withHeader">
{children}
</div>
<div className="h-withHeader">{children}</div>
</div>
) : (
<div className="">{children}</div>
);
};


+ 2
- 0
src/constants/pages.js Целия файл

@@ -2,9 +2,11 @@ export const BASE_PAGE = '/';
export const FORGOT_PASSWORD_PAGE = '/forgot-password';
export const HOME_PAGE = '/home';
export const ADS_PAGE = '/ads';
export const AD_DETAILS_PAGE = '/ads/:id';
export const ERROR_PAGE = '/error-page';
export const NOT_FOUND_PAGE = '/not-found';
export const USERS_PAGE = '/users';
export const USER_DETAILS_PAGE = '/users/:id';
export const CANDIDATES_PAGE = '/candidates';
export const CANDIDATES_DETAILS_PAGE = '/candidates/:id';
export const FORGOT_PASSWORD_CONFIRMATION_PAGE = '/forgot-password-confirmation';

+ 89
- 79
src/i18n/resources/en.js Целия файл

@@ -1,105 +1,115 @@
export default {
app: {
title: 'HR Center'
title: "HR Center",
},
refresh: {
title: 'Are you active?',
cta:
"You were registered as not active, please confirm that you are active in the next minute, if you don't you will be logged out.",
title: "Are you active?",
cta: "You were registered as not active, please confirm that you are active in the next minute, if you don't you will be logged out.",
},
common: {
close: 'Close',
trademark: 'TM',
search: 'Search',
error: 'Error',
continue: 'Continue',
labelUsername: 'Username',
labelPassword: 'Password',
or: 'or',
next: 'Next',
nextPage: 'Next page',
previousPage: 'Previous page',
back: 'Back',
goBack: 'Go Back',
ok: 'Ok',
done: 'Done',
confirm: 'Confirm',
printDownload: 'Print/Download',
cancel: 'Cancel',
remove: 'Remove',
invite: 'Invite',
save: 'Save',
complete: 'Complete',
download: 'Download',
yes: 'Yes',
no: 'No',
to: 'to',
select: 'Select...',
none: 'None',
close: "Close",
trademark: "TM",
search: "Search",
error: "Error",
continue: "Continue",
labelUsername: "Username",
labelPassword: "Password",
or: "or",
next: "Next",
nextPage: "Next page",
previousPage: "Previous page",
back: "Back",
goBack: "Go Back",
ok: "Ok",
done: "Done",
confirm: "Confirm",
printDownload: "Print/Download",
cancel: "Cancel",
remove: "Remove",
invite: "Invite",
save: "Save",
complete: "Complete",
download: "Download",
yes: "Yes",
no: "No",
to: "to",
select: "Select...",
none: "None",
date: {
range: '{{start}} to {{end}}',
range: "{{start}} to {{end}}",
},
},
login: {
welcome: 'Welcome!',
welcome: "Welcome!",
dontHaveAccount: "Don't have an account? ",
emailFormat: 'Invalid email address format.',
emailRequired: 'An email or username is required.',
noUsers: 'There are no users with that email.',
passwordStrength: 'Your password is {{strength}}.',
passwordLength: 'Your password contain between 8 and 50 characters.',
signUpRecommendation: 'Sign up',
email: 'Please enter your email address or username to log in:',
logInTitle: 'Log In',
logIn: 'Log In',
signUp: 'Sign Up',
usernameRequired: 'Username is required.',
passwordRequired: 'A Password is required.',
forgotYourPassword: 'Forgot your password?',
forgotPasswordEmail:'Email',
useDifferentEmail: 'Use different email address or username',
signInWithGoogle: 'Continue with google'
emailFormat: "Invalid email address format.",
emailRequired: "An email or username is required.",
noUsers: "There are no users with that email.",
passwordStrength: "Your password is {{strength}}.",
passwordLength: "Your password contain between 8 and 50 characters.",
signUpRecommendation: "Sign up",
email: "Please enter your email address or username to log in:",
logInTitle: "Log In",
logIn: "Log In",
signUp: "Sign Up",
usernameRequired: "Username is required.",
passwordRequired: "A Password is required.",
forgotYourPassword: "Forgot your password?",
forgotPasswordEmail: "Email",
useDifferentEmail: "Use different email address or username",
signInWithGoogle: "Continue with google",
},
password: {
weak: 'weak',
average: 'average',
good: 'good',
strong: 'strong',
weak: "weak",
average: "average",
good: "good",
strong: "strong",
},
forgotPassword: {
title: 'Forgot Password',
label: 'Send email',
emailRequired: 'An email is required.',
emailFormat: 'Invalid email address format.',
title: "Forgot Password",
label: "Send email",
emailRequired: "An email is required.",
emailFormat: "Invalid email address format.",
forgotPassword: {
title: 'Forgot Password',
title: "Forgot Password",
subtitle:
'Please answer the security question to gain access to your account:',
label: 'Reset Password',
"Please answer the security question to gain access to your account:",
label: "Reset Password",
},
},
notFound: {
text: "We're sorry but we couldn't find the page you were looking for.",
goBack: 'Go back to homepage',
goBack: "Go back to homepage",
},
errorPage: {
text:
"We're sorry, an internal server error came up. Please be patient or try again later.",
goBack: 'Go back to homepage',
logout: 'Logout',
text: "We're sorry, an internal server error came up. Please be patient or try again later.",
goBack: "Go back to homepage",
logout: "Logout",
},
apiErrors:{
ClientIpAddressIsNullOrEmpty:"Client Ip address is null or empty",
UsernameDoesNotExist: "Username does not exist"
apiErrors: {
ClientIpAddressIsNullOrEmpty: "Client Ip address is null or empty",
UsernameDoesNotExist: "Username does not exist",
},
nav: {
ads: "Ads",
selectionFlow: "Selection flow",
candidates: "Candidates",
planer: "Planer",
patterns: "Patterns",
stats: "Stats",
users: "Users",
signOut: "Sign Out",
},
ads: {
activeAds: "Active Ads",
archiveAds: "Archive",
adDetailsDescription:
"Team Diligent is constantly growing! We are looking for a team player that will work with experienced engineers. If technology is your passion and you are ready to move the boundaries of your knowledge every day, then, Diligent is the right place for you. If you are not from Niš, we are offering a full remote position.",
adDetailsExperience: "years of experience",
adDetailsExpiredAt: "Expired at",
adDetailsKeyResponsibilities: "Key Responsibilities",
adDetailsRequirements: "Requirements",
adDetailsOffer: "What We Offer",
archiveAdsCandidates: "Registered candidates",
},
nav:{
ads: 'Ads',
selectionFlow: 'Selection flow',
candidates: 'Candidates',
planer: 'Planer',
patterns: 'Patterns',
stats: 'Stats',
users: 'Users',
signOut: 'Sign Out'
}
};

+ 90
- 80
src/i18n/resources/rs.js Целия файл

@@ -1,77 +1,79 @@
export default {
app: {
title: 'HR Centar'
title: "HR Centar",
},
refresh: {
// title: 'Are you active?',
// cta:
// "You were registered as not active, please confirm that you are active in the next minute, if you don't you will be logged out.",
// title: 'Are you active?',
// cta:
// "You were registered as not active, please confirm that you are active in the next minute, if you don't you will be logged out.",
},
common: {
// close: 'Close',
// trademark: 'TM',
// search: 'Search',
// error: 'Error',
// continue: 'Continue',
labelUsername: 'Korisničko ime',
labelPassword: 'Šifra',
labelConfirmPassword: 'Ponovljena šifra',
or: 'ili',
// next: 'Next',
// nextPage: 'Next page',
// previousPage: 'Previous page',
// back: 'Back',
// goBack: 'Go Back',
// ok: 'Ok',
// done: 'Done',
// confirm: 'Confirm',
// printDownload: 'Print/Download',
// cancel: 'Cancel',
// remove: 'Remove',
// invite: 'Invite',
// save: 'Save',
// complete: 'Complete',
// download: 'Download',
// yes: 'Yes',
// no: 'No',
// to: 'to',
// select: 'Select...',
// none: 'None',
// date: {
// range: '{{start}} to {{end}}',
// },
// close: 'Close',
// trademark: 'TM',
// search: 'Search',
// error: 'Error',
// continue: 'Continue',
labelUsername: "Korisničko ime",
labelPassword: "Šifra",
labelConfirmPassword: "Ponovljena šifra",
or: "ili",
// next: 'Next',
// nextPage: 'Next page',
// previousPage: 'Previous page',
// back: 'Back',
// goBack: 'Go Back',
// ok: 'Ok',
// done: 'Done',
// confirm: 'Confirm',
// printDownload: 'Print/Download',
// cancel: 'Cancel',
// remove: 'Remove',
// invite: 'Invite',
// save: 'Save',
// complete: 'Complete',
// download: 'Download',
// yes: 'Yes',
// no: 'No',
// to: 'to',
// select: 'Select...',
// none: 'None',
// date: {
// range: '{{start}} to {{end}}',
// },
},
login: {
welcome: 'Dobrodošli!',
// dontHaveAccount: "Don't have an account? ",
// emailFormat: 'Invalid email address format.',
// emailRequired: 'An email or username is required.',
// noUsers: 'There are no users with that email.',
// passwordStrength: 'Your password is {{strength}}.',
// passwordLength: 'Your password contain between 8 and 50 characters.',
// signUpRecommendation: 'Sign up',
// email: 'Please enter your email address or username to log in:',
logInTitle: 'Prijavi se',
logIn: 'Prijavi se',
// signUp: 'Sign Up',
usernameRequired: 'Potrebno je uneti korisničko ime.',
passwordRequired: 'Potrebno je uneti šifru.',
forgotYourPassword: 'Zaboravio/la si šifru?',
resetYourPassword: 'Nova šifra',
resetYourPasswordHelpText: 'Unesi novu šifru.',
forgotYourPasswordHelpText: 'Samo unesi e-mail adresu svog HR Center profila.',
forgotYourPasswordButton: 'POŠALJI',
forgotYourPasswordBackLink: 'Nazad na Login',
forgotYourPasswordConfimation: 'Proveri email adresu da bi resetovao šifru.',
passwordDontMatch: 'Šifre se ne poklapaju.',
// _useDifferentEmail: 'Use different email address or username',
// get useDifferentEmail() {
// return this._useDifferentEmail;
// },
// set useDifferentEmail(value) {
// this._useDifferentEmail = value;
// },
signInWithGoogle: 'Prijava putem Google-a'
welcome: "Dobrodošli!",
// dontHaveAccount: "Don't have an account? ",
// emailFormat: 'Invalid email address format.',
// emailRequired: 'An email or username is required.',
// noUsers: 'There are no users with that email.',
// passwordStrength: 'Your password is {{strength}}.',
// passwordLength: 'Your password contain between 8 and 50 characters.',
// signUpRecommendation: 'Sign up',
// email: 'Please enter your email address or username to log in:',
logInTitle: "Prijavi se",
logIn: "Prijavi se",
// signUp: 'Sign Up',
usernameRequired: "Potrebno je uneti korisničko ime.",
passwordRequired: "Potrebno je uneti šifru.",
forgotYourPassword: "Zaboravio/la si šifru?",
resetYourPassword: "Nova šifra",
resetYourPasswordHelpText: "Unesi novu šifru.",
forgotYourPasswordHelpText:
"Samo unesi e-mail adresu svog HR Center profila.",
forgotYourPasswordButton: "POŠALJI",
forgotYourPasswordBackLink: "Nazad na Login",
forgotYourPasswordConfimation:
"Proveri email adresu da bi resetovao šifru.",
passwordDontMatch: "Šifre se ne poklapaju.",
// _useDifferentEmail: 'Use different email address or username',
// get useDifferentEmail() {
// return this._useDifferentEmail;
// },
// set useDifferentEmail(value) {
// this._useDifferentEmail = value;
// },
signInWithGoogle: "Prijava putem Google-a",
},
// password: {
// weak: 'weak',
@@ -105,18 +107,26 @@ export default {
// ClientIpAddressIsNullOrEmpty:"Client Ip address is null or empty",
// UsernameDoesNotExist: "Username does not exist"
// },
nav:{
ads: 'Oglasi',
selectionFlow: 'Tok Selekcije',
candidates: 'Kandidati',
planer: 'Planer',
patterns: 'Šabloni',
stats: 'Statistika',
users: 'Korisnici',
signOut: 'Izloguj se'
},
ads: {
activeAds: "Aktivni Oglasi",
archiveAds: "Arhiva"
}
nav: {
ads: "Oglasi",
selectionFlow: "Tok Selekcije",
candidates: "Kandidati",
planer: "Planer",
patterns: "Šabloni",
stats: "Statistika",
users: "Korisnici",
signOut: "Izloguj se",
},
ads: {
activeAds: "Aktivni Oglasi",
archiveAds: "Arhiva",
adDetailsDescription:
"Tim Diligent neprestano raste! Tražimo timskog igrača koji će raditi s iskusnim inženjerima. Ako je tehnologija vaša strast i spremni ste svaki dan pomicati granice svog znanja, onda je Diligent pravo mesto za vas. Ukoliko niste iz Niša, nudimo full-time remote poziciju.",
adDetailsExperience: "godina iskustva",
adDetailsExpiredAt: "Rok prijave do",
adDetailsKeyResponsibilities: "Ključne Odgovornosti",
adDetailsRequirements: "Zahtevi",
adDetailsOffer: "Šta Nudimo",
archiveAdsCandidates: "Prijavljeni kandidati",
},
};

+ 222
- 0
src/pages/AdsPage/AdDetailsPage.js Целия файл

@@ -0,0 +1,222 @@
import React, { useEffect, useRef } from "react";
import { IconButton, useMediaQuery } from "@mui/material";
import aspNetIcon from "../../assets/images/.net_icon.png";
import { Link } from "react-router-dom";
import { useParams } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { setAdReq } from "../../store/actions/ad/adActions";
import { selectAd } from "../../store/selectors/adSelectors";
import { useTranslation } from "react-i18next";
import { useTheme } from "@emotion/react";
import Slider from "react-slick";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import arrow_left from "../../assets/images/arrow_left.png";
import arrow_right from "../../assets/images/arrow_right.png";
import AdDetailsCandidateCard from "../../components/Ads/AdDetailsCandidateCard";

const AdDetailsPage = () => {
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down("sm"));
const { id } = useParams();
const ad = useSelector(selectAd);
const dispatch = useDispatch();
const { t } = useTranslation();
const archiveAdsSliderRef = useRef();

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

var settings = {
dots: false,
infinite: false,
speed: 500,
initialSlide: 0,
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 3,
slidesToScroll: 3,
infinite: true,
dots: false,
},
},
{
breakpoint: 900,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
initialSlide: 0,
},
},
{
breakpoint: 480,
settings: {
slidesToShow: 1,
slidesToScroll: 1,
},
},
],
};

const archiveAdsArrowLeftHandler = () => {
archiveAdsSliderRef.current.slickPrev();
};

const archiveAdsArrowRightHandler = () => {
archiveAdsSliderRef.current.slickNext();
};

const getDummyArchiveAds = (len) => {
const ads = [];

for (let i = 0; i < 5 - len + 1; i++) {
ads.push(<AdDetailsCandidateCard key={i} className="hiddenAd" />);
}

return ads;
};

return (
<>
{!ad && <p>Radi</p>}
{ad && (
<div className="ad-details">
{matches && (
<div className="ad-details-tech-logo-date">
<p>
<span>{t("ads.adDetailsExpiredAt")}: </span>
{new Date(ad.expiredAt).toLocaleDateString()}
</p>
</div>
)}
<div className="ad-details-tech-logo">
<div className="ad-details-tech-logo-title">
<div className="ad-details-tech-logo-title-img">
<img src={aspNetIcon} alt="asp-net-icon" />
</div>
<div className="ad-details-tech-logo-title-title">
<h1>{ad.title}</h1>
</div>
<div className="ad-details-tech-logo-title-sub">
<sub>| {ad.totalApplicants} prijavljenih</sub>
</div>
</div>
{!matches && (
<div className="ad-details-tech-logo-date">
<p>
<span>{t("ads.adDetailsExpiredAt")}: </span>
{new Date(ad.expiredAt).toLocaleDateString()}
</p>
</div>
)}
</div>
<div className="ad-details-content">
<div className="ad-details-content-experience">
<p>
{ad.minimumExperience}+ {t("ads.adDetailsExperience")}
</p>
</div>
<div className="ad-details-content-work-time">
<button>Posao</button>
<button>Full-time</button>
</div>
<div className="ad-details-content-content">
<div className="ad-details-content-conten-description">
<p>{t("ads.adDetailsDescription")}</p>
</div>
<div className="ad-details-content-conten-description">
<h3>{t("ads.adDetailsKeyResponsibilities")}</h3>
<ul>
{ad.keyResponsibilities.split("|").map((r, index) => (
<li key={index}>{r}</li>
))}
</ul>
</div>
<div className="ad-details-content-conten-description">
<h3>{t("ads.adDetailsRequirements")}</h3>
<ul>
{ad.requirements.split("|").map((r, index) => (
<li key={index}>{r}</li>
))}
</ul>
</div>
<div className="ad-details-content-conten-description">
<h3>{t("ads.adDetailsOffer")}</h3>
<ul>
{ad.offer.split("|").map((o, index) => (
<li key={index}>{o}</li>
))}
</ul>
</div>
</div>
</div>
{ad.applicants && ad.applicants.length > 0 && (
<div className="ad-details-applicants">
<div className="ad-details-applicants-header">
<h2>{t("ads.archiveAdsCandidates")}</h2>
</div>
<div className="ad-details-applicants-applicants">
{!matches && (
<div className="ad-details-applicants-applicants-a">
<div className="ad-details-applicants-applicants-arrows">
<button onClick={archiveAdsArrowLeftHandler}>
<img src={arrow_left} alt="arrow-left" />
</button>
<button onClick={archiveAdsArrowRightHandler}>
<img src={arrow_right} alt="arrow-right" />
</button>
</div>
</div>
)}
<div className="ad-details-applicants-applicants-applicant">
<Slider
ref={archiveAdsSliderRef}
{...settings}
slidesToShow={3}
slidesToScroll={3}
style={{ width: "100%" }}
>
{ad.applicants.map((applicant, index) => (
<AdDetailsCandidateCard
key={index}
firstName={applicant.firstName}
lastName={applicant.lastName}
experience={applicant.experience}
cv={applicant.cv}
/>
))}
{ad.applicants.length <= 3 &&
getDummyArchiveAds(ad.applicants.length)}
</Slider>
</div>
</div>
{matches && (
<div className="active-ads-ads-arrows">
<button onClick={archiveAdsArrowLeftHandler}>
<img src={arrow_left} alt="arrow-left" />
</button>
<button onClick={archiveAdsArrowRightHandler}>
<img src={arrow_right} alt="arrow-right" />
</button>
</div>
)}
</div>
)}
<div className="ad-details-buttons">
<Link className="ad-details-buttons-link" to="/ads">
Nazad na sve oglase
</Link>
<IconButton className="c-btn c-btn--primary add-ad-btn">
PRIJAVI SE
</IconButton>
</div>
</div>
)}
</>
);
};

export default AdDetailsPage;

+ 202
- 45
src/pages/AdsPage/AdsPage.js Целия файл

@@ -1,18 +1,43 @@
import React, { useState } from "react";
import React, { useState, useEffect, useRef } from "react";
import PropTypes from "prop-types";
import Ad from "../../components/Ads/Ad";
import ArchiveAd from "../../components/Ads/ArchiveAd";
import IconButton from "../../components/IconButton/IconButton";
import filterVector from "../../assets/images/filter_vector.png";
import arrow_left from "../../assets/images/arrow_left.png";
import arrow_right from "../../assets/images/arrow_right.png";
import { useTranslation } from "react-i18next";
import AddAdModal from "../../components/Ads/AddAdModal";
import AdFilters from "../../components/Ads/AdFilters";
import { useDispatch } from "react-redux";
import { setAdsReq } from "../../store/actions/ads/adsAction";
import { useSelector } from "react-redux";
import { selectAds } from "../../store/selectors/adsSelectors";
import { AD_DETAILS_PAGE } from "../../constants/pages";
import FilterButton from "../../components/Button/FilterButton";
import Slider from "react-slick";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import { useTheme } from "@emotion/react";
import { useMediaQuery } from "@mui/material";
import { selectArchiveAds } from "../../store/selectors/archiveAdsSelectors";
import { setArchiveAdsReq } from "../../store/actions/archiveAds/archiveAdsActions";

const AdsPage = () => {
const AdsPage = ({ history }) => {
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down("sm"));
const [toggleFiltersDrawer, setToggleFiltersDrawer] = useState(false);
const [toggleModal, setToggleModal] = useState(false);
const ads = useSelector(selectAds);
const archiveAds = useSelector(selectArchiveAds);
const activeAdsSliderRef = useRef();
const archiveAdsSliderRef = useRef();
const { t } = useTranslation();
const dispatch = useDispatch();

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

const handleToggleFiltersDrawer = () => {
setToggleFiltersDrawer((oldState) => !oldState);
@@ -22,68 +47,196 @@ const AdsPage = () => {
setToggleModal((oldState) => !oldState);
};

var settings = {
dots: false,
infinite: false,
speed: 500,
initialSlide: 0,
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 3,
slidesToScroll: 3,
infinite: true,
dots: false,
},
},
{
breakpoint: 900,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
initialSlide: 0,
},
},
{
breakpoint: 480,
settings: {
slidesToShow: 1,
slidesToScroll: 1,
},
},
],
};

const activeAdsArrowLeftHandler = () => {
activeAdsSliderRef.current.slickPrev();
};

const activeAdsArrowRightHandler = () => {
activeAdsSliderRef.current.slickNext();
};

const archiveAdsArrowLeftHandler = () => {
archiveAdsSliderRef.current.slickPrev();
};

const archiveAdsArrowRightHandler = () => {
archiveAdsSliderRef.current.slickNext();
};

const getDummyAds = (len) => {
const ads = [];

for (let i = 0; i < 5 - len + 1; i++) {
ads.push(<Ad key={i} className="hiddenAd" />);
}

return ads;
};

const getDummyArchiveAds = (len) => {
const ads = [];

for (let i = 0; i < 5 - len + 1; i++) {
ads.push(<ArchiveAd key={i} className="hiddenAd" />);
}

return ads;
};

return (
<>
<div className="l-t-rectangle"></div>
<div className="r-b-rectangle"></div>
<AdFilters />
{/* <AdFilters /> */}
<AdFilters
open={toggleFiltersDrawer}
handleClose={handleToggleFiltersDrawer}
/>
<AddAdModal open={toggleModal} handleClose={handleToggleModal} />
<div className="ads">
<div className="active-ads">
<div className="active-ads-header">
<h1>{t("ads.activeAds")}</h1>
<IconButton
sx={{ marginLeft: "15px" }}
className="c-btn c-btn--primary-outlined"
onClick={handleToggleFiltersDrawer}
>
Filteri{" "}
<img src={filterVector} alt="filter" className="filter-vector" />
</IconButton>
</div>
<div className="active-ads-ads">
<div className="active-ads-ads-a">
<div className="active-ads-ads-arrows">
<button>
<img src={arrow_left} alt="arrow-left" />
</button>
<button>
<img src={arrow_right} alt="arrow-right" />
</button>
</div>
{ads && ads.length > 0 && (
<div className="active-ads">
<div className="active-ads-header">
<h1>{t("ads.activeAds")}</h1>
<FilterButton onShowFilters={handleToggleFiltersDrawer} />
</div>
<div className="active-ads-ads-ad">
<Ad />
<Ad />
<div className="active-ads-ads">
<div className="active-ads-ads-a">
{!matches && (
<div className="active-ads-ads-arrows">
<button onClick={activeAdsArrowLeftHandler}>
<img src={arrow_left} alt="arrow-left" />
</button>
<button onClick={activeAdsArrowRightHandler}>
<img src={arrow_right} alt="arrow-right" />
</button>
</div>
)}
</div>
<div className="active-ads-ads-ad">
<Slider
{...settings}
slidesToShow={3}
slidesToScroll={3}
style={{ width: "100%" }}
ref={activeAdsSliderRef}
>
{ads.map((ad, index) => (
<Ad
onShowAdDetails={() =>
history.push(AD_DETAILS_PAGE.replace(":id", ad.id))
}
key={index}
title={ad.title}
minimumExperience={ad.minimumExperience}
createdAt={ad.createdAt}
expiredAt={ad.expiredAt}
/>
))}
{ads.length <= 5 && getDummyAds(ads.length)}
</Slider>
</div>
</div>
</div>
</div>

<div className="archived-ads">
<div className="archived-ads-header">
<h2>{t("ads.archiveAds")}</h2>
)}
{matches && (
<div className="active-ads-ads-arrows">
<button onClick={activeAdsArrowLeftHandler}>
<img src={arrow_left} alt="arrow-left" />
</button>
<button onClick={activeAdsArrowRightHandler}>
<img src={arrow_right} alt="arrow-right" />
</button>
</div>
<div className="archived-ads-ads">
<div className="archived-ads-ads-a">
<div className="archived-ads-ads-arrows">
<button>
)}
{archiveAds && archiveAds.length > 0 && (
<div className="archived-ads">
<div className="archived-ads-header">
<h2>{t("ads.archiveAds")}</h2>
</div>
<div className="archived-ads-ads">
{!matches && (
<div className="archived-ads-ads-a">
<div className="archived-ads-ads-arrows">
<button onClick={archiveAdsArrowLeftHandler}>
<img src={arrow_left} alt="arrow-left" />
</button>
<button onClick={archiveAdsArrowRightHandler}>
<img src={arrow_right} alt="arrow-right" />
</button>
</div>
</div>
)}
<div className="archived-ads-ads-ad">
<Slider
ref={archiveAdsSliderRef}
{...settings}
slidesToShow={5}
slidesToScroll={5}
style={{ width: "100%" }}
>
{archiveAds.map((ad, index) => (
<ArchiveAd
key={index}
title={ad.title}
minimumExperience={ad.minimumExperience}
createdAt={ad.createdAt}
expiredAt={ad.expiredAt}
onShowAdDetails={() =>
history.push(AD_DETAILS_PAGE.replace(":id", ad.id))
}
/>
))}
{archiveAds.length <= 5 &&
getDummyArchiveAds(archiveAds.length)}
</Slider>
</div>
</div>
{matches && (
<div className="active-ads-ads-arrows">
<button onClick={archiveAdsArrowLeftHandler}>
<img src={arrow_left} alt="arrow-left" />
</button>
<button>
<button onClick={archiveAdsArrowRightHandler}>
<img src={arrow_right} alt="arrow-right" />
</button>
</div>
</div>
<div className="archived-ads-ads-ad">
<ArchiveAd />
<ArchiveAd />
</div>
)}
</div>
</div>
)}
</div>

<div className="add-ad">
@@ -98,4 +251,8 @@ const AdsPage = () => {
);
};

AdsPage.propTypes = {
history: PropTypes.any,
};

export default AdsPage;

+ 196
- 0
src/pages/UsersPage/UserDetails.js Целия файл

@@ -0,0 +1,196 @@
import React from "react";
import PropTypes from "prop-types";
import avatar from "../../assets/images/Avatar.png";
import filters from "../../assets/images/filters.png";
import lock from "../../assets/images/lock.png";
import forbiden from "../../assets/images/forbiden.png";
import IconButton from "../../components/IconButton/IconButton";
import { Link, useParams } from "react-router-dom";
import { deleteUserReq } from "../../store/actions/users/usersActions";
import { useDispatch } from "react-redux";
import { USERS_PAGE } from "../../constants/pages";
import { forgetPassword } from "../../store/actions/login/loginActions";

const UserDetails = ({ history }) => {
const { id } = useParams();
const dispatch = useDispatch();

const handleReset = (email) => {
dispatch(
forgetPassword({
email,
handleApiResponseSuccessReset,
})
);
};

const handleApiResponseSuccessReset = () => {
alert("Ok");
};

const handleApiResponseSuccess = () => {
history.push({
pathname: USERS_PAGE,
state: {
from: history.location.pathname,
},
});
};

const deleteHandler = () => {
dispatch(deleteUserReq({ id, handleApiResponseSuccess }));
};

return (
<div>
<div className="l-t-rectangle"></div>
<div className="r-b-rectangle"></div>
<div className="pl-144 pt-36px">
<div className="divider">
<div className="flex-center">
<h1 style={{ letterSpacing: "1px" }}>Korisnik</h1>
<div
className="vr"
style={{
margin: "0px 10px 0px 15px",
top: "6px",
backgroundColor: "#252525",
}}
></div>
<h3
style={{
letterSpacing: "0.75px",
position: "relative",
top: "4px",
}}
className="text-blue"
>
Stefan Stamenkovic
</h3>
</div>
<div className="flex-center">
<IconButton
className={
"c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding"
}
onClick={() => handleReset("meris.ahmatovic@dilig.net")}
>
Resetuj password
<img
style={{
position: "relative",
top: -0.25,
paddingLeft: "10px",
}}
src={lock}
/>
</IconButton>
<IconButton
className={
"c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding"
}
>
Blokiraj
<img
style={{
position: "relative",
top: -0.25,
paddingLeft: "10px",
}}
src={forbiden}
/>
</IconButton>
<IconButton
className={
"c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding"
}
onClick={deleteHandler}
>
Obrisi
<img
style={{
position: "relative",
top: -0.25,
paddingLeft: "10px",
}}
src={filters}
/>
</IconButton>
<IconButton
className={
"c-btn--primary-outlined c-btn userPageBtn ml-20px no-padding"
}
>
Uredi profil
<img
style={{
position: "relative",
top: -0.25,
paddingLeft: "10px",
}}
src={filters}
/>
</IconButton>
</div>
</div>
<div className="flex-center" style={{ justifyContent: "flex-start" }}>
<img
src={avatar}
height="108px"
width="108px"
style={{ margin: "18px 15px 36px 0px" }}
/>
<p>HR Specialist</p>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "18px" }}>
<p style={{ fontWeight: "600" }}>Kontakt</p>
<div className="flex-center" style={{ justifyContent: "flex-start" }}>
<p style={{ width: "85px" }}>Email:</p>
<p className="text-blue">stameni@dilig.net</p>
</div>
<div className="flex-center" style={{ justifyContent: "flex-start" }}>
<p style={{ width: "85px" }}>Telefon:</p>
<p className="text-blue">0628264606</p>
</div>
</div>
<div
style={{
display: "flex",
flexDirection: "column",
gap: "18px",
paddingTop: "36px",
}}
>
<p style={{ fontWeight: "600" }}>Drustvene mreze</p>
<div className="flex-center" style={{ justifyContent: "flex-start" }}>
<p style={{ width: "85px" }}>LinkedIn:</p>
<p className="text-blue">https://www.linkedin.com/in/stamenis/</p>
</div>
</div>
<div
style={{
display: "flex",
justifyContent: "flex-end",
marginTop: "150px",
}}
>
<Link to={"/users"} className="text-blue">
Nazad na listu korisnika
</Link>
</div>
</div>
</div>
);
};

export default UserDetails;

UserDetails.propTypes = {
history: PropTypes.shape({
replace: PropTypes.func,
push: PropTypes.func,
location: PropTypes.shape({
pathname: PropTypes.string,
}),
}),
};

+ 112
- 19
src/pages/UsersPage/UsersPage.js Целия файл

@@ -1,32 +1,114 @@
import React from "react";
import React, { useState } from "react";
import IconButton from "../../components/IconButton/IconButton";
import userPageBtnIcon from "../../assets/images/userPageBtnIcon.png";
import planeVector from "../../assets/images/planeVector.png";
import lock from "../../assets/images/lock.png";
import filters from "../../assets/images/filters.png";
import forbiden from "../../assets/images/forbiden.png";
import x from "../../assets/images/x.png";
import edit from "../../assets/images/edit.png";
import { useEffect } from "react";
import addUser from "../../assets/images/addUser.png";
// import { getAllUsers } from "../../request/usersRequest";
// import { getUsers } from "../../store/saga/usersSaga";
import { useDispatch, useSelector } from "react-redux";
import { setUsersReq } from "../../store/actions/users/usersActions";
import {
// deleteUserReq,
setEnableUsersReq,
setUsersReq,
} from "../../store/actions/users/usersActions";
import { useTheme } from "@mui/system";
import { useMediaQuery } from "@mui/material";
import { TextField, useMediaQuery } from "@mui/material";
// import DialogComponent from "../../components/MUI/DialogComponent";
import InviteDialog from "../../components/MUI/InviteDialog";
import { Link } from "react-router-dom";
import { forgetPassword } from "../../store/actions/login/loginActions";

const UsersPage = () => {
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down("sm"));
const dispatch = useDispatch();
const { users } = useSelector((s) => s.users);

const [showInvite, setShowInvite] = useState(false);

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

const disableHandler = (id) => {
dispatch(setEnableUsersReq({ id }));
};

const handleReset = (email) => {
dispatch(
forgetPassword({
email,
handleApiResponseSuccessReset,
})
);
};

const handleApiResponseSuccessReset = () => {
alert("Ok");
};

return (
<div>
<div className="l-t-rectangle"></div>
<div className="r-b-rectangle"></div>
{/* {showInvite && <DialogComponent/>} */}
<InviteDialog
open={showInvite}
onClose={() => {
setShowInvite(false);
}}
title={
<div
className="flex-center"
style={{ justifyContent: "space-between" }}
>
<div className="flex-center" style={{ justifyContent: "start" }}>
<img
style={{
position: "relative",
top: -0.25,
paddingRight: "10px",
}}
src={addUser}
/>
<h5>Invite korisnika</h5>
<div className="vr"></div>
<p className="dialog-subtitle">Registracioni link</p>
</div>
<IconButton onClick={() => setShowInvite(false)}>
<img
style={{
position: "relative",
top: -0.25,
}}
src={x}
/>
</IconButton>
</div>
}
content={
<form>
{/* <label>Primaoc</label> */}
<TextField
name="username"
label={"Primaoc"}
margin="normal"
// value={formik.values.username}
// onChange={formik.handleChange}
// error={formik.touched.username && Boolean(formik.errors.username)}
// helperText={formik.touched.username && formik.errors.username}
// autoFocus
fullWidth
/>
</form>
}
/>
<div>
<div
className="pl-144 flex-center"
@@ -78,13 +160,12 @@ const UsersPage = () => {
}}
>
<div className=" table-cont">
<table className="usersTable" style={{ width: "1117px" }}>
<table className="usersTable" style={{ width: "893.56px" }}>
<thead>
<tr className="headingRow">
<th>Ime i prezime</th>
<th>E-mail</th>
<th>Pozicija</th>
<th>CV Link</th>
<th></th>
</tr>
</thead>
@@ -96,10 +177,11 @@ const UsersPage = () => {
</td>
<td>{n.email}</td>
<td>HR Specialist</td>
<td className="cvLink">CV_Ermin.pdf</td>
{/* <td className="cvLink">CV_Ermin.pdf</td> */}
<td>
<IconButton
className={"c-btn--primary-outlined c-btn td-btn"}
className={`c-btn--primary-outlined c-btn td-btn`}
onClick={() => handleReset(n.email)}
>
<img
style={{
@@ -109,7 +191,11 @@ const UsersPage = () => {
/>
</IconButton>
<IconButton
className={"c-btn--primary-outlined c-btn td-btn"}
className={`c-btn--primary-outlined c-btn td-btn ${
n.isEnabled ? "active" : "inactive"
}`}
onClick={() => disableHandler(n.id)}
>
<img
style={{
@@ -118,16 +204,18 @@ const UsersPage = () => {
src={forbiden}
/>
</IconButton>
<IconButton
className={"c-btn--primary-outlined c-btn td-btn"}
>
<img
style={{
position: "relative",
}}
src={edit}
/>
</IconButton>
<Link to={`/users/${n.id}`}>
<IconButton
className={"c-btn--primary-outlined c-btn td-btn"}
>
<img
style={{
position: "relative",
}}
src={edit}
/>
</IconButton>
</Link>
</td>
</tr>
))}
@@ -135,7 +223,12 @@ const UsersPage = () => {
</table>
</div>
<div style={{ display: "flex", justifyContent: "flex-end" }}>
<IconButton className={"c-btn--primary c-btn inviteBtn"}>
<IconButton
className={"c-btn--primary c-btn inviteBtn"}
onClick={() => {
setShowInvite(true);
}}
>
<img
style={{
position: "relative",

+ 7
- 0
src/request/adsRequest.js Целия файл

@@ -0,0 +1,7 @@
import { getRequest } from ".";
import apiEndpoints from "./apiEndpoints";

export const getAllAds = () => getRequest(apiEndpoints.ads.allAds);
export const getAllArchiveAds = () =>
getRequest(apiEndpoints.ads.allArchiveAds);
export const getAdDetailsById = (id) => getRequest(`${apiEndpoints.ads.adDetails}/${id}`);

+ 13
- 0
src/request/apiEndpoints.js Целия файл

@@ -11,11 +11,24 @@ export default {
},
users:{
allUsers:'http://localhost:26081/v1/users',
user:'http://localhost:26081/v1/users/{id}',
toggleEnabled:'http://localhost:26081/v1/users/toggleEnable/{id}',
},
candidates: {
allCandidates: base + "/applicants",
},
ads: {
allAds: base + "/ads",
allArchiveAds: base + "/ads/archive",
adDetails: base + "/ads/details",
},
<<<<<<< HEAD
candidates:{
allCandidates:base + "/applicants",
},
comments:{
addComment:base + '/comments'
}
=======
>>>>>>> FE_dev
};

+ 15
- 1
src/request/usersRequest.js Целия файл

@@ -1,4 +1,18 @@
import { getRequest } from ".";
import { deleteRequest, getRequest, postRequest, replaceInUrl } from ".";
import apiEndpoints from "./apiEndpoints";

export const getAllUsers = () => getRequest(apiEndpoints.users.allUsers);

export const enableUserRequest = (id) =>
postRequest(
replaceInUrl(apiEndpoints.users.toggleEnabled, {
id,
})
);

export const deleteUserRequest = (id) =>
deleteRequest(
replaceInUrl(apiEndpoints.users.user, {
id,
})
);

+ 3
- 0
src/store/actions/ad/adActionConstants.js Целия файл

@@ -0,0 +1,3 @@
export const FETCH_AD_REQ = 'FETCH_AD_REQ';
export const FETCH_AD_ERR = 'FETCH_AD_ERR';
export const FETCH_AD_SUCCESS = 'FETCH_AD_SUCCESS';

+ 20
- 0
src/store/actions/ad/adActions.js Целия файл

@@ -0,0 +1,20 @@
import {
FETCH_AD_REQ,
FETCH_AD_ERR,
FETCH_AD_SUCCESS,
} from "./adActionConstants";

export const setAdReq = (payload) => ({
type: FETCH_AD_REQ,
payload,
});

export const setAdError = (payload) => ({
type: FETCH_AD_ERR,
payload,
});

export const setAd = (payload) => ({
type: FETCH_AD_SUCCESS,
payload,
});

+ 19
- 0
src/store/actions/ads/adsAction.js Целия файл

@@ -0,0 +1,19 @@
import {
FETCH_ADS_REQ,
FETCH_ADS_ERR,
FETCH_ADS_SUCCESS,
} from "./adsActionConstants";

export const setAdsReq = () => ({
type: FETCH_ADS_REQ,
});

export const setAdsError = (payload) => ({
type: FETCH_ADS_ERR,
payload,
});

export const setAds = (payload) => ({
type: FETCH_ADS_SUCCESS,
payload,
});

+ 3
- 0
src/store/actions/ads/adsActionConstants.js Целия файл

@@ -0,0 +1,3 @@
export const FETCH_ADS_REQ = 'FETCH_ADS_REQ';
export const FETCH_ADS_ERR = 'FETCH_ADS_ERR';
export const FETCH_ADS_SUCCESS = 'FETCH_ADS_SUCCESS';

+ 3
- 0
src/store/actions/archiveAds/archiveAdsActionConstants.js Целия файл

@@ -0,0 +1,3 @@
export const FETCH_ARCHIVE_ADS_REQ = 'FETCH_ARCHIVE_ADS_REQ';
export const FETCH_ARCHIVE_ADS_ERR = 'FETCH_ARCHIVE_ADS_ERR';
export const FETCH_ARCHIVE_ADS_SUCCESS = 'FETCH_ARCHIVE_ADS_SUCCESS';

+ 19
- 0
src/store/actions/archiveAds/archiveAdsActions.js Целия файл

@@ -0,0 +1,19 @@
import {
FETCH_ARCHIVE_ADS_REQ,
FETCH_ARCHIVE_ADS_SUCCESS,
FETCH_ARCHIVE_ADS_ERR,
} from "./archiveAdsActionConstants";

export const setArchiveAdsReq = () => ({
type: FETCH_ARCHIVE_ADS_REQ,
});

export const setArchiveAdsError = (payload) => ({
type: FETCH_ARCHIVE_ADS_ERR,
payload,
});

export const setArchiveAds = (payload) => ({
type: FETCH_ARCHIVE_ADS_SUCCESS,
payload,
});

+ 1
- 1
src/store/actions/login/loginActionConstants.js Целия файл

@@ -24,7 +24,7 @@ export const LOGIN_GOOGLE_USER_ERROR = createErrorType(LOGIN_GOOGLE_USER_SCOPE);
export const CLEAR_LOGIN_GOOGLE_USER_ERROR = createClearType(
`${LOGIN_GOOGLE_USER_SCOPE}_ERROR`,
);
export const LOGIN_GOOGLE_USER_LOADING = createLoadingType(LOGIN_USER_SCOPE);
export const LOGIN_GOOGLE_USER_LOADING = createLoadingType(LOGIN_GOOGLE_USER_SCOPE);


export const UPDATE_USER_JWT_TOKEN = 'UPDATE_USER_JWT_TOKEN';

+ 9
- 1
src/store/actions/users/usersActionConstants.js Целия файл

@@ -1,3 +1,11 @@
export const FETCH_USERS_REQ = 'FETCH_USERS_REQ';
export const FETCH_USERS_ERR = 'FETCH_USERS_ERR';
export const FETCH_USERS_SUCCESS = 'FETCH_USERS_SUCCESS';
export const FETCH_USERS_SUCCESS = 'FETCH_USERS_SUCCESS';

export const TOGGLE_USER_ENABLE_REQ = 'TOGGLE_USER_ENABLE_REQ';
export const TOGGLE_USER_ENABLE_ERR = 'TOGGLE_USER_ENABLE_ERR';
export const TOGGLE_USER_ENABLE_SUCCESS = 'TOGGLE_USER_ENABLE_SUCCESS';

export const DELETE_USER_REQ = 'DELETE_USER_REQ';
export const DELETE_USER_ERR = 'DELETE_USER_ERR';
export const DELETE_USER_SUCCESS = 'DELETE_USER_SUCCESS';

+ 54
- 19
src/store/actions/users/usersActions.js Целия файл

@@ -1,20 +1,55 @@
import {
FETCH_USERS_ERR,
FETCH_USERS_REQ,
FETCH_USERS_SUCCESS,
} from './usersActionConstants';
export const setUsersReq = () => ({
type: FETCH_USERS_REQ,
});

export const setUsersError = (payload) => ({
type: FETCH_USERS_ERR,
payload,
});
export const setUsers = (payload) => ({
type: FETCH_USERS_SUCCESS,
payload,
});
DELETE_USER_ERR,
DELETE_USER_REQ,
DELETE_USER_SUCCESS,
FETCH_USERS_ERR,
FETCH_USERS_REQ,
FETCH_USERS_SUCCESS,
TOGGLE_USER_ENABLE_ERR,
TOGGLE_USER_ENABLE_REQ,
TOGGLE_USER_ENABLE_SUCCESS,
} from "./usersActionConstants";

export const setUsersReq = () => ({
type: FETCH_USERS_REQ,
});

export const setUsersError = (payload) => ({
type: FETCH_USERS_ERR,
payload,
});

export const setUsers = (payload) => ({
type: FETCH_USERS_SUCCESS,
payload,
});

export const setEnableUsersReq = (payload) => ({
type: TOGGLE_USER_ENABLE_REQ,
payload,
});

export const setEnableUsersError = (payload) => ({
type: TOGGLE_USER_ENABLE_ERR,
payload,
});

export const setEnableUsers = (payload) => ({
type: TOGGLE_USER_ENABLE_SUCCESS,
payload,
});

export const deleteUserReq = (payload) => ({
type: DELETE_USER_REQ,
payload,
});

export const deleteUserError = (payload) => ({
type: DELETE_USER_ERR,
payload,
});

export const deleteStateUser = (payload) => ({
type: DELETE_USER_SUCCESS,
payload,
});

+ 26
- 0
src/store/reducers/ad/adReducer.js Целия файл

@@ -0,0 +1,26 @@
import {
FETCH_AD_ERR,
FETCH_AD_SUCCESS,
} from "../../actions/ad/adActionConstants";
import createReducer from "../../utils/createReducer";

const initialState = {
ad: null,
errorMessage: "",
};

export default createReducer(
{
[FETCH_AD_SUCCESS]: setStateAd,
[FETCH_AD_ERR]: setStateErrorMessage,
},
initialState
);

function setStateAd(state, action) {
return { ...state, ad: action.payload };
}

function setStateErrorMessage(state, action) {
return { ...state, errorMessage: action.payload };
}

+ 32
- 0
src/store/reducers/ad/adsReducer.js Целия файл

@@ -0,0 +1,32 @@
import {
FETCH_ADS_ERR,
FETCH_ADS_SUCCESS,
} from "../../actions/ads/adsActionConstants";
import createReducer from "../../utils/createReducer";

const initialState = {
ads: [],
errorMessage: "",
};

export default createReducer(
{
[FETCH_ADS_SUCCESS]: setStateAds,
[FETCH_ADS_ERR]: setAdsErrorMessage,
},
initialState
);

function setStateAds(state, action) {
return {
...state,
ads: action.payload,
};
}

function setAdsErrorMessage(state, action) {
return {
...state,
errorMessage: action.payload,
};
}

+ 32
- 0
src/store/reducers/ad/archiveAdsReducer.js Целия файл

@@ -0,0 +1,32 @@
import {
FETCH_ARCHIVE_ADS_SUCCESS,
FETCH_ARCHIVE_ADS_ERR,
} from "../../actions/archiveAds/archiveAdsActionConstants";
import createReducer from "../../utils/createReducer";

const initialState = {
archiveAds: [],
errorMessage: "",
};

export default createReducer(
{
[FETCH_ARCHIVE_ADS_SUCCESS]: setStateArchiveAds,
[FETCH_ARCHIVE_ADS_ERR]: setArchiveAdsError,
},
initialState
);

function setStateArchiveAds(state, action) {
return {
...state,
archiveAds: action.payload,
};
}

function setArchiveAdsError(state, action) {
return {
...state,
errorMessage: action.payload,
};
}

+ 21
- 1
src/store/reducers/index.js Целия файл

@@ -1,3 +1,4 @@
<<<<<<< HEAD
import { combineReducers } from 'redux';
import loginReducer from './login/loginReducer';
import loadingReducer from './loading/loadingReducer';
@@ -6,13 +7,32 @@ import randomDataReducer from './randomData/randomDataReducer';
import usersReducer from './user/usersReducer';
import candidatesReducer from './candidates/candidatesReducer';
import candidateReducer from './candidate/candidateReducer';
=======
import { combineReducers } from "redux";
import loginReducer from "./login/loginReducer";
import loadingReducer from "./loading/loadingReducer";
import userReducer from "./user/userReducer";
import randomDataReducer from "./randomData/randomDataReducer";
import usersReducer from "./user/usersReducer";
import adsReducer from "./ad/adsReducer";
import adReducer from "./ad/adReducer";
import archiveAdsReducer from "./ad/archiveAdsReducer";
import candidatesReducer from "./candidates/candidatesReducer";
>>>>>>> FE_dev

export default combineReducers({
login: loginReducer,
user: userReducer,
loading:loadingReducer,
loading: loadingReducer,
randomData: randomDataReducer,
users: usersReducer,
<<<<<<< HEAD
candidates:candidatesReducer,
candidate:candidateReducer
=======
ads: adsReducer,
ad: adReducer,
archiveAds: archiveAdsReducer,
candidates: candidatesReducer,
>>>>>>> FE_dev
});

+ 41
- 2
src/store/reducers/user/usersReducer.js Целия файл

@@ -1,19 +1,28 @@
import createReducer from "../../utils/createReducer";
import {
DELETE_USER_ERR,
DELETE_USER_SUCCESS,
FETCH_USERS_ERR,
FETCH_USERS_SUCCESS,
TOGGLE_USER_ENABLE_ERR,
TOGGLE_USER_ENABLE_SUCCESS,
} from "../../actions/users/usersActionConstants";
// import { setUsersError } from "../../actions/users/usersActions";

const initialState = {
users: [],
errorMessage: "",
fetchUsersErrorMessage: "",
toggleEnableErrorMessage: ""
};

export default createReducer(
{
[FETCH_USERS_SUCCESS]: setStateUsers,
[FETCH_USERS_ERR]: setUsersErrorMessage,
[TOGGLE_USER_ENABLE_SUCCESS]: toggleEnableStateUsers,
[TOGGLE_USER_ENABLE_ERR]: setToggleEnableErrorMessage,
[DELETE_USER_SUCCESS]: deleteStateUser,
[DELETE_USER_ERR]: setDeleteUserErrorMessage,
},
initialState
);
@@ -28,6 +37,36 @@ function setStateUsers(state, action) {
function setUsersErrorMessage(state, action) {
return {
...state,
errorMessage: action.payload,
fetchUsersErrorMessage: action.payload,
};
}

function toggleEnableStateUsers(state, action) {
let newUsers = state.users.map(n => n.id === action.payload ? {...n, isEnabled: !n.isEnabled} : n)
return {
...state,
users: newUsers,
};
}

function setToggleEnableErrorMessage(state, action) {
return {
...state,
toggleEnableErrorMessage: action.payload,
};
}

function deleteStateUser(state, action) {
let newUsers = state.users.filter(n => n.id !== action.payload)
return {
...state,
users: newUsers,
};
}

function setDeleteUserErrorMessage(state, action) {
return {
...state,
toggleEnableErrorMessage: action.payload,
};
}

+ 50
- 0
src/store/saga/adsSaga.js Целия файл

@@ -0,0 +1,50 @@
import { all, call, put, takeLatest } from "redux-saga/effects";
import {
getAllAds,
getAdDetailsById,
getAllArchiveAds,
} from "../../request/adsRequest";
import { setAds, setAdsError } from "../actions/ads/adsAction";
import { setAd, setAdError } from "../actions/ad/adActions";
import {
setArchiveAds,
setArchiveAdsError,
} from "../actions/archiveAds/archiveAdsActions";
import { FETCH_ADS_REQ } from "../actions/ads/adsActionConstants";
import { FETCH_AD_REQ } from "../actions/ad/adActionConstants";
import { FETCH_ARCHIVE_ADS_REQ } from "../actions/archiveAds/archiveAdsActionConstants";

export function* getAds() {
try {
const result = yield call(getAllAds);
yield put(setAds(result.data));
} catch (error) {
yield put(setAdsError(error));
}
}

export function* getAd({ payload }) {
try {
const result = yield call(getAdDetailsById, payload.id);
yield put(setAd(result.data));
} catch (error) {
yield put(setAdError(error));
}
}

export function* getArchiveAds() {
try {
const result = yield call(getAllArchiveAds);
yield put(setArchiveAds(result.data));
} catch (error) {
yield put(setArchiveAdsError(error));
}
}

export default function* adsSaga() {
yield all([
takeLatest(FETCH_ADS_REQ, getAds),
takeLatest(FETCH_AD_REQ, getAd),
takeLatest(FETCH_ARCHIVE_ADS_REQ, getArchiveAds),
]);
}

+ 5
- 3
src/store/saga/index.js Целия файл

@@ -1,12 +1,14 @@
import { all } from 'redux-saga/effects';
import { all } from "redux-saga/effects";
import adsSaga from "./adsSaga";
import candidatesSaga from './candidatesSaga';
import loginSaga from './loginSaga';
import usersSaga from './usersSaga';
import loginSaga from "./loginSaga";
import usersSaga from "./usersSaga";

export default function* rootSaga() {
yield all([
loginSaga(),
usersSaga(),
adsSaga(),
candidatesSaga()
]);
}

+ 6
- 2
src/store/saga/loginSaga.js Целия файл

@@ -76,6 +76,7 @@ function* fetchUser({ payload }) {

function* forgetPassword({ payload }) {
try {
// console.log(payload)
const { data } = yield call(forgetPasswordEmailSend, payload);
yield put(forgetPasswordSuccess(data));
if (payload.handleApiResponseSuccess) {
@@ -90,13 +91,16 @@ function* forgetPassword({ payload }) {
yield put(fetchUserError(errorMessage));
}
}
finally{
console.log('Done')
}
}

function* resetPassword({ payload }) {
try {
console.log(payload)
// console.log(payload)
const { data } = yield call(sendResetPassword, payload);
console.log(data);
// console.log(data);
yield put(forgetPasswordSuccess(data));
if (payload.handleApiResponseSuccess) {
yield call(payload.handleApiResponseSuccess);

+ 59
- 6
src/store/saga/usersSaga.js Целия файл

@@ -1,23 +1,76 @@
import { all, call, put, takeEvery } from "redux-saga/effects";
import { JWT_TOKEN } from "../../constants/localStorage";
import { addHeaderToken } from "../../request";
import { getAllUsers } from "../../request/usersRequest";
import {
deleteUserRequest,
enableUserRequest,
getAllUsers,
} from "../../request/usersRequest";
import { authScopeStringGetHelper } from "../../util/helpers/authScopeHelpers";
import { FETCH_USERS_REQ } from "../actions/users/usersActionConstants";
import { setUsers, setUsersError } from "../actions/users/usersActions";
import { rejectErrorCodeHelper } from "../../util/helpers/rejectErrorCodeHelper";
import {
DELETE_USER_REQ,
FETCH_USERS_REQ,
TOGGLE_USER_ENABLE_REQ,
} from "../actions/users/usersActionConstants";
import {
deleteStateUser,
deleteUserError,
setEnableUsers,
setEnableUsersError,
setUsers,
setUsersError,
} from "../actions/users/usersActions";

export function* getUsers() {
try {
const JwtToken = yield call(authScopeStringGetHelper, JWT_TOKEN);
yield call(addHeaderToken, JwtToken);
const result = yield call(getAllUsers);
console.log(result.data)
yield put(setUsers(result.data));
} catch (error) {
yield put(setUsersError(error))
if (error.response && error.response.data) {
const errorMessage = yield call(rejectErrorCodeHelper, error);
yield put(setUsersError(errorMessage));
}
}
}

export function* enableUser({ payload }) {
try {
const result = yield call(enableUserRequest, payload.id);
yield put(setEnableUsers(result.data));
} catch (error) {
if (error.response && error.response.data) {
const errorMessage = yield call(rejectErrorCodeHelper, error);
yield put(setEnableUsersError(errorMessage));
}
}
}

export function* deleteUser({ payload }) {
try {
const result = yield call(deleteUserRequest, payload.id);

// linija koda ispod nece biti potrebna
// jer nakon brisanja svakako idemo na
// users page gde se setuje state ponovo sa novim vrednostima
yield put(deleteStateUser(result.data));
if(payload.handleApiResponseSuccess){
yield call(payload.handleApiResponseSuccess)
}
} catch (error) {
if (error.response && error.response.data) {
const errorMessage = yield call(rejectErrorCodeHelper, error);
yield put(deleteUserError(errorMessage));
}
}
}

export default function* usersSaga() {
yield all([takeEvery(FETCH_USERS_REQ, getUsers)]);
yield all([
takeEvery(FETCH_USERS_REQ, getUsers),
takeEvery(TOGGLE_USER_ENABLE_REQ, enableUser),
takeEvery(DELETE_USER_REQ, deleteUser),
]);
}

+ 10
- 0
src/store/selectors/adSelectors.js Целия файл

@@ -0,0 +1,10 @@
import { createSelector } from "@reduxjs/toolkit";

export const adSelector = (state) => state.ad;

export const selectAd = createSelector(adSelector, (state) => state.ad);

export const selectAdError = createSelector(
adSelector,
(state) => state.errorMessage
);

+ 10
- 0
src/store/selectors/adsSelectors.js Целия файл

@@ -0,0 +1,10 @@
import { createSelector } from "@reduxjs/toolkit";

export const adsSelector = (state) => state.ads;

export const selectAds = createSelector(adsSelector, (state) => state.ads);

export const selectAdsError = createSelector(
adsSelector,
(state) => state.errorMessage
);

+ 13
- 0
src/store/selectors/archiveAdsSelectors.js Целия файл

@@ -0,0 +1,13 @@
import { createSelector } from "@reduxjs/toolkit";

export const archiveAdsSelector = (state) => state.archiveAds;

export const selectArchiveAds = createSelector(
archiveAdsSelector,
(state) => state.archiveAds
);

export const selectArchiveAdsError = createSelector(
archiveAdsSelector,
(state) => state.errorMessage
);

+ 91
- 53
yarn.lock Целия файл

@@ -28,7 +28,7 @@
"resolved" "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.14.4.tgz"
"version" "7.14.4"

"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.1.0", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.4.0-0", "@babel/core@^7.7.5", "@babel/core@^7.8.4", "@babel/core@7 || ^7.0.0-rc.2":
"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.5", "@babel/core@^7.8.4":
"integrity" "sha512-jB5AmTKOCSJIZ72sd78ECEhuPiDMKlQdDI/4QRI6lzYATx5SSogS1oQA2AoPecRCknm30gHi2l+QVvNUu3wZAg=="
"resolved" "https://registry.npmjs.org/@babel/core/-/core-7.14.3.tgz"
"version" "7.14.3"
@@ -1313,7 +1313,7 @@
"resolved" "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz"
"version" "0.8.0"

"@emotion/react@^11.0.0-rc.0", "@emotion/react@^11.1.1", "@emotion/react@^11.4.1", "@emotion/react@^11.5.0":
"@emotion/react@^11.1.1", "@emotion/react@^11.5.0":
"integrity" "sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA=="
"resolved" "https://registry.npmjs.org/@emotion/react/-/react-11.10.4.tgz"
"version" "11.10.4"
@@ -1649,7 +1649,7 @@
dependencies:
"@babel/runtime" "^7.19.0"

"@mui/material@^5.0.0", "@mui/material@^5.0.6", "@mui/material@^5.4.1":
"@mui/material@^5.0.6":
"integrity" "sha512-ioLvqY7VvcePz9dnEIRhpiVvtJmAFmvG6rtLXXzVdMmAVbSaelr5Io07mPz/mCyqE+Uv8/4EuJV276DWO7etzA=="
"resolved" "https://registry.npmjs.org/@mui/material/-/material-5.10.10.tgz"
"version" "5.10.10"
@@ -1686,7 +1686,7 @@
"csstype" "^3.1.1"
"prop-types" "^15.8.1"

"@mui/system@^5.10.10", "@mui/system@^5.4.1":
"@mui/system@^5.10.10":
"integrity" "sha512-TXwtKN0adKpBrZmO+eilQWoPf2veh050HLYrN78Kps9OhlvO70v/2Kya0+mORFhu9yhpAwjHXO8JII/R4a5ZLA=="
"resolved" "https://registry.npmjs.org/@mui/system/-/system-5.10.10.tgz"
"version" "5.10.10"
@@ -1992,7 +1992,7 @@
dependencies:
"defer-to-connect" "^1.0.1"

"@testing-library/dom@^8.0.0", "@testing-library/dom@>=7.21.4":
"@testing-library/dom@^8.0.0":
"integrity" "sha512-6YWYPPpxG3e/xOo6HIWwB/58HukkwIVTOaZ0VwdMVjhRUX/01E4FtQbck9GazOOj7MXHc5RBzMrU86iBJHbI+A=="
"resolved" "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.0.tgz"
"version" "8.19.0"
@@ -2233,7 +2233,7 @@
dependencies:
"@types/react" "*"

"@types/react@*", "@types/react@^17.0.0 || ^18.0.0":
"@types/react@*":
"integrity" "sha512-7QUCOxvFgnD5Jk8ZKlUAhVcRj7GuJRjnjjiY/IUBWKgOlnvDvTMLD4RTF7NPyVmbRhNrbomZiOepg7M/2Kj1mA=="
"resolved" "https://registry.npmjs.org/@types/react/-/react-18.0.21.tgz"
"version" "18.0.21"
@@ -2301,7 +2301,7 @@
"@types/source-list-map" "*"
"source-map" "^0.7.3"

"@types/webpack@^4.41.8", "@types/webpack@4.x":
"@types/webpack@^4.41.8":
"integrity" "sha512-6pLaORaVNZxiB3FSHbyBiWM7QdazAWda1zvAq4SbZObZqHSDbWLi62iFdblVea6SK9eyBIVp5yHhKt/yNQdR7Q=="
"resolved" "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.29.tgz"
"version" "4.41.29"
@@ -2325,7 +2325,7 @@
dependencies:
"@types/yargs-parser" "*"

"@typescript-eslint/eslint-plugin@^4.0.0", "@typescript-eslint/eslint-plugin@^4.5.0", "@typescript-eslint/eslint-plugin@>= 4":
"@typescript-eslint/eslint-plugin@^4.5.0":
"integrity" "sha512-yA7IWp+5Qqf+TLbd8b35ySFOFzUfL7i+4If50EqvjT6w35X8Lv0eBHb6rATeWmucks37w+zV+tWnOXI9JlG6Eg=="
"resolved" "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.0.tgz"
"version" "4.26.0"
@@ -2362,7 +2362,7 @@
"eslint-scope" "^5.1.1"
"eslint-utils" "^3.0.0"

"@typescript-eslint/parser@^4.0.0", "@typescript-eslint/parser@^4.5.0":
"@typescript-eslint/parser@^4.5.0":
"integrity" "sha512-b4jekVJG9FfmjUfmM4VoOItQhPlnt6MPOBUL0AQbiTmm+SSpSdhHYlwayOm4IW9KLI/4/cRKtQCmDl1oE2OlPg=="
"resolved" "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.26.0.tgz"
"version" "4.26.0"
@@ -2618,16 +2618,16 @@
"resolved" "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz"
"version" "7.2.0"

"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^7.1.0", "acorn@^7.1.1", "acorn@^7.4.0":
"integrity" "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
"resolved" "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
"version" "7.4.1"

"acorn@^6.4.1":
"integrity" "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="
"resolved" "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz"
"version" "6.4.2"

"acorn@^7.1.0", "acorn@^7.1.1", "acorn@^7.4.0":
"integrity" "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
"resolved" "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
"version" "7.4.1"

"acorn@^8.2.4":
"integrity" "sha512-tqPKHZ5CaBJw0Xmy0ZZvLs1qTV+BNFSyvn77ASXkpBNfIRk8ev26fKrD9iLGwGA9zedPao52GSHzq8lyZG0NUw=="
"resolved" "https://registry.npmjs.org/acorn/-/acorn-8.3.0.tgz"
@@ -2671,7 +2671,7 @@
"resolved" "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz"
"version" "3.5.2"

"ajv@^6.1.0", "ajv@^6.10.0", "ajv@^6.10.2", "ajv@^6.12.4", "ajv@^6.12.5", "ajv@^6.9.1", "ajv@>=5.0.0":
"ajv@^6.1.0", "ajv@^6.10.0", "ajv@^6.10.2", "ajv@^6.12.4", "ajv@^6.12.5":
"integrity" "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="
"resolved" "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
"version" "6.12.6"
@@ -2994,7 +2994,7 @@
"resolved" "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz"
"version" "2.2.0"

"babel-eslint@^10.0.0", "babel-eslint@^10.1.0":
"babel-eslint@^10.1.0":
"integrity" "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg=="
"resolved" "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz"
"version" "10.1.0"
@@ -3425,7 +3425,7 @@
dependencies:
"pako" "~1.0.5"

"browserslist@^4", "browserslist@^4.0.0", "browserslist@^4.12.0", "browserslist@^4.16.6", "browserslist@^4.6.2", "browserslist@^4.6.4":
"browserslist@^4.0.0", "browserslist@^4.12.0", "browserslist@^4.16.6", "browserslist@^4.6.2", "browserslist@^4.6.4":
"integrity" "sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ=="
"resolved" "https://registry.npmjs.org/browserslist/-/browserslist-4.16.6.tgz"
"version" "4.16.6"
@@ -3789,6 +3789,11 @@
"isobject" "^3.0.0"
"static-extend" "^0.1.1"

"classnames@^2.2.5":
"integrity" "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
"resolved" "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz"
"version" "2.3.2"

"clean-css@^4.2.3":
"integrity" "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA=="
"resolved" "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz"
@@ -4985,6 +4990,11 @@
"memory-fs" "^0.5.0"
"tapable" "^1.0.0"

"enquire.js@^2.1.6":
"integrity" "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw=="
"resolved" "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz"
"version" "2.1.6"

"enquirer@^2.3.5":
"integrity" "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg=="
"resolved" "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz"
@@ -5211,7 +5221,7 @@
"lodash" "^4.17.15"
"string-natural-compare" "^3.0.1"

"eslint-plugin-import@^2.22.0", "eslint-plugin-import@^2.22.1", "eslint-plugin-import@^2.23.4":
"eslint-plugin-import@^2.22.1", "eslint-plugin-import@^2.23.4":
"integrity" "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ=="
"resolved" "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz"
"version" "2.23.4"
@@ -5232,7 +5242,7 @@
"resolve" "^1.20.0"
"tsconfig-paths" "^3.9.0"

"eslint-plugin-jest@^24.0.0", "eslint-plugin-jest@^24.1.0":
"eslint-plugin-jest@^24.1.0":
"integrity" "sha512-WOVH4TIaBLIeCX576rLcOgjNXqP+jNlCiEmRgFTfQtJ52DpwnIQKAVGlGPAN7CZ33bW6eNfHD6s8ZbEUTQubJg=="
"resolved" "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-24.3.6.tgz"
"version" "24.3.6"
@@ -5263,12 +5273,12 @@
dependencies:
"prettier-linter-helpers" "^1.0.0"

"eslint-plugin-react-hooks@^4 || ^3 || ^2.3.0 || ^1.7.0", "eslint-plugin-react-hooks@^4.0.8", "eslint-plugin-react-hooks@^4.2.0":
"eslint-plugin-react-hooks@^4.2.0":
"integrity" "sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ=="
"resolved" "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz"
"version" "4.2.0"

"eslint-plugin-react@^7.20.3", "eslint-plugin-react@^7.21.5", "eslint-plugin-react@^7.24.0":
"eslint-plugin-react@^7.21.5", "eslint-plugin-react@^7.24.0":
"integrity" "sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q=="
"resolved" "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz"
"version" "7.24.0"
@@ -5293,7 +5303,7 @@
dependencies:
"safe-regex" "^1.1.0"

"eslint-plugin-testing-library@^3.9.0", "eslint-plugin-testing-library@^3.9.2":
"eslint-plugin-testing-library@^3.9.2":
"integrity" "sha512-WAmOCt7EbF1XM8XfbCKAEzAPnShkNSwcIsAD2jHdsMUT9mZJPjLCG7pMzbcC8kK366NOuGip8HKLDC+Xk4yIdA=="
"resolved" "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-3.10.2.tgz"
"version" "3.10.2"
@@ -5369,7 +5379,7 @@
"normalize-path" "^3.0.0"
"schema-utils" "^3.0.0"

"eslint@*", "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0", "eslint@^3 || ^4 || ^5 || ^6 || ^7", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", "eslint@^5 || ^6 || ^7", "eslint@^5.0.0 || ^6.0.0 || ^7.0.0", "eslint@^5.16.0 || ^6.8.0 || ^7.2.0", "eslint@^7.0.0", "eslint@^7.1.0", "eslint@^7.11.0", "eslint@^7.28.0", "eslint@^7.5.0", "eslint@>= 4.12.1", "eslint@>=5", "eslint@>=5.0.0", "eslint@>=7.0.0":
"eslint@^7.11.0", "eslint@^7.28.0":
"integrity" "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA=="
"resolved" "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz"
"version" "7.32.0"
@@ -5716,7 +5726,7 @@
dependencies:
"flat-cache" "^3.0.4"

"file-loader@*", "file-loader@6.1.1":
"file-loader@6.1.1":
"integrity" "sha512-Klt8C4BjWSXYQAfhpYYkG4qHNTna4toMHEbWrI5IuVoxbU6uiDKeKAP99R8mmbJi3lvewn/jQBOgU4+NS3tDQw=="
"resolved" "https://registry.npmjs.org/file-loader/-/file-loader-6.1.1.tgz"
"version" "6.1.1"
@@ -6514,7 +6524,7 @@
"resolved" "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz"
"version" "1.1.1"

"i18next@^20.3.1", "i18next@>= 19.0.0":
"i18next@^20.3.1":
"integrity" "sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A=="
"resolved" "https://registry.npmjs.org/i18next/-/i18next-20.6.1.tgz"
"version" "20.6.1"
@@ -7459,20 +7469,6 @@
"jest-regex-util" "^26.0.0"
"jest-snapshot" "^26.6.2"

"jest-resolve@*", "jest-resolve@26.6.0":
"integrity" "sha512-tRAz2bwraHufNp+CCmAD8ciyCpXCs1NQxB5EJAmtCFy6BN81loFEGWKzYu26Y62lAJJe4X4jg36Kf+NsQyiStQ=="
"resolved" "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.0.tgz"
"version" "26.6.0"
dependencies:
"@jest/types" "^26.6.0"
"chalk" "^4.0.0"
"graceful-fs" "^4.2.4"
"jest-pnp-resolver" "^1.2.2"
"jest-util" "^26.6.0"
"read-pkg-up" "^7.0.1"
"resolve" "^1.17.0"
"slash" "^3.0.0"

"jest-resolve@^26.6.2":
"integrity" "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ=="
"resolved" "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz"
@@ -7487,6 +7483,20 @@
"resolve" "^1.18.1"
"slash" "^3.0.0"

"jest-resolve@26.6.0":
"integrity" "sha512-tRAz2bwraHufNp+CCmAD8ciyCpXCs1NQxB5EJAmtCFy6BN81loFEGWKzYu26Y62lAJJe4X4jg36Kf+NsQyiStQ=="
"resolved" "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.0.tgz"
"version" "26.6.0"
dependencies:
"@jest/types" "^26.6.0"
"chalk" "^4.0.0"
"graceful-fs" "^4.2.4"
"jest-pnp-resolver" "^1.2.2"
"jest-util" "^26.6.0"
"read-pkg-up" "^7.0.1"
"resolve" "^1.17.0"
"slash" "^3.0.0"

"jest-runner@^26.6.0", "jest-runner@^26.6.3":
"integrity" "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ=="
"resolved" "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz"
@@ -7643,7 +7653,7 @@
"merge-stream" "^2.0.0"
"supports-color" "^7.0.0"

"jest@^26.0.0", "jest@^26.6.0", "jest@26.6.0":
"jest@^26.6.0", "jest@26.6.0":
"integrity" "sha512-jxTmrvuecVISvKFFhOkjsWRZV7sFqdSUAd1ajOKY+/QE/aLBVstsJ/dX8GczLzwiT6ZEwwmZqtCUHLHHQVzcfA=="
"resolved" "https://registry.npmjs.org/jest/-/jest-26.6.0.tgz"
"version" "26.6.0"
@@ -7776,6 +7786,13 @@
"resolved" "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz"
"version" "1.0.1"

"json2mq@^0.2.0":
"integrity" "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA=="
"resolved" "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz"
"version" "0.2.0"
dependencies:
"string-convert" "^0.2.0"

"json3@^3.3.3":
"integrity" "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA=="
"resolved" "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz"
@@ -10050,7 +10067,7 @@
dependencies:
"fast-diff" "^1.1.2"

"prettier@>=1.13.0", "prettier@2.3.1":
"prettier@2.3.1":
"integrity" "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA=="
"resolved" "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz"
"version" "2.3.1"
@@ -10442,7 +10459,7 @@
"prop-types" "^15.7.2"
"react-is" "^17.0.2"

"react-refresh@^0.8.3", "react-refresh@>=0.8.3 <0.10.0":
"react-refresh@^0.8.3":
"integrity" "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg=="
"resolved" "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz"
"version" "0.8.3"
@@ -10562,6 +10579,17 @@
"object-assign" "^4.1.1"
"react-is" "^16.12.0 || ^17.0.0 || ^18.0.0"

"react-slick@^0.29.0":
"integrity" "sha512-TGdOKE+ZkJHHeC4aaoH85m8RnFyWqdqRfAGkhd6dirmATXMZWAxOpTLmw2Ll/jPTQ3eEG7ercFr/sbzdeYCJXA=="
"resolved" "https://registry.npmjs.org/react-slick/-/react-slick-0.29.0.tgz"
"version" "0.29.0"
dependencies:
"classnames" "^2.2.5"
"enquire.js" "^2.1.6"
"json2mq" "^0.2.0"
"lodash.debounce" "^4.0.8"
"resize-observer-polyfill" "^1.5.0"

"react-test-renderer@^18.2.0":
"integrity" "sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA=="
"resolved" "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz"
@@ -10794,7 +10822,7 @@
"resolved" "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz"
"version" "2.4.1"

"redux@^4", "redux@^4.0.0", "redux@^4.0.4", "redux@^4.1.0", "redux@^4.1.2":
"redux@^4.0.0", "redux@^4.0.4", "redux@^4.1.0", "redux@^4.1.2":
"integrity" "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA=="
"resolved" "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz"
"version" "4.2.0"
@@ -10951,6 +10979,11 @@
"resolved" "https://registry.npmjs.org/reselect/-/reselect-4.1.6.tgz"
"version" "4.1.6"

"resize-observer-polyfill@^1.5.0":
"integrity" "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
"resolved" "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz"
"version" "1.5.1"

"resolve-cwd@^2.0.0":
"integrity" "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo="
"resolved" "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz"
@@ -11139,7 +11172,7 @@
dependencies:
"estree-walker" "^0.6.1"

"rollup@^1.20.0 || ^2.0.0", "rollup@^1.20.0||^2.0.0", "rollup@^1.31.1", "rollup@>=0.60.0 <3", "rollup@>=0.66.0 <3":
"rollup@^1.31.1":
"integrity" "sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A=="
"resolved" "https://registry.npmjs.org/rollup/-/rollup-1.32.1.tgz"
"version" "1.32.1"
@@ -11234,7 +11267,7 @@
"schema-utils" "^3.0.0"
"semver" "^7.3.2"

"sass@^1.3.0", "sass@^1.34.1":
"sass@^1.34.1":
"integrity" "sha512-Pk+PMy7OGLs9WaxZGJMn7S96dvlyVBwwtToX895WmCpAOr5YiJYEUJfiJidMuKb613z2xNWcXCHEuOvjZbqC6A=="
"resolved" "https://registry.npmjs.org/sass/-/sass-1.55.0.tgz"
"version" "1.55.0"
@@ -11568,6 +11601,11 @@
"astral-regex" "^2.0.0"
"is-fullwidth-code-point" "^3.0.0"

"slick-carousel@^1.8.1":
"integrity" "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA=="
"resolved" "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz"
"version" "1.8.1"

"snapdragon-node@^2.0.1":
"integrity" "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw=="
"resolved" "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz"
@@ -11598,7 +11636,7 @@
"source-map-resolve" "^0.5.0"
"use" "^3.1.0"

"sockjs-client@^1.4.0", "sockjs-client@^1.5.0":
"sockjs-client@^1.5.0":
"integrity" "sha512-VnVAb663fosipI/m6pqRXakEOw7nvd7TUgdr3PlR/8V2I95QIdwT8L4nMxhyU8SmDBHYXU1TOElaKOmKLfYzeQ=="
"resolved" "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.5.1.tgz"
"version" "1.5.1"
@@ -11871,6 +11909,11 @@
dependencies:
"safe-buffer" "~5.2.0"

"string-convert@^0.2.0":
"integrity" "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A=="
"resolved" "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz"
"version" "0.2.1"

"string-length@^4.0.1":
"integrity" "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ=="
"resolved" "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz"
@@ -12508,11 +12551,6 @@
dependencies:
"typescript-compare" "^0.0.2"

"typescript@^3.2.1 || ^4", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta":
"integrity" "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ=="
"resolved" "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz"
"version" "4.8.4"

"unbox-primitive@^1.0.2":
"integrity" "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw=="
"resolved" "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz"
@@ -12872,7 +12910,7 @@
"range-parser" "^1.2.1"
"webpack-log" "^2.0.0"

"webpack-dev-server@3.11.1", "webpack-dev-server@3.x":
"webpack-dev-server@3.11.1":
"integrity" "sha512-u4R3mRzZkbxQVa+MBWi2uVpB5W59H3ekZAJsQlKUTdl7Elcah2EhygTPLmeFXybQkf9i2+L0kn7ik9SnXa6ihQ=="
"resolved" "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.1.tgz"
"version" "3.11.1"
@@ -12937,7 +12975,7 @@
"source-list-map" "^2.0.0"
"source-map" "~0.6.1"

"webpack@^4.0.0", "webpack@^4.0.0 || ^5.0.0", "webpack@^4.27.0 || ^5.0.0", "webpack@^4.36.0 || ^5.0.0", "webpack@^4.4.0 || ^5.0.0", "webpack@>=2", "webpack@>=4.43.0 <6.0.0", "webpack@2 || 3 || 4", "webpack@4.44.2":
"webpack@4.44.2":
"integrity" "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q=="
"resolved" "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz"
"version" "4.44.2"

Loading…
Отказ
Запис