Merge branch 'reports'

This commit is contained in:
Дмитрий Кравцов 2022-05-27 14:55:54 +03:00
commit 6fefc29cae
93 changed files with 6274 additions and 3883 deletions

2
.envexample Normal file
View File

@ -0,0 +1,2 @@
REACT_APP_API_URL = https://dev.itguild.info
REACT_APP_BASE_URL = https://dev.itguild.info

5
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

6
.idea/misc.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="JSX" />
</component>
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/outstaffing-react.iml" filepath="$PROJECT_DIR$/.idea/outstaffing-react.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

655
package-lock.json generated
View File

@ -7,6 +7,7 @@
"": { "": {
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
<<<<<<< HEAD
"@babel/core": "7.12.3", "@babel/core": "7.12.3",
"@pmmmwh/react-refresh-webpack-plugin": "0.4.3", "@pmmmwh/react-refresh-webpack-plugin": "0.4.3",
"@reduxjs/toolkit": "^1.6.0", "@reduxjs/toolkit": "^1.6.0",
@ -62,12 +63,27 @@
"react-app-polyfill": "^2.0.0", "react-app-polyfill": "^2.0.0",
"react-bootstrap": "^1.6.0", "react-bootstrap": "^1.6.0",
"react-dev-utils": "^11.0.3", "react-dev-utils": "^11.0.3",
=======
"@reduxjs/toolkit": "^1.6.0",
"@testing-library/jest-dom": "^5.12.0",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"axios": "^0.21.1",
"bootstrap": "^4.6.0",
"dotenv": "^10.0.0",
"eslint": "^7.27.0",
"form-data": "^4.0.0",
"moment": "^2.29.1",
"react": "^17.0.2",
"react-bootstrap": "^1.6.0",
>>>>>>> reports
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-inlinesvg": "^2.3.0", "react-inlinesvg": "^2.3.0",
"react-loader-spinner": "^4.0.0", "react-loader-spinner": "^4.0.0",
"react-outside-click-handler": "^1.3.0", "react-outside-click-handler": "^1.3.0",
"react-phone-input-2": "^2.14.0", "react-phone-input-2": "^2.14.0",
"react-redux": "^7.2.4", "react-redux": "^7.2.4",
<<<<<<< HEAD
"react-refresh": "^0.8.3", "react-refresh": "^0.8.3",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-select": "^4.3.1", "react-select": "^4.3.1",
@ -88,6 +104,18 @@
"webpack-dev-server": "3.11.1", "webpack-dev-server": "3.11.1",
"webpack-manifest-plugin": "2.2.0", "webpack-manifest-plugin": "2.2.0",
"workbox-webpack-plugin": "5.1.4" "workbox-webpack-plugin": "5.1.4"
=======
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"react-select": "^4.3.1",
"react-syntax-highlighter": "^15.4.5",
"react-yandex-metrika": "^2.6.0",
"redux-devtools-extension": "^2.13.9",
"sass": "^1.34.0",
"sass-loader": "^11.1.1",
"sweetalert2-react": "^0.8.3",
"web-vitals": "^1.1.2"
>>>>>>> reports
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
@ -3215,6 +3243,17 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/@types/hast": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz",
"integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==",
"dependencies": {
"@types/unist": "*"
}
},
>>>>>>> reports
"node_modules/@types/hoist-non-react-statics": { "node_modules/@types/hoist-non-react-statics": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
@ -3265,9 +3304,15 @@
} }
}, },
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
<<<<<<< HEAD
"version": "7.0.9", "version": "7.0.9",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
"integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="
=======
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
"integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA=="
>>>>>>> reports
}, },
"node_modules/@types/json5": { "node_modules/@types/json5": {
"version": "0.0.29", "version": "0.0.29",
@ -3390,6 +3435,14 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/@types/unist": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
"integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ=="
},
>>>>>>> reports
"node_modules/@types/warning": { "node_modules/@types/warning": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
@ -5443,6 +5496,36 @@
"node": ">=10" "node": ">=10"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/character-entities": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
"integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/character-entities-legacy": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
"integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/character-reference-invalid": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
"integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
>>>>>>> reports
"node_modules/check-types": { "node_modules/check-types": {
"version": "11.1.2", "version": "11.1.2",
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz",
@ -5656,6 +5739,18 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/comma-separated-tokens": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
"integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
>>>>>>> reports
"node_modules/commander": { "node_modules/commander": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@ -6935,11 +7030,19 @@
} }
}, },
"node_modules/dotenv": { "node_modules/dotenv": {
<<<<<<< HEAD
"version": "8.2.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
"engines": { "engines": {
"node": ">=8" "node": ">=8"
=======
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
"engines": {
"node": ">=10"
>>>>>>> reports
} }
}, },
"node_modules/dotenv-expand": { "node_modules/dotenv-expand": {
@ -8611,6 +8714,21 @@
"reusify": "^1.0.4" "reusify": "^1.0.4"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/fault": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
"integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
"dependencies": {
"format": "^0.2.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
>>>>>>> reports
"node_modules/faye-websocket": { "node_modules/faye-websocket": {
"version": "0.11.4", "version": "0.11.4",
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
@ -8989,6 +9107,17 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/format": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
"integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=",
"engines": {
"node": ">=0.4.x"
}
},
>>>>>>> reports
"node_modules/forwarded": { "node_modules/forwarded": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@ -9470,6 +9599,34 @@
"minimalistic-assert": "^1.0.1" "minimalistic-assert": "^1.0.1"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/hast-util-parse-selector": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
"integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hastscript": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz",
"integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
"dependencies": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^1.0.0",
"hast-util-parse-selector": "^2.0.0",
"property-information": "^5.0.0",
"space-separated-tokens": "^1.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
>>>>>>> reports
"node_modules/he": { "node_modules/he": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@ -9483,6 +9640,17 @@
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
}, },
<<<<<<< HEAD
=======
"node_modules/highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
"engines": {
"node": "*"
}
},
>>>>>>> reports
"node_modules/history": { "node_modules/history": {
"version": "4.10.1", "version": "4.10.1",
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
@ -10166,6 +10334,31 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/is-alphabetical": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
"integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-alphanumerical": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
"integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
"dependencies": {
"is-alphabetical": "^1.0.0",
"is-decimal": "^1.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
>>>>>>> reports
"node_modules/is-arguments": { "node_modules/is-arguments": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz",
@ -10302,6 +10495,18 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/is-decimal": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
"integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
>>>>>>> reports
"node_modules/is-descriptor": { "node_modules/is-descriptor": {
"version": "0.1.6", "version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
@ -10388,6 +10593,18 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/is-hexadecimal": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
>>>>>>> reports
"node_modules/is-module": { "node_modules/is-module": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
@ -13108,6 +13325,22 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
}, },
<<<<<<< HEAD
=======
"node_modules/lowlight": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz",
"integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
"dependencies": {
"fault": "^1.0.0",
"highlight.js": "~10.7.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
>>>>>>> reports
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@ -14334,6 +14567,26 @@
"safe-buffer": "^5.1.1" "safe-buffer": "^5.1.1"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/parse-entities": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
"dependencies": {
"character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0",
"character-reference-invalid": "^1.0.0",
"is-alphanumerical": "^1.0.0",
"is-decimal": "^1.0.0",
"is-hexadecimal": "^1.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
>>>>>>> reports
"node_modules/parse-json": { "node_modules/parse-json": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@ -15963,6 +16216,17 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
}, },
<<<<<<< HEAD
=======
"node_modules/prismjs": {
"version": "1.26.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.26.0.tgz",
"integrity": "sha512-HUoH9C5Z3jKkl3UunCyiD5jwk0+Hz0fIgQ2nbwU2Oo/ceuTAQAg+pPVnfdt2TJWRVLcxKh9iuoYDUSc8clb5UQ==",
"engines": {
"node": ">=6"
}
},
>>>>>>> reports
"node_modules/process": { "node_modules/process": {
"version": "0.11.10", "version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -16041,6 +16305,21 @@
"react": ">=0.14.0" "react": ">=0.14.0"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/property-information": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
"integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
"dependencies": {
"xtend": "^4.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
>>>>>>> reports
"node_modules/proxy-addr": { "node_modules/proxy-addr": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
@ -16701,6 +16980,153 @@
"isarray": "0.0.1" "isarray": "0.0.1"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/react-scripts": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-4.0.3.tgz",
"integrity": "sha512-S5eO4vjUzUisvkIPB7jVsKtuH2HhWcASREYWHAQ1FP5HyCv3xgn+wpILAEWkmy+A+tTNbSZClhxjT3qz6g4L1A==",
"dependencies": {
"@babel/core": "7.12.3",
"@pmmmwh/react-refresh-webpack-plugin": "0.4.3",
"@svgr/webpack": "5.5.0",
"@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.6.0",
"babel-loader": "8.1.0",
"babel-plugin-named-asset-import": "^0.3.7",
"babel-preset-react-app": "^10.0.0",
"bfj": "^7.0.2",
"camelcase": "^6.1.0",
"case-sensitive-paths-webpack-plugin": "2.3.0",
"css-loader": "4.3.0",
"dotenv": "8.2.0",
"dotenv-expand": "5.1.0",
"eslint": "^7.11.0",
"eslint-config-react-app": "^6.0.0",
"eslint-plugin-flowtype": "^5.2.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.1.0",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-testing-library": "^3.9.2",
"eslint-webpack-plugin": "^2.5.2",
"file-loader": "6.1.1",
"fs-extra": "^9.0.1",
"html-webpack-plugin": "4.5.0",
"identity-obj-proxy": "3.0.0",
"jest": "26.6.0",
"jest-circus": "26.6.0",
"jest-resolve": "26.6.0",
"jest-watch-typeahead": "0.6.1",
"mini-css-extract-plugin": "0.11.3",
"optimize-css-assets-webpack-plugin": "5.0.4",
"pnp-webpack-plugin": "1.6.4",
"postcss-flexbugs-fixes": "4.2.1",
"postcss-loader": "3.0.0",
"postcss-normalize": "8.0.1",
"postcss-preset-env": "6.7.0",
"postcss-safe-parser": "5.0.2",
"prompts": "2.4.0",
"react-app-polyfill": "^2.0.0",
"react-dev-utils": "^11.0.3",
"react-refresh": "^0.8.3",
"resolve": "1.18.1",
"resolve-url-loader": "^3.1.2",
"sass-loader": "^10.0.5",
"semver": "7.3.2",
"style-loader": "1.3.0",
"terser-webpack-plugin": "4.2.3",
"ts-pnp": "1.2.0",
"url-loader": "4.1.1",
"webpack": "4.44.2",
"webpack-dev-server": "3.11.1",
"webpack-manifest-plugin": "2.2.0",
"workbox-webpack-plugin": "5.1.4"
},
"bin": {
"react-scripts": "bin/react-scripts.js"
},
"engines": {
"node": "^10.12.0 || >=12.0.0"
},
"optionalDependencies": {
"fsevents": "^2.1.3"
},
"peerDependencies": {
"react": ">= 16",
"typescript": "^3.2.1 || ^4"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/react-scripts/node_modules/dotenv": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
"engines": {
"node": ">=8"
}
},
"node_modules/react-scripts/node_modules/sass-loader": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.2.0.tgz",
"integrity": "sha512-kUceLzC1gIHz0zNJPpqRsJyisWatGYNFRmv2CKZK2/ngMJgLqxTbXwe/hJ85luyvZkgqU3VlJ33UVF2T/0g6mw==",
"dependencies": {
"klona": "^2.0.4",
"loader-utils": "^2.0.0",
"neo-async": "^2.6.2",
"schema-utils": "^3.0.0",
"semver": "^7.3.2"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
},
"peerDependencies": {
"fibers": ">= 3.1.0",
"node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0",
"sass": "^1.3.0",
"webpack": "^4.36.0 || ^5.0.0"
},
"peerDependenciesMeta": {
"fibers": {
"optional": true
},
"node-sass": {
"optional": true
},
"sass": {
"optional": true
}
}
},
"node_modules/react-scripts/node_modules/schema-utils": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
"integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
"dependencies": {
"@types/json-schema": "^7.0.6",
"ajv": "^6.12.5",
"ajv-keywords": "^3.5.2"
},
"engines": {
"node": ">= 10.13.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
>>>>>>> reports
"node_modules/react-select": { "node_modules/react-select": {
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-select/-/react-select-4.3.1.tgz", "resolved": "https://registry.npmjs.org/react-select/-/react-select-4.3.1.tgz",
@ -16719,6 +17145,24 @@
"react-dom": "^16.8.0 || ^17.0.0" "react-dom": "^16.8.0 || ^17.0.0"
} }
}, },
<<<<<<< HEAD
=======
"node_modules/react-syntax-highlighter": {
"version": "15.4.5",
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.5.tgz",
"integrity": "sha512-RC90KQTxZ/b7+9iE6s9nmiFLFjWswUcfULi4GwVzdFVKVMQySkJWBuOmJFfjwjMVCo0IUUuJrWebNKyviKpwLQ==",
"dependencies": {
"@babel/runtime": "^7.3.1",
"highlight.js": "^10.4.1",
"lowlight": "^1.17.0",
"prismjs": "^1.25.0",
"refractor": "^3.2.0"
},
"peerDependencies": {
"react": ">= 0.14.0"
}
},
>>>>>>> reports
"node_modules/react-transition-group": { "node_modules/react-transition-group": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
@ -16921,6 +17365,28 @@
"resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz", "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz",
"integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=" "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA="
}, },
<<<<<<< HEAD
=======
"node_modules/refractor": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.5.0.tgz",
"integrity": "sha512-QwPJd3ferTZ4cSPPjdP5bsYHMytwWYnAN5EEnLtGvkqp/FCCnGsBgxrm9EuIDnjUC3Uc/kETtvVi7fSIVC74Dg==",
"dependencies": {
"hastscript": "^6.0.0",
"parse-entities": "^2.0.0",
"prismjs": "~1.25.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/refractor/node_modules/prismjs": {
"version": "1.25.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz",
"integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg=="
},
>>>>>>> reports
"node_modules/regenerate": { "node_modules/regenerate": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@ -17661,6 +18127,7 @@
} }
}, },
"node_modules/sass-loader": { "node_modules/sass-loader": {
<<<<<<< HEAD
"version": "10.2.0", "version": "10.2.0",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.2.0.tgz", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.2.0.tgz",
"integrity": "sha512-kUceLzC1gIHz0zNJPpqRsJyisWatGYNFRmv2CKZK2/ngMJgLqxTbXwe/hJ85luyvZkgqU3VlJ33UVF2T/0g6mw==", "integrity": "sha512-kUceLzC1gIHz0zNJPpqRsJyisWatGYNFRmv2CKZK2/ngMJgLqxTbXwe/hJ85luyvZkgqU3VlJ33UVF2T/0g6mw==",
@ -17670,6 +18137,14 @@
"neo-async": "^2.6.2", "neo-async": "^2.6.2",
"schema-utils": "^3.0.0", "schema-utils": "^3.0.0",
"semver": "^7.3.2" "semver": "^7.3.2"
=======
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-11.1.1.tgz",
"integrity": "sha512-fOCp/zLmj1V1WHDZbUbPgrZhA7HKXHEqkslzB+05U5K9SbSbcmH91C7QLW31AsXikxUMaxXRhhcqWZAxUMLDyA==",
"dependencies": {
"klona": "^2.0.4",
"neo-async": "^2.6.2"
>>>>>>> reports
}, },
"engines": { "engines": {
"node": ">= 10.13.0" "node": ">= 10.13.0"
@ -17682,7 +18157,11 @@
"fibers": ">= 3.1.0", "fibers": ">= 3.1.0",
"node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0", "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0",
"sass": "^1.3.0", "sass": "^1.3.0",
<<<<<<< HEAD
"webpack": "^4.36.0 || ^5.0.0" "webpack": "^4.36.0 || ^5.0.0"
=======
"webpack": "^5.0.0"
>>>>>>> reports
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"fibers": { "fibers": {
@ -17696,6 +18175,7 @@
} }
} }
}, },
<<<<<<< HEAD
"node_modules/sass-loader/node_modules/schema-utils": { "node_modules/sass-loader/node_modules/schema-utils": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
@ -17713,6 +18193,8 @@
"url": "https://opencollective.com/webpack" "url": "https://opencollective.com/webpack"
} }
}, },
=======
>>>>>>> reports
"node_modules/sax": { "node_modules/sax": {
"version": "1.2.4", "version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@ -18313,6 +18795,18 @@
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
}, },
<<<<<<< HEAD
=======
"node_modules/space-separated-tokens": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
"integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
>>>>>>> reports
"node_modules/spdx-correct": { "node_modules/spdx-correct": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
@ -24133,6 +24627,14 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/hast": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz",
"integrity": "sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==",
"requires": {
"@types/unist": "*"
}
},
"@types/hoist-non-react-statics": { "@types/hoist-non-react-statics": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
@ -24307,6 +24809,11 @@
} }
} }
}, },
"@types/unist": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
"integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ=="
},
"@types/warning": { "@types/warning": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
@ -25926,6 +26433,21 @@
"resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
"integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="
}, },
"character-entities": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz",
"integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw=="
},
"character-entities-legacy": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz",
"integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA=="
},
"character-reference-invalid": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz",
"integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg=="
},
"check-types": { "check-types": {
"version": "11.1.2", "version": "11.1.2",
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz",
@ -26104,6 +26626,11 @@
"delayed-stream": "~1.0.0" "delayed-stream": "~1.0.0"
} }
}, },
"comma-separated-tokens": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz",
"integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw=="
},
"commander": { "commander": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@ -28423,6 +28950,14 @@
"reusify": "^1.0.4" "reusify": "^1.0.4"
} }
}, },
"fault": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
"integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
"requires": {
"format": "^0.2.0"
}
},
"faye-websocket": { "faye-websocket": {
"version": "0.11.4", "version": "0.11.4",
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
@ -28722,6 +29257,11 @@
"mime-types": "^2.1.12" "mime-types": "^2.1.12"
} }
}, },
"format": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
"integrity": "sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs="
},
"forwarded": { "forwarded": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
@ -29090,6 +29630,23 @@
"minimalistic-assert": "^1.0.1" "minimalistic-assert": "^1.0.1"
} }
}, },
"hast-util-parse-selector": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz",
"integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ=="
},
"hastscript": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz",
"integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==",
"requires": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^1.0.0",
"hast-util-parse-selector": "^2.0.0",
"property-information": "^5.0.0",
"space-separated-tokens": "^1.0.0"
}
},
"he": { "he": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
@ -29100,6 +29657,11 @@
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
}, },
"highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
},
"history": { "history": {
"version": "4.10.1", "version": "4.10.1",
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
@ -29658,6 +30220,20 @@
} }
} }
}, },
"is-alphabetical": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz",
"integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg=="
},
"is-alphanumerical": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz",
"integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==",
"requires": {
"is-alphabetical": "^1.0.0",
"is-decimal": "^1.0.0"
}
},
"is-arguments": { "is-arguments": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz",
@ -29754,6 +30330,11 @@
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz",
"integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==" "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A=="
}, },
"is-decimal": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz",
"integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw=="
},
"is-descriptor": { "is-descriptor": {
"version": "0.1.6", "version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
@ -29809,6 +30390,11 @@
"is-extglob": "^2.1.1" "is-extglob": "^2.1.1"
} }
}, },
"is-hexadecimal": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw=="
},
"is-module": { "is-module": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
@ -31827,6 +32413,15 @@
} }
} }
}, },
"lowlight": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz",
"integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
"requires": {
"fault": "^1.0.0",
"highlight.js": "~10.7.0"
}
},
"lru-cache": { "lru-cache": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@ -32811,6 +33406,19 @@
"safe-buffer": "^5.1.1" "safe-buffer": "^5.1.1"
} }
}, },
"parse-entities": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz",
"integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==",
"requires": {
"character-entities": "^1.0.0",
"character-entities-legacy": "^1.0.0",
"character-reference-invalid": "^1.0.0",
"is-alphanumerical": "^1.0.0",
"is-decimal": "^1.0.0",
"is-hexadecimal": "^1.0.0"
}
},
"parse-json": { "parse-json": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@ -34123,6 +34731,11 @@
} }
} }
}, },
"prismjs": {
"version": "1.26.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.26.0.tgz",
"integrity": "sha512-HUoH9C5Z3jKkl3UunCyiD5jwk0+Hz0fIgQ2nbwU2Oo/ceuTAQAg+pPVnfdt2TJWRVLcxKh9iuoYDUSc8clb5UQ=="
},
"process": { "process": {
"version": "0.11.10", "version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -34189,6 +34802,14 @@
"warning": "^4.0.0" "warning": "^4.0.0"
} }
}, },
"property-information": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
"integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==",
"requires": {
"xtend": "^4.0.0"
}
},
"proxy-addr": { "proxy-addr": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
@ -34717,6 +35338,18 @@
"react-transition-group": "^4.3.0" "react-transition-group": "^4.3.0"
} }
}, },
"react-syntax-highlighter": {
"version": "15.4.5",
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.5.tgz",
"integrity": "sha512-RC90KQTxZ/b7+9iE6s9nmiFLFjWswUcfULi4GwVzdFVKVMQySkJWBuOmJFfjwjMVCo0IUUuJrWebNKyviKpwLQ==",
"requires": {
"@babel/runtime": "^7.3.1",
"highlight.js": "^10.4.1",
"lowlight": "^1.17.0",
"prismjs": "^1.25.0",
"refractor": "^3.2.0"
}
},
"react-transition-group": { "react-transition-group": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
@ -34872,6 +35505,23 @@
"resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz", "resolved": "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz",
"integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=" "integrity": "sha1-dJrO7H8/34tj+SegSAnpDFwLNGA="
}, },
"refractor": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/refractor/-/refractor-3.5.0.tgz",
"integrity": "sha512-QwPJd3ferTZ4cSPPjdP5bsYHMytwWYnAN5EEnLtGvkqp/FCCnGsBgxrm9EuIDnjUC3Uc/kETtvVi7fSIVC74Dg==",
"requires": {
"hastscript": "^6.0.0",
"parse-entities": "^2.0.0",
"prismjs": "~1.25.0"
},
"dependencies": {
"prismjs": {
"version": "1.25.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz",
"integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg=="
}
}
},
"regenerate": { "regenerate": {
"version": "1.4.2", "version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@ -35996,6 +36646,11 @@
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
}, },
"space-separated-tokens": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz",
"integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA=="
},
"spdx-correct": { "spdx-correct": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",

View File

@ -67,6 +67,7 @@
"react-refresh": "^0.8.3", "react-refresh": "^0.8.3",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-select": "^4.3.1", "react-select": "^4.3.1",
"react-syntax-highlighter": "^15.4.5",
"react-yandex-metrika": "^2.6.0", "react-yandex-metrika": "^2.6.0",
"redux-devtools-extension": "^2.13.9", "redux-devtools-extension": "^2.13.9",
"resolve": "1.18.1", "resolve": "1.18.1",

View File

@ -14,9 +14,9 @@
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <!-- <noscript>You need to enable JavaScript to run this app.</noscript> -->
<div id="root"></div> <div id="root"></div>
<script type="text/javascript"> <!-- <script type="text/javascript">
(function (m, e, t, r, i, k, a) { (function (m, e, t, r, i, k, a) {
m[i] = m[i] || function () { (m[i].a = m[i].a || []).push(arguments) }; m[i] = m[i] || function () { (m[i].a = m[i].a || []).push(arguments) };
m[i].l = 1 * new Date(); k = e.createElement(t), a = e.getElementsByTagName(t)[0], k.async = 1, k.src = r, a.parentNode.insertBefore(k, a) m[i].l = 1 * new Date(); k = e.createElement(t), a = e.getElementsByTagName(t)[0], k.async = 1, k.src = r, a.parentNode.insertBefore(k, a)
@ -32,7 +32,7 @@
</script> </script>
<noscript> <noscript>
<div><img src="https://mc.yandex.ru/watch/84188125" style="position:absolute; left:-9999px;" alt="" /></div> <div><img src="https://mc.yandex.ru/watch/84188125" style="position:absolute; left:-9999px;" alt="" /></div>
</noscript> </noscript> -->
</body> </body>
</html> </html>

View File

@ -1,4 +1,4 @@
import React, { Suspense, lazy } from 'react' import React, { Suspense, lazy, useEffect, useState } from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { selectAuth } from './redux/outstaffingSlice' import { selectAuth } from './redux/outstaffingSlice'
@ -6,6 +6,7 @@ import 'bootstrap/dist/css/bootstrap.min.css'
import './fonts/stylesheet.css' import './fonts/stylesheet.css'
import { ProtectedRoute } from './components/ProtectedRoute/ProtectedRoute' import { ProtectedRoute } from './components/ProtectedRoute/ProtectedRoute'
import { YMInitializer } from 'react-yandex-metrika' import { YMInitializer } from 'react-yandex-metrika'
import AuthPageForDevelopers from './pages/AuthPageForDevelopers' import AuthPageForDevelopers from './pages/AuthPageForDevelopers'
@ -16,6 +17,13 @@ import CalendarPage from './pages/CalendarPage'
import ReportPage from './pages/ReportFormPage.js' import ReportPage from './pages/ReportFormPage.js'
import FormPage from './pages/FormPage.js' import FormPage from './pages/FormPage.js'
import SingleReportPage from './pages/SingleReportPage' import SingleReportPage from './pages/SingleReportPage'
import { Highlighter } from './pages/CodeSnippetPage'
import { QuizPage } from './pages/quiz/QuizPage'
import { InterjacentPage } from './pages/quiz/InterjacentPage'
import { QuizTestPage } from './pages/quiz/QuizTestPage'
import { Instruction } from './components/features/quiz/Instructions'
import { InstructionPage } from './pages/quiz/InstructionPage'
import {ResultPage} from "./pages/quiz/ResultPage";
const App = (props) => { const App = (props) => {
const isAuth = useSelector(selectAuth) const isAuth = useSelector(selectAuth)
@ -36,7 +44,7 @@ const App = (props) => {
path='/candidate/:id' path='/candidate/:id'
component={CandidatePage} component={CandidatePage}
/> />
<ProtectedRoute path='/calendar' component={CalendarPage} /> <ProtectedRoute path='/:userId/calendar' component={CalendarPage} />
<ProtectedRoute <ProtectedRoute
exact exact
path='/candidate/:id/form' path='/candidate/:id/form'
@ -44,6 +52,11 @@ const App = (props) => {
/> />
<ProtectedRoute exact path='/report' component={ReportPage} /> <ProtectedRoute exact path='/report' component={ReportPage} />
<ProtectedRoute path='/report/:id' component={SingleReportPage} /> <ProtectedRoute path='/report/:id' component={SingleReportPage} />
<ProtectedRoute path='/quiz' component={QuizPage} />
<ProtectedRoute path='/quiz-interjacent' component={InterjacentPage} />
<ProtectedRoute path='/quiz-test' component={QuizTestPage} />
<ProtectedRoute path='/quiz-instruction' component={InstructionPage} />
<ProtectedRoute path='/quiz-result' component={ResultPage} />
<ProtectedRoute component={() => <div>Page not found</div>} /> <ProtectedRoute component={() => <div>Page not found</div>} />
</Switch> </Switch>
</Router> </Router>
@ -63,3 +76,5 @@ const App = (props) => {
} }
export default App export default App

View File

@ -1,8 +1,10 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import { AuthBox } from '../AuthBox/AuthBox'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { auth } from '../../redux/outstaffingSlice' import { auth } from '../../redux/outstaffingSlice'
import { loading } from '../../redux/loaderSlice' import { loading } from '../../redux/loaderSlice'
import style from './AuthForDevelopers.module.css'
import ellipse from '../../images/ellipse.png' import ellipse from '../../images/ellipse.png'
import arrow from '../../images/arrow__login_page.png' import arrow from '../../images/arrow__login_page.png'
import authImg from '../../images/auth_img.png' import authImg from '../../images/auth_img.png'
@ -10,115 +12,42 @@ import cross from '../../images/cross.png'
import text from '../../images/Body_Text.png' import text from '../../images/Body_Text.png'
import vector from '../../images/Vector_Smart_Object.png' import vector from '../../images/Vector_Smart_Object.png'
import vectorBlack from '../../images/Vector_Smart_Object_black.png' import vectorBlack from '../../images/Vector_Smart_Object_black.png'
import { fetchAuth } from '../../server/server'
import { selectAuth } from '../../redux/outstaffingSlice'; import { selectAuth } from '../../redux/outstaffingSlice'
import { selectIsLoading } from '../../redux/loaderSlice'; import { Redirect, Link } from 'react-router-dom'
import { setRole } from '../../redux/roleSlice';
import { Redirect, Link } from 'react-router-dom';
import { Loader } from '../Loader/Loader'
import { withSwalInstance } from 'sweetalert2-react';
import swal from 'sweetalert2';
import { Footer } from '../Footer/Footer' import { Footer } from '../Footer/Footer'
const SweetAlert = withSwalInstance(swal); import './authForDevelopers.scss'
const AuthForDevelopers = () => { const AuthForDevelopers = () => {
const dispatch = useDispatch()
const isAuth = useSelector(selectAuth) const isAuth = useSelector(selectAuth)
const isLoading = useSelector(selectIsLoading)
const [username, setUsername] = useState('') if (isAuth) {
const [password, setPassword] = useState('')
const [error, setError] = useState(null);
if(isAuth) {
return <Redirect to='/' /> return <Redirect to='/' />
} }
return ( return (
<section className={style.developers}> <section className='auth-developers'>
<div className={style.developers__background}> <div className='auth-developers__background'>
<img className={style.vector} src={vector} alt='' /> <img className='auth-developers__vector' src={vector} alt='' />
<img className={style.vectorBlack} src={vectorBlack} alt='' /> <img
className='auth-developers__vector-black'
src={'./images/Vector_Smart_Object_black.png'}
alt=''
/>
<div className='container'> <div className='container'>
<div className='row'> <div className='row'>
<div className='col-12 col-xl-6'> <div className='col-12 col-xl-6'>
<div className={style.developers__box}> <div className='auth-developers__box'>
<h2 className={style.developers__title}> <AuthBox title='Для разработчиков' roleChangeLink='/auth' />
Войти в <span>систему</span>
</h2>
<div className={style.developers__partners}>
<img src={ellipse} alt='' />
<span>Для разработчиков</span>
</div>
<form className={style.developers__form}>
<label htmlFor='login'>Ваш логин:</label>
<input
id='login'
type='text'
placeholder='Логин'
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<label htmlFor='password'>Пароль:</label>
<input
id='password'
type='password'
placeholder='Пароль'
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{ error && <div className={style.form__error}>
<SweetAlert
show={!!error}
title="Ошибка"
text={error}
onConfirm={() => setError(null)}
/>
</div> }
<div className={style.form__buttons}>
<button
className={style.form__btn}
onClick={!isLoading ? (e) => {
e.preventDefault();
dispatch(loading(true))
fetchAuth({
username,
password,
dispatch: ()=> {
dispatch(auth(true))
dispatch(loading(false))
dispatch(setRole('ROLE_DEV'))
},
catchError: () => {
setError('Некорректные данные для входа')
dispatch(loading(false))
}
})
} : ()=>{}}
>
{ isLoading ? <Loader /> : 'Войти' }
</button>
<button className={`${style.form__btn__partners} ${style.auth__link}`}>
<Link to='/auth'>Для партнёров</Link>
</button>
</div>
</form>
</div> </div>
</div> </div>
<div className='col-xl-2'> <div className='col-xl-2'>
<img className={style.developers__arrow} src={arrow} alt='' /> <img className='auth-developers__arrow' src={arrow} alt='' />
</div> </div>
<div className='col-12 col-xl-4'> <div className='col-12 col-xl-4'>
<div className={style.developers__info}> <div className='auth-developers__info'>
<div className={style.developers__info__box}> <div className='auth-developers__info-box'>
<img src={authImg} alt='' /> <img src={authImg} alt='' />
<h3> <h3>
Управление Управление
@ -126,28 +55,30 @@ const AuthForDevelopers = () => {
</h3> </h3>
</div> </div>
<div className={style.developers__info__container}> <div className='auth-developers__info-container'>
<div className={style.developers__info__img}> <div className='auth-developers__info-img'>
<div> <div>
<img className='cross' src={cross} alt='' /> <img className='cross' src={cross} alt='' />
</div> </div>
<div> <div>
{/* <img className={style.specialists} src={specialists} alt="" /> */} {/* <img className='auth-specialists} src={specialists} alt="" /> */}
<p className={style.specialists}>20 Специалистов</p> <p className='auth-developers__specialists'>
20 Специалистов
</p>
</div> </div>
</div> </div>
<ul className={style.info__list}> <ul className='auth-developers__info-list'>
<li className={style.info__list__item}> <li className='auth-developers__info-item'>
Рабочее пространство Рабочее пространство
</li> </li>
<li className={style.info__list__item}> <li className='auth-info__list-item'>
Управление задачами Управление задачами
</li> </li>
</ul> </ul>
</div> </div>
<img className={style.img__text} src={text} alt='' /> <img className='auth-developers__img-text' src={text} alt='' />
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,385 +0,0 @@
.developers {
overflow: hidden;
}
.developers__background {
background-color: #f1f1f1;
position: relative;
}
.vector,
.vectorBlack {
position: absolute;
}
.vector {
top: -720px;
left: -320px;
}
.vectorBlack {
top: 460px;
right: -224px;
}
@media (max-width: 575.98px) {
.vector,
.vectorBlack {
display: none;
}
}
.developers__box {
display: flex;
flex-direction: column;
align-self: center;
margin-bottom: 194px;
}
@media (max-width: 575.98px) {
.developers__box {
margin-bottom: 44px;
}
}
.developers__title {
font-family: 'GT Eesti Pro Display';
font-size: 5.3em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 77.81px;
text-align: left;
margin-top: 164px;
}
@media (max-width: 575.98px) {
.developers__title {
text-align: center;
margin-top: 44px;
}
}
.developers__title > span {
font-family: 'GT Eesti Pro Display';
color: #52b709;
font-style: normal;
letter-spacing: 0.56px;
line-height: normal;
}
.developers__partners {
display: flex;
align-items: center;
justify-content: center;
margin-top: 15px;
margin-bottom: 65px;
}
.developers__partners > img {
width: 6px;
height: 6px;
margin-left: 120px;
}
.developers__partners > span {
color: #18586e;
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
margin-left: 10px;
}
.developers__form {
display: flex;
flex-direction: column;
}
.developers__form > label {
color: #48802d;
font-family: 'GT Eesti Pro Display';
font-size: 2.4em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
margin-bottom: 20px;
margin-left: 45px;
}
.developers__form > input {
max-width: 366px;
height: 75px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 37px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
margin-bottom: 60px;
color: #a6a6a6;
font-family: 'GT Eesti Pro Display';
font-size: 2.2em;
font-weight: 300;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
padding-left: 45px;
outline: none;
}
.form__buttons {
display: flex;
justify-content: start;
align-items: center;
}
.form__btn {
width: 268px;
height: 75px;
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
border-radius: 38px;
background-color: #ffffff;
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%),
linear-gradient(
36deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.16) 47%,
rgba(255, 255, 255, 0.17) 50%,
rgba(255, 255, 255, 0) 100%
);
border: none;
color: #ffffff;
font-family: 'Muller';
font-weight: 500;
font-size: 2.2em;
font-weight: bold;
letter-spacing: normal;
line-height: 71.88px;
text-align: center;
border: 2px solid #6aaf5c;
margin-right: 1.5rem;
}
.form__btn:hover .loader * {
fill: #6aaf5c;
}
.form__btn:hover {
background-image: none;
background-color: #ffffff;
border: 2px solid #6aaf5c;
color: #6aaf5c !important;
transition: .3s;
}
.form__btn__partners {
width: 268px;
height: 75px;
border-radius: 38px;
background-color: #ffffff;
border: 2px solid #6aaf5c;
font-family: 'Muller';
font-size: 2em;
font-weight: 300;
letter-spacing: normal;
line-height: 71.88px;
text-align: center;
}
.form__btn__partners a {
color: #6aaf5c !important;
}
/* .form__error {
font-size: 16px;
color: #b21;
margin-left: 45px;
} */
@media (max-width: 575.98px) {
.form__btn {
margin: 0 auto;
}
}
.developers__arrow {
margin-top: 360px;
}
@media (max-width: 575.98px) {
.developers__arrow {
display: none;
}
}
.developers__info {
background-color: #e1fccf;
margin-top: 70px;
max-width: 310px;
padding-top: 30px;
position: relative;
padding-bottom: 310px;
}
@media (max-width: 575.98px) {
.developers__info {
max-width: 380px;
}
}
@media (max-width: 375.98px) {
.developers__info {
max-width: 340px;
}
}
.developers__info__box {
display: flex;
align-items: center;
}
@media (max-width: 575.98px) {
.developers__info__box {
flex-direction: column;
justify-content: center;
}
}
.developers__info__box > img {
width: 150px;
height: 150px;
margin-left: -84px;
margin-right: 30px;
}
@media (max-width: 575.98px) {
.developers__info__box > img {
margin-left: 0px;
margin-right: 0px;
}
}
.developers__info__box > h3 {
font-family: 'GT Eesti Pro Display';
font-size: 2em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
}
@media (max-width: 575.98px) {
.developers__info__box > h3 {
margin-top: 20px;
}
}
.developers__info__container {
display: flex;
position: relative;
}
.developers__info__img {
display: flex;
flex-direction: column;
text-align: center;
margin-top: 28px;
margin-left: -20px;
}
@media (max-width: 575.98px) {
.developers__info__img {
margin-left: -48px;
}
}
.developers__info__img > div > img {
margin-bottom: 100px;
}
.specialists {
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 26.12px;
text-align: left;
transform: rotate(-90deg);
text-transform: uppercase;
}
.info__list {
list-style: none;
margin-top: 110px;
position: absolute;
left: 114px;
}
@media (max-width: 575.98px) {
.info__list {
left: 34px;
}
}
.info__list__item {
color: #1f1f1f;
font-family: 'GT Eesti Pro Display';
font-size: 4em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 56.95px;
text-align: left;
text-decoration: underline;
text-transform: uppercase;
margin-bottom: 34px;
}
@media (max-width: 575.98px) {
.info__list__item {
font-size: 2.6em;
margin-bottom: 14px;
}
}
.img__text {
position: absolute;
right: -68px;
bottom: -84px;
}
@media (max-width: 575.98px) {
.img__text {
right: -10px;
}
}
.auth__link {
display: block;
}
.auth__link a {
display: block;
width: 100%;
height: 100%;
color: #fff;
}
@media (max-width: 766px) {
.form__buttons {
flex-direction: column;
}
.form__btn {
margin: 0;
margin-bottom: 1.5rem;
}
}

View File

@ -1,149 +1,74 @@
import React, { useState } from 'react'; import React from 'react'
import { useDispatch } from 'react-redux'; import arrow from '../../images/arrow__login_page.png'
import { auth } from '../../redux/outstaffingSlice'; import medium from '../../images/medium_male_big.png'
import { loading } from '../../redux/loaderSlice'; import cross from '../../images/cross.png'
import style from './AuthForPartners.module.css'; import text from '../../images/Body_Text.png'
import ellipse from '../../images/ellipse.png'; import vector from '../../images/Vector_Smart_Object.png'
import arrow from '../../images/arrow__login_page.png'; import vectorBlack from '../../images/Vector_Smart_Object_black.png'
import medium from '../../images/medium_male_big.png';
import cross from '../../images/cross.png';
import text from '../../images/Body_Text.png';
import align from '../../images/align-left.png';
import phone from '../../images/phone.png';
import telegram from '../../images/telegram.png';
import vector from '../../images/Vector_Smart_Object.png';
import vectorBlack from '../../images/Vector_Smart_Object_black.png';
import { fetchAuth } from '../../server/server'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { selectAuth } from '../../redux/outstaffingSlice'; import { selectAuth } from '../../redux/outstaffingSlice'
import { selectIsLoading } from '../../redux/loaderSlice'; import { Redirect } from 'react-router-dom'
import { setRole } from '../../redux/roleSlice';
import { Redirect, Link } from 'react-router-dom';
import { Loader } from '../Loader/Loader'
import { withSwalInstance } from 'sweetalert2-react';
import swal from 'sweetalert2';
import { Footer } from '../Footer/Footer' import { Footer } from '../Footer/Footer'
import { AuthBox } from '../AuthBox/AuthBox'
const SweetAlert = withSwalInstance(swal); import './authForPartners.scss'
const AuthForPartners = () => { const AuthForPartners = () => {
const dispatch = useDispatch()
const isAuth = useSelector(selectAuth) const isAuth = useSelector(selectAuth)
const isLoading = useSelector(selectIsLoading)
const [username, setUsername] = useState('') if (isAuth) {
const [password, setPassword] = useState('')
const [error, setError] = useState(null);
if(isAuth) {
return <Redirect to='/' /> return <Redirect to='/' />
} }
return ( return (
<section className={style.partners}> <section className='auth-partners'>
<div className={style.partners__background}> <div className='auth-partners__background'>
<img className={style.vector} src={vector} alt="" /> <img className='auth-partners__vector' src={vector} alt='' />
<img className={style.vectorBlack} src={vectorBlack} alt="" /> <img className='auth-partners__vector-black' src={vectorBlack} alt='' />
<div className="container"> <div className='container'>
<div className="row"> <div className='row'>
<div className="col-12 col-xl-6"> <div className='col-12 col-xl-6'>
<div className={style.partners__box}> <div className='auth-partners__box'>
<h2 className={style.partners__title}> <AuthBox title='Для партнёров' roleChangeLink='/authdev' />
Войти в <span>систему</span>
</h2>
<div className={style.partners__partners}>
<img src={ellipse} alt="" />
<span>Для партнеров</span>
</div>
<form className={style.partners__form}>
<label htmlFor="login">Ваш логин:</label>
<input id="login" type="text" placeholder="Логин"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<label htmlFor="password">Пароль:</label>
<input id="password" type="password" placeholder="Пароль"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{ error && <div className={style.form__error}>
<SweetAlert
show={!!error}
title="Ошибка"
text={error}
onConfirm={() => setError(null)}
/>
</div> }
<div className={style.form__buttons}>
<button
className={style.form__btn}
onClick={!isLoading ? (e) => {
e.preventDefault();
dispatch(loading(true))
fetchAuth({
username,
password,
dispatch: ()=> {
dispatch(auth(true))
dispatch(loading(false))
dispatch(setRole('ROLE_PARTNER'))
},
catchError: () => {
setError('Некорректные данные для входа')
dispatch(loading(false))
}
})
} : ()=>{}}
>
{ isLoading ? <Loader /> : 'Войти' }
</button>
<button className={`${style.form__btn__dev} ${style.auth__link}`}>
<Link to='/authdev'>Для разработчиков</Link>
</button>
</div>
</form>
</div> </div>
</div> </div>
<div className="col-xl-2"> <div className='col-xl-2'>
<img className={style.partners__arrow} src={arrow} alt="" /> <img className='auth-partners__arrow' src={arrow} alt='' />
</div> </div>
<div className="col-12 col-xl-4"> <div className='col-12 col-xl-4'>
<div className={style.partners__info}> <div className='auth-partners__info'>
<div className={style.partners__info__box}> <div className='auth-partners__info-box'>
<img src={medium} alt="" /> <img src={medium} alt='' />
<h3> <h3>
Frontend разработчик, Frontend разработчик,
<br /> Middle <br /> Middle
</h3> </h3>
</div> </div>
<div className={style.partners__info__container}> <div className='auth-partners__info-container'>
<div className={style.partners__info__img}> <div className='auth-partners__info-img'>
<div> <div>
<img className="cross" src={cross} alt="" /> <img className='cross' src={cross} alt='' />
</div> </div>
<div> <div>
{/* <img className={style.specialists} src={specialists} alt="" /> */} {/* <img className='auth-specialists} src={specialists} alt="" /> */}
<p className={style.specialists}>20 Специалистов</p> <p className='auth-partners__specialists'>
20 Специалистов
</p>
</div> </div>
</div> </div>
<ul className={style.info__list}> <ul className='auth-partners__info-list'>
<li className={style.info__list__item}>Ruby on Rails</li> <li className='auth-partners__info-item'>Ruby on Rails</li>
<li className={style.info__list__item}>PHP</li> <li className='auth-partners__info-item'>PHP</li>
<li className={style.info__list__item}>Python</li> <li className='auth-partners__info-item'>Python</li>
<li className={style.info__list__item}>Vue.js</li> <li className='auth-partners__info-item'>Vue.js</li>
<li className={style.info__list__item}>React. JS</li> <li className='auth-partners__info-item'>React. JS</li>
</ul> </ul>
</div> </div>
<img className={style.img__text} src={text} alt="" /> <img className='auth-partners__img-text' src={text} alt='' />
</div> </div>
</div> </div>
</div> </div>
@ -151,7 +76,7 @@ const AuthForPartners = () => {
</div> </div>
</div> </div>
</section> </section>
); )
}; }
export default AuthForPartners; export default AuthForPartners

View File

@ -1,466 +0,0 @@
.partners {
overflow: hidden;
}
.partners__background {
background-color: #f1f1f1;
position: relative;
}
.vector,
.vectorBlack {
position: absolute;
}
.vector {
top: -720px;
left: -320px;
}
.vectorBlack {
top: 460px;
right: -224px;
}
@media (max-width: 575.98px) {
.vector,
.vectorBlack {
display: none;
}
}
.partners__box {
display: flex;
flex-direction: column;
align-self: center;
margin-bottom: 194px;
}
@media (max-width: 575.98px) {
.partners__box {
margin-bottom: 44px;
}
}
.partners__title {
font-family: 'GT Eesti Pro Display';
font-size: 5.3em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 77.81px;
text-align: left;
margin-top: 164px;
}
@media (max-width: 575.98px) {
.partners__title {
text-align: center;
margin-top: 44px;
}
}
.partners__title > span {
font-family: 'GT Eesti Pro Display';
color: #52b709;
font-style: normal;
letter-spacing: 0.56px;
line-height: normal;
}
.partners__partners {
display: flex;
align-items: center;
justify-content: center;
margin-top: 15px;
margin-bottom: 65px;
}
.partners__partners > img {
width: 6px;
height: 6px;
margin-left: 120px;
}
.partners__partners > span {
color: #18586e;
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
margin-left: 10px;
}
.partners__form {
display: flex;
flex-direction: column;
}
.partners__form > label {
color: #48802d;
font-family: 'GT Eesti Pro Display';
font-size: 2.4em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
margin-bottom: 20px;
margin-left: 45px;
}
.partners__form > input {
max-width: 366px;
height: 75px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 37px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
margin-bottom: 60px;
color: #a6a6a6;
font-family: 'GT Eesti Pro Display';
font-size: 2.2em;
font-weight: 300;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
padding-left: 45px;
outline: none;
}
.form__buttons {
display: flex;
justify-content: start;
align-items: center;
}
.form__btn {
width: 268px;
height: 75px;
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
border-radius: 38px;
background-color: #ffffff;
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%),
linear-gradient(
36deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.16) 47%,
rgba(255, 255, 255, 0.17) 50%,
rgba(255, 255, 255, 0) 100%
);
border: none;
color: #ffffff;
font-family: 'Muller';
font-weight: bold;
font-size: 2.2em;
letter-spacing: normal;
line-height: 71.88px;
text-align: center;
border: 2px solid #6aaf5c;
margin-right: 1.5rem;
}
.form__btn:hover {
background-image: none;
background-color: #ffffff;
border: 2px solid #6aaf5c;
color: #6aaf5c !important;
transition: .3s;
}
.form__btn__dev {
width: 268px;
height: 75px;
border-radius: 38px;
background-color: #ffffff;
border: 2px solid #6aaf5c;
font-family: 'Muller';
font-size: 2em;
font-weight: 300;
letter-spacing: normal;
line-height: 71.88px;
text-align: center;
}
.form__btn__dev a {
color: #6aaf5c !important;
}
/* .form__error {
font-size: 16px;
color: #b21;
margin-left: 45px;
} */
@media (max-width: 575.98px) {
.form__btn {
margin: 0 auto;
}
}
.partners__arrow {
margin-top: 360px;
}
@media (max-width: 575.98px) {
.partners__arrow {
display: none;
}
}
.partners__info {
background-color: #e1fccf;
margin-top: 70px;
max-width: 310px;
padding-top: 30px;
position: relative;
padding-bottom: 310px;
}
@media (max-width: 575.98px) {
.partners__info {
max-width: 380px;
}
}
@media (max-width: 375.98px) {
.partners__info {
max-width: 340px;
}
}
.partners__info__box {
display: flex;
align-items: center;
}
@media (max-width: 575.98px) {
.partners__info__box {
flex-direction: column;
justify-content: center;
}
}
.partners__info__box > img {
width: 165px;
height: 165px;
margin-left: -84px;
margin-right: 30px;
}
@media (max-width: 575.98px) {
.partners__info__box > img {
margin-left: 0px;
margin-right: 0px;
}
}
.partners__info__box > h3 {
font-family: 'GT Eesti Pro Display';
font-size: 2em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
}
@media (max-width: 575.98px) {
.partners__info__box > h3 {
margin-top: 20px;
}
}
.partners__info__container {
display: flex;
position: relative;
}
.partners__info__img {
display: flex;
flex-direction: column;
text-align: center;
margin-top: 28px;
}
@media (max-width: 575.98px) {
.partners__info__img {
margin-left: -48px;
}
}
.partners__info__img > div > img {
margin-bottom: 100px;
}
.specialists {
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 26.12px;
text-align: left;
transform: rotate(-90deg);
text-transform: uppercase;
}
/* .specialists {
margin-left: 26px;
} */
.info__list {
list-style: none;
margin-top: 110px;
position: absolute;
right: -120px;
}
@media (max-width: 575.98px) {
.info__list {
left: 34px;
}
}
.info__list__item {
color: #1f1f1f;
font-family: 'GT Eesti Pro Display';
font-size: 4em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 56.95px;
text-align: left;
text-decoration: underline;
text-transform: uppercase;
}
@media (max-width: 575.98px) {
.info__list__item {
font-size: 2.6em;
}
}
.img__text {
position: absolute;
right: -68px;
bottom: -84px;
}
@media (max-width: 575.98px) {
.img__text {
right: -10px;
}
}
.partners__footer__left {
display: flex;
align-items: center;
margin-bottom: 60px;
}
@media (max-width: 575.98px) {
.partners__footer__left {
margin-top: 120px;
}
}
.footer__left__sp {
padding: 0 100px 0 34px;
}
@media (max-width: 575.98px) {
.footer__left__sp {
padding: 0;
margin-left: 10px;
}
}
.partners__footer__left > div > span {
color: #18586e;
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
}
@media (max-width: 575.98px) {
.partners__footer__left > div > span {
font-size: 1.2em;
}
}
.partners__footer__icon {
text-align: end;
}
.partners__footer__icon > img {
margin-left: 20px;
}
@media (max-width: 575.98px) {
.partners__footer__icon > img {
margin-left: 10px;
}
}
.partners__footer__right {
display: flex;
flex-direction: column;
align-items: left;
}
@media (max-width: 575.98px) {
.partners__footer__right {
margin-bottom: 20px;
}
}
.phone {
color: #003b65;
font-family: 'CeraPro';
font-size: 2.1em;
letter-spacing: normal;
line-height: 25px;
text-align: left;
}
.workingHours {
color: #003b65;
font-family: 'CeraPro';
font-size: 1.2em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: normal;
margin-left: 24px;
}
.auth__link {
display: block;
}
.auth__link a {
display: block;
width: 100%;
height: 100%;
color: #fff;
}
@media (max-width: 766px) {
.form__buttons {
flex-direction: column;
}
.form__btn {
margin: 0;
margin-bottom: 1.5rem;
}
}

View File

@ -0,0 +1,212 @@
.auth-developers {
overflow: hidden;
}
.auth-developers__background {
background-color: #f1f1f1;
position: relative;
}
.auth-developers__vector,
.auth-developers__vector-black {
position: absolute;
}
.auth-developers__vector {
top: -720px;
left: -320px;
}
.auth-developers__vector-black {
top: 460px;
right: -224px;
}
@media (max-width: 575.98px) {
.auth-developers__vector,
.auth-developers__vector-black {
display: none;
}
}
/* .form__error {
font-size: 16px;
color: #b21;
margin-left: 45px;
} */
.auth-developers__arrow {
margin-top: 360px;
}
@media (max-width: 575.98px) {
.auth-developers__arrow {
display: none;
}
}
.auth-developers__info {
background-color: #e1fccf;
margin-top: 70px;
max-width: 310px;
padding-top: 30px;
position: relative;
padding-bottom: 310px;
}
@media (max-width: 575.98px) {
.auth-developers__info {
max-width: 380px;
}
}
@media (max-width: 375.98px) {
.auth-developers__info {
max-width: 340px;
}
}
.auth-developers__info-box {
display: flex;
align-items: center;
}
@media (max-width: 575.98px) {
.auth-developers__info-box {
flex-direction: column;
justify-content: center;
}
}
.auth-developers__info-box > img {
width: 150px;
height: 150px;
margin-left: -84px;
margin-right: 30px;
}
@media (max-width: 575.98px) {
.auth-developers__info-box > img {
margin-left: 0px;
margin-right: 0px;
}
}
.auth-developers__info-box > h3 {
font-family: 'GT Eesti Pro Display';
font-size: 2em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
}
@media (max-width: 575.98px) {
.auth-developers__info-box > h3 {
margin-top: 20px;
}
}
.auth-developers__info-container {
display: flex;
position: relative;
}
.auth-developers__info-img {
display: flex;
flex-direction: column;
text-align: center;
margin-top: 28px;
margin-left: -20px;
}
@media (max-width: 575.98px) {
.auth-developers__info-img {
margin-left: -48px;
}
}
.auth-developers__info-img > div > img {
margin-bottom: 100px;
}
.auth-developers__specialists {
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 26.12px;
text-align: left;
transform: rotate(-90deg);
text-transform: uppercase;
}
.auth-developers__info-list {
list-style: none;
margin-top: 110px;
position: absolute;
left: 114px;
}
@media (max-width: 575.98px) {
.auth-developers__info-list {
left: 34px;
}
}
.auth-developers__info-item {
color: #1f1f1f;
font-family: 'GT Eesti Pro Display';
font-size: 4em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 56.95px;
text-align: left;
text-decoration: underline;
text-transform: uppercase;
margin-bottom: 34px;
}
@media (max-width: 575.98px) {
.auth-developers__info-item {
font-size: 2.6em;
margin-bottom: 14px;
}
}
.auth-developers__img-text {
position: absolute;
right: -68px;
bottom: -84px;
}
@media (max-width: 575.98px) {
.auth-developers__img-text {
right: -10px;
}
}
.auth-developers__auth-link {
display: block;
}
.auth-developers__auth-link a {
display: block;
width: 100%;
height: 100%;
color: #fff;
}
@media (max-width: 766px) {
.auth-developers__form-buttons {
flex-direction: column;
}
.auth-developers__form-btn {
margin: 0;
margin-bottom: 1.5rem;
}
}

View File

@ -0,0 +1,292 @@
.auth-partners {
overflow: hidden;
}
.auth-partners__background {
background-color: #f1f1f1;
position: relative;
}
.auth-partners__vector,
.auth-partners__vector-black {
position: absolute;
}
.auth-partners__vector {
top: -720px;
left: -320px;
}
.auth-partners__vector-black {
top: 460px;
right: -224px;
}
@media (max-width: 575.98px) {
.auth-partners__vector,
.auth-partners__vector-black {
display: none;
}
}
.auth-partners__arrow {
margin-top: 360px;
}
@media (max-width: 575.98px) {
.auth-partners__arrow {
display: none;
}
}
.auth-partners__info {
background-color: #e1fccf;
margin-top: 70px;
max-width: 310px;
padding-top: 30px;
position: relative;
padding-bottom: 310px;
}
@media (max-width: 575.98px) {
.auth-partners__info {
max-width: 380px;
}
}
@media (max-width: 375.98px) {
.auth-partners__info {
max-width: 340px;
}
}
.auth-partners__info-box {
display: flex;
align-items: center;
}
@media (max-width: 575.98px) {
.auth-partners__info-box {
flex-direction: column;
justify-content: center;
}
}
.auth-partners__info-box > img {
width: 165px;
height: 165px;
margin-left: -84px;
margin-right: 30px;
}
@media (max-width: 575.98px) {
.auth-partners__info-box > img {
margin-left: 0px;
margin-right: 0px;
}
}
.auth-partners__info-box > h3 {
font-family: 'GT Eesti Pro Display';
font-size: 2em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
}
@media (max-width: 575.98px) {
.auth-partners__info-box > h3 {
margin-top: 20px;
}
}
.auth-partners__info-container {
display: flex;
position: relative;
}
.auth-partners__info-img {
display: flex;
flex-direction: column;
text-align: center;
margin-top: 28px;
}
@media (max-width: 575.98px) {
.auth-partners__info-img {
margin-left: -48px;
}
}
.auth-partners__info-img > div > img {
margin-bottom: 100px;
}
.auth-partners__specialists {
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 26.12px;
text-align: left;
transform: rotate(-90deg);
text-transform: uppercase;
}
/* .specialists {
margin-left: 26px;
} */
.auth-partners__info-list {
list-style: none;
margin-top: 110px;
position: absolute;
right: -120px;
}
@media (max-width: 575.98px) {
.auth-partners__info-list {
left: 34px;
}
}
.auth-partners__info-item {
color: #1f1f1f;
font-family: 'GT Eesti Pro Display';
font-size: 4em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 56.95px;
text-align: left;
text-decoration: underline;
text-transform: uppercase;
}
@media (max-width: 575.98px) {
.auth-partners__info-item {
font-size: 2.6em;
}
}
.auth-partners__img-text {
position: absolute;
right: -68px;
bottom: -84px;
}
@media (max-width: 575.98px) {
.auth-partners__img-text {
right: -10px;
}
}
.auth-partners__footer--left {
display: flex;
align-items: center;
margin-bottom: 60px;
}
@media (max-width: 575.98px) {
.auth-partners__footer--left {
margin-top: 120px;
}
}
.auth-partners__footer__sp {
padding: 0 100px 0 34px;
}
@media (max-width: 575.98px) {
.auth-partners__footer__sp {
padding: 0;
margin-left: 10px;
}
}
.auth-partners__footer--left > div > span {
color: #18586e;
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
}
@media (max-width: 575.98px) {
.auth-partners__footer--left > div > span {
font-size: 1.2em;
}
}
.auth-partners__footer-icon {
text-align: end;
}
.auth-partners__footer-icon > img {
margin-left: 20px;
}
@media (max-width: 575.98px) {
.auth-partners__footer-icon > img {
margin-left: 10px;
}
}
.auth-partners__footer--right {
display: flex;
flex-direction: column;
align-items: left;
}
@media (max-width: 575.98px) {
.auth-partners__footer--right {
margin-bottom: 20px;
}
}
.auth-partners__phone {
color: #003b65;
font-family: 'CeraPro';
font-size: 2.1em;
letter-spacing: normal;
line-height: 25px;
text-align: left;
}
.auth-partners__working-hours {
color: #003b65;
font-family: 'CeraPro';
font-size: 1.2em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: normal;
margin-left: 24px;
}
.auth-partners__auth-link {
display: block;
}
.auth-partners__auth-link a {
display: block;
width: 100%;
height: 100%;
color: #fff;
}
@media (max-width: 766px) {
.auth-partners__form-buttons {
flex-direction: column;
}
.auth-partners__form-btn {
margin: 0;
margin-bottom: 1.5rem;
}
}

View File

@ -0,0 +1,101 @@
import React, { useState } from 'react'
import { Link } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
import { auth } from '../../redux/outstaffingSlice'
import { loading } from '../../redux/loaderSlice'
import ellipse from '../../images/ellipse.png'
import { Loader } from '../Loader/Loader'
import { fetchAuth } from '../../server/server'
import { setRole } from '../../redux/roleSlice'
import { selectIsLoading } from '../../redux/loaderSlice'
import './authBox.scss'
import { withSwalInstance } from 'sweetalert2-react'
import swal from 'sweetalert2'
const SweetAlert = withSwalInstance(swal)
export const AuthBox = ({ title, roleChangeLink }) => {
const dispatch = useDispatch()
const isLoading = useSelector(selectIsLoading)
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState(null)
return (
<div className='auth-box'>
<h2 className='auth-box__header'>
Войти в <span>систему</span>
</h2>
<div className='auth-box__title'>
<img src={ellipse} alt='' />
<span>{title}</span>
</div>
<form className='auth-box__form'>
<label htmlFor='login'>Ваш логин:</label>
<input
id='login'
type='text'
placeholder='Логин'
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<label htmlFor='password'>Пароль:</label>
<input
id='password'
type='password'
placeholder='Пароль'
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
{error && (
<div className='auth-box__form-error'>
<SweetAlert
show={!!error}
title='Ошибка'
text={error}
onConfirm={() => setError(null)}
/>
</div>
)}
<div className='auth-box__form-buttons'>
<button
className='auth-box__form-btn'
onClick={
!isLoading
? (e) => {
e.preventDefault()
dispatch(loading(true))
fetchAuth({
username,
password,
dispatch: () => {
dispatch(auth(true))
dispatch(loading(false))
dispatch(setRole('ROLE_PARTNER'))
},
catchError: () => {
setError('Некорректные данные для входа')
dispatch(loading(false))
}
})
}
: () => {}
}
>
{isLoading ? <Loader /> : 'Войти'}
</button>
<button className='auth-box__form-btn--role auth-box__auth-link' >
<Link to={roleChangeLink}>Для разработчиков</Link>
</button>
</div>
</form>
</div>
)
}

View File

@ -0,0 +1,196 @@
.auth-box {
display: flex;
flex-direction: column;
align-self: center;
margin-bottom: 194px;
&__header {
font-family: "GT Eesti Pro Display";
font-size: 5.3em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 77.81px;
text-align: left;
margin-top: 164px;
span {
color: #52b709;
letter-spacing: .56px;
line-height: normal;
}
}
&__sign-in {
font-family: 'GT Eesti Pro Display';
font-size: 5.3em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 77.81px;
text-align: left;
margin-top: 164px;
span {
font-family: 'GT Eesti Pro Display';
color: #52b709;
font-style: normal;
letter-spacing: 0.56px;
line-height: normal;
}
}
&__title {
display: flex;
align-items: center;
justify-content: center;
margin-top: 15px;
margin-bottom: 65px;
span {
color: #18586e;
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
margin-left: 10px;
}
img {
width: 6px;
height: 6px;
margin-left: 120px;
}
}
&__form {
display: flex;
flex-direction: column;
label {
color: #48802d;
font-family: 'GT Eesti Pro Display';
font-size: 2.4em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
margin-bottom: 20px;
margin-left: 45px;
}
input {
max-width: 366px;
height: 75px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 37px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
margin-bottom: 60px;
color: #a6a6a6;
font-family: 'GT Eesti Pro Display';
font-size: 2.2em;
font-weight: 300;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
padding-left: 45px;
outline: none;
}
&-buttons {
display: flex;
justify-content: start;
align-items: center;
}
&-btn {
width: 268px;
height: 75px;
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
border-radius: 38px;
background-color: #ffffff;
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%),
linear-gradient(
36deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.16) 47%,
rgba(255, 255, 255, 0.17) 50%,
rgba(255, 255, 255, 0) 100%
);
border: none;
color: #ffffff;
font-family: 'Muller';
font-weight: 500;
font-size: 2.2em;
font-weight: bold;
letter-spacing: normal;
line-height: 71.88px;
text-align: center;
border: 2px solid #6aaf5c;
margin-right: 1.5rem;
&:hover {
background-image: none;
background-color: #ffffff;
border: 2px solid #6aaf5c;
color: #6aaf5c !important;
transition: 0.3s;
.loader * {
fill: #6aaf5c;
}
}
&--role {
width: 268px;
height: 75px;
border-radius: 38px;
background-color: #ffffff;
border: 2px solid #6aaf5c;
font-family: 'Muller';
font-size: 2em;
font-weight: 300;
letter-spacing: normal;
line-height: 71.88px;
text-align: center;
border: 2px solid #6aaf5c;
margin-right: 1.5rem;
a {
color: #6aaf5c !important;
}
}
.loader * {
fill: #fff !important;
}
}
}
}
@media (max-width: 575.98px) {
.auth-box {
margin-bottom: 44px;
&__sign-in {
text-align: center;
margin-top: 44px;
}
&__form-buttons {
margin: 0 auto;
flex-direction: column;
&>* {
margin-bottom: 1.5rem;
}
}
}
}

View File

@ -1,61 +1,92 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux'
import { selectCurrentCandidate } from '../../redux/outstaffingSlice'; import { selectCurrentCandidate, auth } from '../../redux/outstaffingSlice'
import { Link } from 'react-router-dom'; import { Link, useHistory, useParams } from 'react-router-dom'
import style from './Calendar.module.css'; import calendarMale from '../../images/medium_male.png'
import calendarMale from '../../images/medium_male.png'; import rectangle from '../../images/rectangle_secondPage.png'
import rectangle from '../../images/rectangle_secondPage.png'; import CalendarComponent from './CalendarComponent'
import CalendarComponent from './CalendarComponent'; import { currentMonth } from './calendarHelper'
import { currentMonth } from './calendarHelper'; import { Footer } from '../Footer/Footer'
import { Footer } from '../Footer/Footer';
const Calendar = () => { import { fetchReportList } from '../../server/server'
const candidateForCalendar = useSelector(selectCurrentCandidate); import { getRole } from '../../redux/roleSlice'
const [month, setMonth] = useState(''); import './calendar.scss'
const getDateParamString = ({ paramName, value }) => {
return value ? `${paramName}=${value}` : ''
}
const Calendar = ({ onSelect }) => {
const dispatch = useDispatch()
const candidateForCalendar = useSelector(selectCurrentCandidate)
const role = useSelector(getRole)
const { userId } = useParams()
const [month, setMonth] = useState('')
const [fromDate, setFromDate] = useState(null)
const [toDate, setToDate] = useState(null)
const history = useHistory()
// useEffect(() => {
// fetchReportList({
// link: `${
// process.env.REACT_APP_API_URL
// }/api/reports/index?user_id=${userId}${getDateParamString({
// paramName: 'fromDate',
// value: fromDate
// })}${getDateParamString({
// paramName: 'toDate',
// value: toDate
// })}`,
// history,
// role,
// logout: () => {}
// })
// }, [])
useEffect(() => { useEffect(() => {
setMonth(currentMonth); setMonth(currentMonth)
}, [month]); }, [month])
const { name, skillsName, photo } = candidateForCalendar; const { name, skillsName, photo } = candidateForCalendar
const abbreviatedName = name && name.substring(0, name.lastIndexOf(' ')); const abbreviatedName = name && name.substring(0, name.lastIndexOf(' '))
return ( return (
<section className={style.calendar}> <section className='calendar'>
<div className="row"> <div className='row'>
<h2 className={style.calendar__title}> <h2 className='calendar__profile'>
Добрый день, <span>Александр !</span> Добрый день, <span>Александр !</span>
</h2> </h2>
<div className="col-12 col-xl-12 d-flex justify-content-between align-items-center flex-column flex-sm-row"> <div className='col-12 col-xl-12 d-flex justify-content-between align-items-center flex-column flex-sm-row'>
<div className={style.calendarHeader__info}> <div className='calendar__info'>
<img className={style.calendarHeader__info__img} src={photo} alt="img" /> <img className='calendar__info-img' src={photo} alt='img' />
<h3 className={style.calendarHeader__info__name}>{abbreviatedName}</h3> <h3 className='calendar__info-name'>{abbreviatedName}</h3>
</div> </div>
<div className={style.calendarHeader__title}> <div className='calendar__title'>
<h3 className={style.calendarHeader__title__text}>{skillsName} разработчик</h3> <h3 className='calendar__title-text'>{skillsName} разработчик</h3>
<img className={style.calendarHeader__title__img} src={rectangle} alt="img" /> <img className='calendar__title-img' src={rectangle} alt='img' />
</div> </div>
<div> <div>
<Link to="/report"> <Link to='/report'>
<button className={style.calendarHeader__btn}>Заполнить отчет за день</button> <button className='calendar__btn'>Заполнить отчет за день</button>
</Link> </Link>
</div>
</div> </div>
</div> </div>
</div>
<div className="row"> <div className='row'>
<div className="col-12 col-xl-12"> <div className='col-12 col-xl-12'>
<CalendarComponent /> <CalendarComponent onSelect={onSelect} />
<p className={style.calendarFooter__text}> <p className='calendar__hours'>
{month} : <span> 60 часов </span> {month} : <span> 60 часов </span>
</p> </p>
</div>
</div> </div>
<Footer /> </div>
<Footer />
</section> </section>
); )
}; }
export default Calendar; export default Calendar

View File

@ -1,299 +0,0 @@
.calendar {
margin-bottom: 40px;
}
.calendar__title {
color: #282828;
font-family: 'GT Eesti Pro Display';
font-size: 3.4em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 48.74px;
text-align: left;
margin-top: 80px;
margin-bottom: 60px;
}
@media (max-width: 575.98px) {
.calendar__title {
font-size: 5em;
text-align: center;
margin: 60px 0 40px 0;
}
}
@media (max-width: 560.98px) {
.calendar__title {
font-size: 4.8em;
}
}
@media (max-width: 540.98px) {
.calendar__title {
font-size: 4.6em;
}
}
@media (max-width: 520.98px) {
.calendar__title {
font-size: 4.4em;
}
}
@media (max-width: 486.98px) {
.calendar__title {
font-size: 4.2em;
}
}
@media (max-width: 466.98px) {
.calendar__title {
font-size: 4em;
}
}
@media (max-width: 446.98px) {
.calendar__title {
font-size: 3.8em;
}
}
@media (max-width: 425.98px) {
.calendar__title {
font-size: 3.6em;
}
}
.calendar__title > span {
color: #52b709;
font-style: normal;
letter-spacing: 0.56px;
line-height: normal;
}
.calendarHeader__info {
background-color: #f9f9f9;
display: flex;
position: relative;
width: 24%;
height: 112px;
}
@media (max-width: 575.98px) {
.calendarHeader__info {
width: 74%;
height: 94px;
margin-top: 40px;
margin-bottom: 40px;
}
}
@media (max-width: 375.98px) {
.calendarHeader__info {
width: 84%;
height: 94px;
margin-top: 20px;
}
}
.calendarHeader__info__img {
position: absolute;
width: 132px;
height: 132px;
left: -40px;
top: -10px;
}
@media (max-width: 575.98px) {
.calendarHeader__info__img {
position: absolute;
width: 110px;
height: 110px;
left: 0;
}
}
.calendarHeader__info__name {
font-family: 'GT Eesti Pro Display';
font-size: 2em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: 30px;
text-align: left;
position: absolute;
top: 50%;
left: 68%;
transform: translate(-50%, -50%);
}
@media (max-width: 575.98px) {
.calendarHeader__title {
margin-left: 100px;
}
}
@media (max-width: 505.98px) {
.calendarHeader__title {
margin-left: 80px;
}
}
@media (max-width: 455.98px) {
.calendarHeader__title {
margin-left: 60px;
}
}
@media (max-width: 375.98px) {
.calendarHeader__title {
margin-left: 40px;
}
}
@media (max-width: 348.98px) {
.calendarHeader__title {
margin-left: 0;
}
}
.calendarHeader__title__text {
font-family: 'GT Eesti Pro Display';
font-size: 2.7em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
}
@media (max-width: 575.98px) {
.calendarHeader__title__text {
font-size: 4.4em;
}
}
@media (max-width: 564.98px) {
.calendarHeader__title__text {
font-size: 4.2em;
}
}
@media (max-width: 544.98px) {
.calendarHeader__title__text {
font-size: 4em;
}
}
@media (max-width: 524.98px) {
.calendarHeader__title__text {
font-size: 3.6em;
}
}
@media (max-width: 504.98px) {
.calendarHeader__title__text {
font-size: 3.6em;
}
}
@media (max-width: 484.98px) {
.calendarHeader__title__text {
font-size: 3.4em;
}
}
@media (max-width: 464.98px) {
.calendarHeader__title__text {
font-size: 3.2em;
}
}
@media (max-width: 444.98px) {
.calendarHeader__title__text {
font-size: 3em;
}
}
@media (max-width: 375.98px) {
.calendarHeader__title__text {
font-size: 2.7em;
}
}
@media (max-width: 325.98px) {
.calendarHeader__title__text {
font-size: 2.7em;
}
}
@media (max-width: 575.98px) {
.calendarHeader__title__img {
width: 60%;
margin: 30px 0;
}
}
@media (max-width: 525.98px) {
.calendarHeader__title__img {
left: 15%;
}
}
@media (max-width: 414.98px) {
.calendarHeader__title__img {
left: 10%;
}
}
@media (max-width: 375.98px) {
.calendarHeader__title__img {
left: 5%;
}
}
.calendarHeader__btn {
width: 280px;
height: 62px;
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
border-radius: 31px;
background-color: #ffffff;
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%),
linear-gradient(
36deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.16) 47%,
rgba(255, 255, 255, 0.17) 50%,
rgba(255, 255, 255, 0) 100%
);
border: none;
color: #ffffff;
font-family: 'Muller';
font-size: 1.6em;
letter-spacing: normal;
text-align: center;
}
.calendarFooter__text {
font-family: 'GT Eesti Pro Display';
font-size: 2.2em;
font-weight: 600;
font-style: normal;
letter-spacing: normal;
line-height: 30px;
text-align: left;
margin-left: 68px;
}
.calendarFooter__text > span {
font-family: 'GT Eesti Pro Display';
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: normal;
}
.calendar footer {
margin-top: 2rem !important;
}

View File

@ -1,69 +1,63 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react'
import style from './CalendarComponent.module.css'; import ellipse from '../../images/ellipse.png'
import ellipse from '../../images/ellipse.png'; import rectangle from '../../images/rectangle__calendar.png'
import rectangle from '../../images/rectangle__calendar.png'; import calendarIcon from '../../images/calendar_icon.png'
import calendarIcon from '../../images/calendar_icon.png'; import moment from 'moment'
import moment from 'moment'; import 'moment/locale/ru'
import 'moment/locale/ru'; import { calendarHelper, currentMonthAndDay, } from './calendarHelper'
import { calendarHelper, currentMonthAndDay } from './calendarHelper';
const CalendarComponent = () => { import './calendarComponent.scss'
const [value, setValue] = useState(moment());
const [calendar, setCalendar] = useState([]); const CalendarComponent = ({ onSelect }) => {
const [value, setValue] = useState(moment())
const [calendar, setCalendar] = useState([])
useEffect(() => { useEffect(() => {
setCalendar(calendarHelper(value)); setCalendar(calendarHelper(value))
}, [value]); }, [value])
function beforeToday(day) { function beforeToday(day) {
return day.isBefore(new Date(), 'day'); return day.isBefore(new Date(), 'day')
} }
function isToday(day) { function isToday(day) {
return day.isSame(new Date(), 'day'); return day.isSame(new Date(), 'day')
} }
function dayStyles(day) { function dayStyles(day) {
if (beforeToday(day)) return `${style.before}`; if (beforeToday(day)) return `before`
if (isToday(day)) return `${style.today}`; if (isToday(day)) return `today`
if (day.day() === 6 || day.day() === 0) return `${style.selected}`; if (day.day() === 6 || day.day() === 0) return `selected`
return ''; return ''
} }
function prevMonth() { function prevMonth() {
return value.clone().subtract(1, 'month'); return value.clone().subtract(1, 'month')
} }
function nextMonth() { function nextMonth() {
return value.clone().subtract(2, 'month'); return value.clone().add(1, 'month');
} }
const prevMonthFirst = () => {
return moment().subtract(1, 'month').format('MMMM');
};
const prevMonthSecond = () => {
return moment().subtract(2, 'month').format('MMMM');
};
return ( return (
<div className={style.CalendarComponent}> <div className='calendar-component'>
<div className={style.CalendarComponent__header}> <div className='calendar-component__header'>
<h3>Мои отчеты</h3> <h3>Мои отчеты</h3>
<div className={style.CalendarComponent__header__box}> <div className='calendar-component__header-box'>
<img src={ellipse} alt="" /> <img src={ellipse} alt='' />
<span onClick={() => setValue(prevMonth())}>{prevMonthFirst()}</span> <span onClick={() => setValue(prevMonth())}>{prevMonth().format('MMMM')}</span>
</div> </div>
<div className={style.CalendarComponent__header__box}> <div className='calendar-component__header-box'>
<img src={ellipse} alt="" /> <img src={ellipse} alt='' />
<span onClick={() => setValue(nextMonth())}>{prevMonthSecond()}</span> <span onClick={() => setValue(nextMonth())}>{nextMonth().format('MMMM')}</span>
</div> </div>
</div> </div>
<div className={style.CalendarComponent__rectangle}> <div className='calendar-component__rectangle'>
<img src={rectangle} alt="" /> <img src={rectangle} alt='' />
</div> </div>
<div className={style.CalendarComponent__body}> <div className='calendar-component__body'>
<div> <div>
<p>Пн</p> <p>Пн</p>
<p>Вт</p> <p>Вт</p>
@ -74,17 +68,17 @@ const CalendarComponent = () => {
<p>Вс</p> <p>Вс</p>
</div> </div>
<div className={style.CalendarComponent__form}> <div className='calendar-component__form'>
{calendar.map((week) => {calendar.map((week) =>
week.map((day) => ( week.map((day) => (
<button <button
onClick={() => setValue(day)} onClick={() => { setValue(day); onSelect(day) }}
key={day} key={day}
className={dayStyles(day)} className={dayStyles(day)}
name={day.format('dddd')} name={day.format('dddd')}
id="btn" id='btn'
> >
<img className={style.calendarIcon} src={calendarIcon} alt="" /> <img className={'calendar__icon'} src={calendarIcon} alt='' />
{currentMonthAndDay(day)} {currentMonthAndDay(day)}
</button> </button>
)) ))
@ -92,7 +86,7 @@ const CalendarComponent = () => {
</div> </div>
</div> </div>
</div> </div>
); )
}; }
export default CalendarComponent; export default CalendarComponent

View File

@ -1,244 +0,0 @@
.CalendarComponent {
position: relative;
margin-top: 80px;
margin-bottom: 60px;
background-color: #f9f9f9;
padding-left: 68px;
padding-right: 54px;
padding-top: 48px;
padding-bottom: 94px;
}
@media (max-width: 575.98px) {
.CalendarComponent {
margin-bottom: 40px;
padding: 28px 0 48px 0;
}
}
.CalendarComponent__header {
display: flex;
}
.CalendarComponent__header > h3 {
font-family: 'GT Eesti Pro Display';
font-size: 2.5em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: 30px;
text-align: left;
}
@media (max-width: 575.98px) {
.CalendarComponent__header > h3 {
position: absolute;
top: -10%;
left: 25%;
}
}
.CalendarComponent__header__box {
display: flex;
align-items: center;
margin-left: 40px;
}
@media (max-width: 575.98px) {
.CalendarComponent__header__box {
margin-left: 20px;
}
}
.CalendarComponent__header__box > img {
width: 6px;
height: 6px;
}
.CalendarComponent__header__box > span {
color: #18586e;
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
margin-left: 10px;
cursor: pointer;
}
.CalendarComponent__rectangle {
margin: 36px 0;
}
@media (max-width: 575.98px) {
.CalendarComponent__rectangle {
margin: 24px 0;
}
}
.CalendarComponent__rectangle > img {
width: 100%;
}
.CalendarComponent__body > div {
display: flex;
justify-content: space-around;
}
.CalendarComponent__body > div > p {
color: #398208;
font-family: 'GT Eesti Pro Display';
font-size: 25px;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: 30px;
text-align: left;
}
.CalendarComponent__form {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.CalendarComponent__form > button {
width: 125px;
height: 42px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
margin-top: 20px;
font-family: 'GT Eesti Pro Display';
font-size: 1.2em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: center;
}
@media (max-width: 575.98px) {
.CalendarComponent__form > button {
width: 72px;
height: 40px;
}
}
@media (max-width: 560.98px) {
.CalendarComponent__form > button {
width: 70px;
height: 40px;
}
}
@media (max-width: 540.98px) {
.CalendarComponent__form > button {
width: 68px;
height: 40px;
}
}
@media (max-width: 520.98px) {
.CalendarComponent__form > button {
width: 66px;
height: 40px;
}
}
@media (max-width: 500.98px) {
.CalendarComponent__form > button {
width: 64px;
height: 40px;
}
}
@media (max-width: 480.98px) {
.CalendarComponent__form > button {
width: 60px;
height: 40px;
}
}
@media (max-width: 460.98px) {
.CalendarComponent__form > button {
width: 56px;
height: 40px;
}
}
@media (max-width: 440.98px) {
.CalendarComponent__form > button {
width: 52px;
height: 40px;
}
}
@media (max-width: 428.98px) {
.CalendarComponent__form > button {
width: 50px;
height: 40px;
}
}
@media (max-width: 414.98px) {
.CalendarComponent__form > button {
width: 49px;
height: 40px;
}
}
@media (max-width: 395.98px) {
.CalendarComponent__form > button {
width: 46px;
height: 40px;
}
}
@media (max-width: 350.98px) {
.CalendarComponent__form > button {
width: 44px;
height: 40px;
}
}
@media (max-width: 349.98px) {
.CalendarComponent__form > button {
width: 42px;
height: 40px;
}
}
@media (max-width: 346.98px) {
.CalendarComponent__form > button {
width: 40px;
height: 40px;
}
}
.calendarIcon {
margin-right: 10px;
margin-top: -4px;
}
@media (max-width: 575.98px) {
.calendarIcon {
display: none;
}
}
.before {
background-color: #f0f7e0 !important;
}
.today {
font-weight: bold !important;
border: 1px solid #398208 !important;
}
.selected {
background-color: #f9f9c3 !important;
}

View File

@ -0,0 +1,301 @@
.calendar {
margin-bottom: 40px;
&__profile {
color: #282828;
font-family: 'GT Eesti Pro Display';
font-size: 3.4em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 48.74px;
text-align: left;
margin-top: 80px;
margin-bottom: 60px;
span {
color: #52b709;
font-style: normal;
letter-spacing: 0.56px;
line-height: normal;
}
}
&__info {
background-color: #f9f9f9;
display: flex;
position: relative;
width: 24%;
height: 112px;
&-name {
font-family: 'GT Eesti Pro Display';
font-size: 2em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: 30px;
text-align: left;
position: absolute;
top: 50%;
left: 68%;
transform: translate(-50%, -50%);
}
&-img {
position: absolute;
width: 132px;
height: 132px;
left: -40px;
top: -10px;
}
}
&__title {
&-text {
font-family: 'GT Eesti Pro Display';
font-size: 2.7em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
}
}
&__btn {
width: 280px;
height: 62px;
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
border-radius: 31px;
background-color: #ffffff;
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%),
linear-gradient(
36deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.16) 47%,
rgba(255, 255, 255, 0.17) 50%,
rgba(255, 255, 255, 0) 100%
);
border: none;
color: #ffffff;
font-family: 'Muller';
font-size: 1.6em;
letter-spacing: normal;
text-align: center;
}
&__hours {
font-family: 'GT Eesti Pro Display';
font-size: 2.2em;
font-weight: 600;
font-style: normal;
letter-spacing: normal;
line-height: 30px;
text-align: left;
margin-left: 68px;
span {
font-family: 'GT Eesti Pro Display';
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: normal;
}
}
footer {
margin-top: 2rem !important;
}
}
@media (max-width: 575.98px) {
.calendar__profile {
font-size: 5em;
text-align: center;
margin: 60px 0 40px 0;
}
}
@media (max-width: 560.98px) {
.calendar__profile {
font-size: 4.8em;
}
}
@media (max-width: 540.98px) {
.calendar__profile {
font-size: 4.6em;
}
}
@media (max-width: 520.98px) {
.calendar__profile {
font-size: 4.4em;
}
}
@media (max-width: 486.98px) {
.calendar__profile {
font-size: 4.2em;
}
}
@media (max-width: 466.98px) {
.calendar__profile {
font-size: 4em;
}
}
@media (max-width: 446.98px) {
.calendar__profile {
font-size: 3.8em;
}
}
@media (max-width: 425.98px) {
.calendar__profile {
font-size: 3.6em;
}
}
@media (max-width: 575.98px) {
.calendar__info {
width: 74%;
height: 94px;
margin-top: 40px;
margin-bottom: 40px;
}
}
@media (max-width: 375.98px) {
.calendar__info {
width: 84%;
height: 94px;
margin-top: 20px;
}
}
@media (max-width: 575.98px) {
.calendar__info-img {
position: absolute;
width: 110px;
height: 110px;
left: 0;
}
}
@media (max-width: 575.98px) {
.calendar__title {
margin-left: 100px;
}
}
@media (max-width: 505.98px) {
.calendarHeader__title {
margin-left: 80px;
}
}
@media (max-width: 455.98px) {
.calendar__title {
margin-left: 60px;
}
}
@media (max-width: 375.98px) {
.calendar__title {
margin-left: 40px;
}
}
@media (max-width: 348.98px) {
.calendar__title {
margin-left: 0;
}
}
@media (max-width: 575.98px) {
.calendar__title-text {
font-size: 4.4em;
}
}
@media (max-width: 564.98px) {
.calendar__title-text {
font-size: 4.2em;
}
}
@media (max-width: 544.98px) {
.calendar__title-text {
font-size: 4em;
}
}
@media (max-width: 524.98px) {
.calendar__title-text {
font-size: 3.6em;
}
}
@media (max-width: 504.98px) {
.calendar__title-text {
font-size: 3.6em;
}
}
@media (max-width: 484.98px) {
.calendar__title-text {
font-size: 3.4em;
}
}
@media (max-width: 464.98px) {
.calendar__title-text {
font-size: 3.2em;
}
}
@media (max-width: 444.98px) {
.calendar__title-text {
font-size: 3em;
}
}
@media (max-width: 375.98px) {
.calendar__title-text {
font-size: 2.7em;
}
}
@media (max-width: 325.98px) {
.calendar__title-text {
font-size: 2.7em;
}
}
@media (max-width: 575.98px) {
.calendar__title-img {
width: 60%;
margin: 30px 0;
}
}
@media (max-width: 525.98px) {
.calendar__title-img {
left: 15%;
}
}
@media (max-width: 414.98px) {
.calendar__title-img {
left: 10%;
}
}
@media (max-width: 375.98px) {
.calendar__title-img {
left: 5%;
}
}

View File

@ -0,0 +1,263 @@
.calendar-component {
position: relative;
margin-top: 80px;
margin-bottom: 60px;
background-color: #f9f9f9;
padding-left: 68px;
padding-right: 54px;
padding-top: 48px;
padding-bottom: 94px;
&__header {
display: flex;
h3 {
font-family: 'GT Eesti Pro Display';
font-size: 2.5em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: 30px;
text-align: left;
}
&-box {
display: flex;
align-items: center;
margin-left: 40px;
img {
width: 6px;
height: 6px;
}
span {
color: #18586e;
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
margin-left: 10px;
cursor: pointer;
}
}
}
&__rectangle {
margin: 36px 0;
img {
width: 100%;
}
}
&__body {
div {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
p {
color: #398208;
font-family: 'GT Eesti Pro Display';
font-size: 25px;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: 30px;
text-align: center;
}
}
}
&__form {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
button {
margin: 0 auto;
width: 125px;
height: 42px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
margin-top: 20px;
font-family: 'GT Eesti Pro Display';
font-size: 1.2em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: center;
}
}
}
@media (max-width: 1200px) {
.calendar-component {
&__form {
button {
width: 100px;
height: 40px;
}
}
}
}
@media (max-width: 968px) {
.calendar-component {
margin-bottom: 40px;
padding: 28px 0 48px 0;
&__header {
h3 {
position: absolute;
top: -10%;
left: 25%;
}
&-box {
margin-left: 20px;
}
}
&__rectangle {
margin: 24px 0;
}
&__form {
button {
width: 72px;
height: 40px;
img {
display: none;
}
}
}
}
}
@media (max-width: 768px) {
.calendar-component__form > button {
width: 70px;
height: 40px;
img {
display: none;
}
}
}
@media (max-width: 540.98px) {
.calendar-component__form > button {
width: 68px;
height: 40px;
}
}
@media (max-width: 520.98px) {
.calendar-component__form > button {
width: 66px;
height: 40px;
}
}
@media (max-width: 500.98px) {
.calendar-component__form > button {
width: 64px;
height: 40px;
}
}
@media (max-width: 480.98px) {
.calendar-component__form > button {
width: 60px;
height: 40px;
}
}
@media (max-width: 460.98px) {
.calendar-component__form > button {
width: 56px;
height: 40px;
}
}
@media (max-width: 440.98px) {
.calendar-component__form > button {
width: 52px;
height: 40px;
}
}
@media (max-width: 428.98px) {
.calendar-component__form > button {
width: 50px;
height: 40px;
}
}
@media (max-width: 414.98px) {
.calendar-component__form > button {
width: 49px;
height: 40px;
}
}
@media (max-width: 395.98px) {
.calendar-component__form > button {
width: 46px;
height: 40px;
}
}
@media (max-width: 350.98px) {
.calendar-component__form > button {
width: 44px;
height: 40px;
}
}
@media (max-width: 349.98px) {
.calendar-component__form > button {
width: 42px;
height: 40px;
}
}
@media (max-width: 346.98px) {
.calendar-component__form > button {
width: 40px;
height: 40px;
}
}
.calendar__icon {
margin-right: 10px;
margin-top: -4px;
}
@media (max-width: 575.98px) {
.calendar__icon {
display: none;
}
}
.before {
background-color: #f0f7e0 !important;
}
.today {
font-weight: bold !important;
border: 1px solid #398208 !important;
}
.selected {
background-color: #f9f9c3 !important;
}

View File

@ -1,140 +1,195 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react'
import { useHistory, useParams, Link } from 'react-router-dom'; import { useHistory, useParams, Link } from 'react-router-dom'
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux'
import { currentCandidate, selectCurrentCandidate, auth } from '../../redux/outstaffingSlice'; import {
import arrow from '../../images/right-arrow.png'; currentCandidate,
import rectangle from '../../images/rectangle_secondPage.png'; selectCurrentCandidate,
import Sidebar from '../Sidebar/Sidebar'; auth
import SectionSkills from './SectionSkills'; } from '../../redux/outstaffingSlice'
import front from '../../images/front_end.png'; import arrow from '../../images/right-arrow.png'
import back from '../../images/back_end.png'; import rectangle from '../../images/rectangle_secondPage.png'
import design from '../../images/design.png'; import Sidebar from '../CandidateSidebar/CandidateSidebar'
import { fetchItemsForId } from '../../server/server'; import SkillSection from '../SkillSection/SkillSection'
import { Footer } from '../Footer/Footer'; import front from '../../images/front_end.png'
import back from '../../images/back_end.png'
import design from '../../images/design.png'
import { fetchGet } from '../../server/server'
import { Footer } from '../Footer/Footer'
import './candidate.css'; import './candidate.scss'
import { getRole } from '../../redux/roleSlice'; import { getRole } from '../../redux/roleSlice'
import { CodeSnippetlighter } from '../../pages/CodeSnippetPage'
import { useState } from 'react'
const Candidate = () => { const Candidate = () => {
const history = useHistory(); const history = useHistory()
const { id: candidateId } = useParams(); const { id: candidateId } = useParams()
const dispatch = useDispatch(); const dispatch = useDispatch()
const role = useSelector(getRole); const role = useSelector(getRole)
const [activeSnippet, setActiveSnippet] = useState(true)
useEffect(() => { useEffect(() => {
window.scrollTo(0, 0) window.scrollTo(0, 0)
}, []) }, [])
useEffect(() => { useEffect(() => {
fetchItemsForId({ link: `${process.env.REACT_APP_API_URL}/api/profile/`, index:Number(candidateId), history, role, logout: dispatch(auth(false)) }).then((el) => fetchGet({
dispatch(currentCandidate(el)) link: `${process.env.REACT_APP_API_URL}/api/profile/${candidateId}`,
); params: Number(candidateId),
}, [dispatch, candidateId]); history,
role,
logout: () => dispatch(auth(false))
}).then((el) => dispatch(currentCandidate(el)))
}, [dispatch, candidateId])
const currentCandidateObj = useSelector(selectCurrentCandidate); const currentCandidateObj = useSelector(selectCurrentCandidate)
const { position_id, skillValues, vc_text: text } = currentCandidateObj; const { position_id, skillValues, vc_text: text } = currentCandidateObj
const setStyles = () => { const setStyles = () => {
const styles = { const styles = {
classes: '', classes: '',
header: '', header: '',
img: '', img: ''
}; }
switch (Number(position_id)) { switch (Number(position_id)) {
case 1: { case 1: {
styles.classes = 'back'; styles.classes = 'back'
styles.header = 'Backend'; styles.header = 'Backend'
styles.img = back; styles.img = back
break; break
} }
case 2: { case 2: {
styles.classes = 'des'; styles.classes = 'des'
styles.header = 'Frontend'; styles.header = 'Frontend'
styles.img = front; styles.img = front
break; break
} }
case 3: { case 3: {
styles.classes = 'front'; styles.classes = 'front'
styles.header = 'Design'; styles.header = 'Design'
styles.img = design; styles.img = design
break; break
} }
default: default:
break; break
} }
return styles; return styles
};
function createMarkup(text) {
return { __html: text.split('</p>').join('</p>') };
} }
const { header, img, classes } = setStyles(); function createMarkup(text) {
return { __html: text.split('</p>').join('</p>') }
}
const { header, img, classes } = setStyles()
return ( return (
<div className='candidate'> <div className='candidate'>
<div className="row"> <div className='row'>
<div className="col-12"> <div className='col-12'>
<div className='candidate__title'> <div className='candidate__title'>
<h2> <h2>
<span>Аутстаффинг</span> it-персонала <span>Аутстаффинг</span> it-персонала
</h2> </h2>
</div>
</div>
</div>
<div className='row'>
<div className='col-12'>
<div className='candidate__header'>
<div className='candidate__arrow' onClick={() => history.push('/')}>
<div className='candidate__arrow-img'>
<img src={arrow} alt='' />
</div>
<div className='candidate__arrow-sp'>
<span>Вернуться к списку</span>
</div>
</div>
<div className='candidate__icon'>
<h3>{header}</h3>
<img className={classes} src={img} alt='' />
</div> </div>
</div> </div>
</div> </div>
</div>
<div className="row"> <div className='candidate__main'>
<div className="col-12"> <div className='row'>
<div className='candidate__header'> <div className='col-12 col-xl-4'>
<div className='arrow' onClick={() => history.push('/')}> <Sidebar candidate={currentCandidateObj} position activeSnippet={activeSnippet} setActiveSnippet={setActiveSnippet}/>
<div className='arrow__img'>
<img src={arrow} alt="" />
</div>
<div className='arrow__sp'>
<span>Вернуться к списку</span>
</div>
</div>
<div className='icon'>
<h3>{header}</h3>
<img className={classes} src={img} alt="" />
</div>
</div>
</div> </div>
</div> {
<div className='candidate__main'> activeSnippet ?
<div className="row"> (
<div className="col-12 col-xl-4"> <div className='col-12 col-xl-8'>
<Sidebar candidate={currentCandidateObj} /> <div className='candidate__main-description'>
</div> <img src={rectangle} alt='' />
<div className="col-12 col-xl-8"> <p className='candidate__hashtag'># Описание опыта</p>
<div className='candidate__main__description'> {text ? (
<img src={rectangle} alt="" /> <div
<p className='hashtag'># Описание опыта</p> className='candidate__text'
{text ? ( dangerouslySetInnerHTML={createMarkup(text)}
<div className='candidate__text' dangerouslySetInnerHTML={createMarkup(text)}></div> ></div>
) : ( ) : (
<p className='candidate__textSecondary'> <p className='candidate__text-secondary'>
{currentCandidateObj.vc_text ? currentCandidateObj.vc_text : 'Описание отсутствует...' } {currentCandidateObj.vc_text
</p> ? currentCandidateObj.vc_text
)} : 'Описание отсутствует...'}
{/* <Link to={`/candidate/${currentCandidateObj.id}/form`}> </p>
)}
{/* <Link to={`/candidate/${currentCandidateObj.id}/form`}>
<button type="submit" className='candidate__btn'> <button type="submit" className='candidate__btn'>
Выбрать к собеседованию Выбрать к собеседованию
</button> </button>
</Link> */} </Link> */}
<SectionSkills skillsArr={skillValues} /> <SkillSection skillsArr={skillValues} />
</div>
</div>
) :
(
// <div className="col-12 col-xl-8">
// <CodeSnippetlighter />
// </div>
<div className="col-12 col-xl-8">
<div className="candidate__works works">
<div className="works__body">
<div className="works__item item-works">
<div className="item-works__body">
<Link to="/" className="item-works__link">Vuetifyis.com</Link>
<div className="item-works__text">Forked from peluprvi/vuetifyjs.com <br /> Vuetifyjs.com documentation</div>
<div className="item-works__mark">Angular</div>
</div>
</div>
<div className="works__item item-works">
<div className="item-works__body">
<Link to="/" className="item-works__link">Vuetifyis.com</Link>
<div className="item-works__text">Forked from peluprvi/vuetifyjs.com <br /> Vuetifyjs.com documentation</div>
<div className="item-works__mark">Angular</div>
</div>
</div>
<div className="works__item item-works">
<div className="item-works__body">
<Link to="/" className="item-works__link">Vuetifyis.com</Link>
<div className="item-works__text">Forked from peluprvi/vuetifyjs.com <br /> Vuetifyjs.com documentation</div>
<div className="item-works__mark item-works__mark_yellow">Laravel</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> )
<Footer /> }
</div>
);
};
export default Candidate; </div>
</div>
<Footer />
</div>
)
}
export default Candidate

View File

@ -1,13 +0,0 @@
import React from 'react';
import './candidate.css';
const SectionSkills = ({ skillsArr }) => {
return (
<div className='SectionSkills'>
<h3>Навыки:</h3>
<ul>{skillsArr && skillsArr.map((skills) => <li key={skills.id}>{skills.skill.name}</li>)}</ul>
</div>
);
};
export default SectionSkills;

View File

@ -1,309 +0,0 @@
.candidate__title {
margin-top: 60px;
}
.candidate__title > h2 {
text-align: center;
color: #52b709;
font-family: 'GT Eesti Pro Display';
font-size: 5em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 77.81px;
}
@media (max-width: 575.98px) {
.candidate__title > h2 {
font-size: 5em;
line-height: normal;
}
}
@media (max-width: 375.98px) {
.candidate__title > h2 {
font-size: 4.5em;
line-height: normal;
}
}
.candidate__title > h2 > span {
color: #282828;
font-style: normal;
letter-spacing: 0.56px;
line-height: normal;
}
.candidate__header {
display: flex;
align-items: center;
margin-top: 120px;
margin-left: 60px;
}
@media (max-width: 768px) {
.candidate__header {
flex-direction: column;
}
.candidate__header .arrow {
margin-left: 60px;
margin-bottom: 40px;
}
}
@media (max-width: 575.98px) {
.candidate__header {
display: flex;
flex-direction: column;
margin-left: 0;
margin-top: 40px;
}
.candidate__header .arrow {
margin-left: 0;
}
}
.arrow {
display: flex;
justify-content: space-between;
align-items: center;
}
@media (max-width: 575.98px) {
.arrow {
margin-bottom: 40px;
}
}
.arrow__img > img {
cursor: pointer;
}
.arrow__sp > span {
margin-left: 40px;
margin-right: 120px;
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
}
@media (max-width: 575.98px) {
.arrow__sp > span {
margin-right: 0px;
}
}
.icon {
min-width: 260px;
min-height: 120px;
background-color: #f9f9f9;
border-radius: 20px;
position: relative;
}
.icon > h3 {
position: absolute;
right: 20%;
top: 40%;
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
}
.icon > img {
position: absolute;
}
.front {
left: -5%;
}
.back {
top: 11%;
left: -9%;
}
.des {
top: 24%;
left: -9%;
}
.candidate__main {
margin-top: 60px;
}
.candidate__main__description {
padding-left: 16px;
}
.candidate__main__description > h2 {
font-family: 'GT Eesti Pro Display';
font-size: 2.8em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
}
@media (max-width: 575.98px) {
.candidate__main__description > h2 {
font-size: 3.2em;
text-align: center;
position: absolute;
top: -410px;
left: 0;
}
}
@media (max-width: 375.98px) {
.candidate__main__description > h2 {
font-size: 3em;
}
}
@media (max-width: 345.98px) {
.candidate__main__description > h2 {
font-size: 2.6em;
}
}
@media (max-width: 575.98px) {
.candidate__main__description > img {
/* width: 50%;
display: block;
margin: 0 auto; */
display: none;
}
}
.candidate__main__description > p {
font-family: 'GT Eesti Pro Display';
font-size: 1.2em;
font-weight: 300;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
margin: 20px 0px;
}
@media (min-width: 576.98px) {
.candidate__btn {
display: none;
}
}
@media (max-width: 575.98px) {
.candidate__btn {
display: block;
width: 221px;
height: 49px;
box-shadow: 0 8px 20px rgba(82, 151, 34, 0.21);
border-radius: 24px;
background-color: #ffffff;
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%),
linear-gradient(
36deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.16) 47%,
rgba(255, 255, 255, 0.17) 50%,
rgba(255, 255, 255, 0) 100%
);
color: #ffffff;
font-family: 'Muller';
font-size: 1.3em;
letter-spacing: normal;
text-align: left;
border: none;
text-align: center;
margin: 28px auto;
}
}
.SectionSkills {
display: flex;
border: 1px solid #69bf2c;
padding: 28px 40px 16px 30px;
margin-top: 60px;
border-radius: 10px;
margin-bottom: 60px;
}
@media (max-width: 575.98px) {
.SectionSkills {
display: none;
}
}
.SectionSkills > h3 {
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 28px;
text-align: left;
}
.SectionSkills > ul {
list-style: none;
display: flex;
padding-left: 0;
flex-wrap: wrap;
}
.SectionSkills > ul > li {
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 28px;
text-align: left;
margin-left: 20px;
}
.candidate__text > p {
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
text-align: left;
}
.candidate__textSecondary {
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 24px;
text-align: left;
line-height: 28px;
}
.candidate + .logout-button{
top: 80px !important;
}
.candidate footer {
margin-top: 2.5rem !important;
}
@media (max-width: 1199px) {
.candidate + .logout-button {
top: 16px !important;
}
}

View File

@ -0,0 +1,376 @@
@use 'sass:math';
.candidate {
&__title {
margin-top: 60px;
h2 {
text-align: center;
color: #52b709;
font-family: 'GT Eesti Pro Display';
font-size: 5em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 77.81px;
span {
color: #282828;
font-style: normal;
letter-spacing: 0.56px;
line-height: normal;
}
}
}
&__header {
display: flex;
align-items: center;
margin-top: 120px;
margin-left: 60px;
}
&__main {
margin-top: 60px;
&-description {
padding-left: 16px;
h2 {
font-family: 'GT Eesti Pro Display';
font-size: 2.8em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
}
p {
font-family: 'GT Eesti Pro Display';
font-size: 1.2em;
font-weight: 300;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
margin: 20px 0px;
}
}
}
&__text {
p {
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
text-align: left;
}
&-secondary {
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 24px;
text-align: left;
line-height: 28px;
}
}
&__icon {
min-width: 260px;
min-height: 120px;
background-color: #f9f9f9;
border-radius: 20px;
position: relative;
img {
position: absolute;
}
h3 {
position: absolute;
right: 20%;
top: 40%;
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
}
}
&__arrow {
display: flex;
justify-content: space-between;
align-items: center;
}
footer {
margin-top: 2.5rem !important;
}
}
@media (max-width: 575.98px) {
.candidate {
&__title {
h2 {
font-size: 5em;
line-height: normal;
}
}
&__arrow {
margin-bottom: 40px;
&-img {
img {
cursor: pointer;
}
}
&-sp {
span {
margin-left: 40px;
margin-right: 120px;
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
}
}
}
}
}
@media (max-width: 375.98px) {
.candidate {
&__title {
h2 {
font-size: 4.5em;
line-height: normal;
}
}
}
}
@media (max-width: 768px) {
.candidate {
&__header {
flex-direction: column;
}
&__arrow {
margin-left: 60px;
margin-bottom: 40px;
}
}
}
@media (max-width: 575.98px) {
.candidate {
&__header {
display: flex;
flex-direction: column;
margin-left: 0;
margin-top: 40px;
}
&__main {
&-description {
h2 {
font-size: 3.2em;
text-align: center;
position: absolute;
top: -410px;
left: 0;
}
img {
display: none;
}
}
}
&__btn {
display: block;
width: 221px;
height: 49px;
box-shadow: 0 8px 20px rgba(82, 151, 34, 0.21);
border-radius: 24px;
background-color: #ffffff;
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%),
linear-gradient(
36deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.16) 47%,
rgba(255, 255, 255, 0.17) 50%,
rgba(255, 255, 255, 0) 100%
);
color: #ffffff;
font-family: 'Muller';
font-size: 1.3em;
letter-spacing: normal;
text-align: left;
border: none;
text-align: center;
margin: 28px auto;
}
&__arrow {
margin-left: 0;
&-sp {
span {
margin-right: 0px;
}
}
}
}
}
.front {
left: -5%;
}
.back {
top: 11%;
left: -9%;
}
.des {
top: 24%;
left: -9%;
}
@media (max-width: 1199px) {
.candidate + .logout-button {
top: 16px !important;
}
}
@media (max-width: 375.98px) {
.candidate {
&__main {
&-description {
h2 {
font-size: 3em;
}
}
}
}
}
@media (max-width: 345.98px) {
.candidate {
&__main {
&-description {
h2 {
font-size: 2.6em;
}
}
}
}
}
@media (min-width: 576.98px) {
.candidate {
&__btn {
display: none;
}
}
}
.candidate + .logout-button {
top: 80px !important;
}
.candidate__works{
@media (min-width: 576px) {
padding: 0 0 45px 0;
}
}
.works__body{
display: flex;
flex-wrap: wrap;
margin: 0 -15px -30px;
}
.item-works{
display: flex;
flex-direction: column;
padding: 0 15px;
margin: 0 0 30px 0;
flex: 0 1 50%;
position: relative;
@media (max-width: 761px) {
flex: 0 1 100%;
}
&__body{
padding: 17px;
border: 2px solid #54b611;
border-radius: 10px;
&::before{
content: "";
position: absolute;
top: -2px;
left: 25px;
height: 5px;
width: 29px;
border-radius: 3px;
background-color: #54b611;
}
}
&__link{
color: #0350dc;
font-family: "GT Eesti Pro Display";
font-size: 16px;
font-weight: 400;
letter-spacing: normal;
line-height: math.div(36,16);
text-decoration: underline;
display: block;
margin-bottom: 17px;
}
&__text{
margin-bottom: 37px;
color: #000000;
font-family: "GT Eesti Pro Display - Thin";
font-size: 12px;
font-weight: 400;
line-height: math.div(18,12);
}
&__mark{
font-size: 13px;
display: flex;
align-items: center;
column-gap: 7px;
line-height: math.div(36,13);
&::before{
border-radius: 50%;
content: "";
width: 13px;
height: 13px;
background-color: #73c141;
}
&_yellow{
&::before{
background-color: #e09f14;
}
}
}
}

View File

@ -0,0 +1,82 @@
import React from 'react'
import { Link } from 'react-router-dom'
import { Achievement } from '../Achievement/Achievement'
import { LEVELS, SKILLS } from '../constants/constants'
import maleBig from '../../images/medium_male_big.png'
import './candidateSidebar.scss'
import { Highlighter } from '../../App'
import { useState } from 'react'
import { useEffect } from 'react'
const getYearsString = (years) => {
let yearsString
if (years % 10 === 1) {
yearsString = 'год'
} else if (years === 11 || years === 12 || years === 13 || years === 14) {
yearsString = 'лет'
} else if (years % 10 === 2 || years % 10 === 3 || years % 10 === 4) {
yearsString = 'года'
} else {
yearsString = 'лет'
}
return `${years} ${yearsString}`
}
const CandidateSidebar = ({ candidate, position, setActiveSnippet, activeSnippet }) => {
const showSnippet = () => {
setActiveSnippet((prev)=>!prev)
}
return (
<div className='candidate-sidebar'>
<div className='candidate-sidebar__info'>
<div className='candidate-sidebar__position'>
<h2>
{candidate.specification} {SKILLS[candidate.position_id]},{' '}
{LEVELS[candidate.level]}{' '}
</h2>
</div>
<img src={candidate.photo} alt='' />
{candidate && candidate.years_of_exp && (
<>
<p className='candidate-sidebar__experience-title'>Опыт работы</p>
<p className='candidate-sidebar__experience'>
{getYearsString(candidate.years_of_exp)}
</p>
</>
)}
<Link to={`/candidate/${candidate.id}/form`}>
<button className='candidate-sidebar__select'>
Выбрать к собеседованию
</button>
</Link>
<Link to={`/${candidate.id}/calendar`}>
<button className='candidate-sidebar__select'>
Отчёты
</button>
</Link>
{/* <Link to={`/candidate/${candidate.id}/code`}> */}
<button
className='candidate-sidebar__select'
onClick={showSnippet}
>
{activeSnippet ? "Показать": "Скрыть"}
</button>
{/* </Link> */}
<div className='candidate-sidebar__achievements'>
{candidate &&
candidate.achievements &&
candidate.achievements.map((item) => {
return <Achievement key={item.id} achievement={item.achievement} />
})}
</div>
</div>
</div>
)
}
export default CandidateSidebar

View File

@ -1,4 +1,4 @@
.candidateSidebar { .candidate-sidebar {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -7,6 +7,19 @@
position: sticky; position: sticky;
top: 80px; top: 80px;
&__position {
margin-bottom: 1rem;
h2 {
font-size: 3rem !important;
font-family: 'GT Eesti Pro Display';
font-size: 2.6em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
}
}
&__achievements { &__achievements {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@ -14,26 +27,32 @@
padding: 0 1rem; padding: 0 1rem;
margin-bottom: 80px; margin-bottom: 80px;
} }
}
.candidateSidebar__info { &__info {
text-align: center; text-align: center;
margin-top: 40px; margin-top: 10px;
} width: 100%;
display: flex;
@media (max-width: 575.98px) { justify-content: center;
.candidateSidebar__info { align-items: flex-start;
margin-top: 140px; flex-direction: column;
} }
} }
.candidateSidebar__info > img { @media (max-width: 575.98px) {
.candidate-sidebar__info {
margin-top: 30px;
align-items: center;
}
}
.candidate-sidebar__info > img {
width: 180px; width: 180px;
height: 180px; height: 180px;
border-radius: 100px; border-radius: 100px;
} }
.candidateSidebar__experience-title { .candidate-sidebar__experience-title {
font-family: 'GT Eesti Pro Display'; font-family: 'GT Eesti Pro Display';
font-size: 1.8em; font-size: 1.8em;
font-weight: normal; font-weight: normal;
@ -43,7 +62,7 @@
margin-top: 20px; margin-top: 20px;
} }
.candidateSidebar__experience { .candidate-sidebar__experience {
font-family: 'GT Eesti Pro Display'; font-family: 'GT Eesti Pro Display';
font-size: 3em; font-size: 3em;
font-weight: 700; font-weight: 700;
@ -52,7 +71,7 @@
line-height: normal; line-height: normal;
} }
.candidateSidebar__select { .candidate-sidebar__select {
width: 280px; width: 280px;
height: 60px; height: 60px;
border-radius: 100px; border-radius: 100px;
@ -66,16 +85,16 @@
line-height: normal; line-height: normal;
text-align: center; text-align: center;
margin-top: 20px; margin-top: 20px;
margin-bottom: 40px; margin-bottom: 10px;
} }
.candidateSidebar__select:hover { .candidate-sidebar__select:hover {
background: rgba(0, 0, 0, 0); background: rgba(0, 0, 0, 0);
color: #73c141; color: #73c141;
box-shadow: inset 0 0 0 3px #73c141; box-shadow: inset 0 0 0 3px #73c141;
} }
.candidateSidebar__info__l { .candidate-sidebar__info__l {
font-family: 'GT Eesti Pro Display'; font-family: 'GT Eesti Pro Display';
font-size: 1.8em; font-size: 1.8em;
font-weight: 600; font-weight: 600;
@ -85,20 +104,20 @@
text-align: center; text-align: center;
} }
.candidateSidebar__arrows { .candidateS-sidebar__arrows {
display: flex; display: flex;
align-items: center; align-items: center;
margin-top: 20px; margin-top: 20px;
} }
@media (max-width: 575.98px) { @media (max-width: 575.98px) {
.candidateSidebar__info__l, .candidate-sidebar__info__l,
.candidateSidebar__arrows { .candidate-sidebar__arrows {
display: none; display: none;
} }
} }
.arrow__left { .candidate-sidebar__arrow--left {
position: relative; position: relative;
width: 30px; width: 30px;
height: 30px; height: 30px;
@ -106,14 +125,14 @@
background-color: #f6f6f6; background-color: #f6f6f6;
} }
.arrow__left > img { .candidate-sidebar__arrow--left > img {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }
.arrows__sp { .candidate-sidebar__arrows-sp {
color: #705fa3; color: #705fa3;
font-family: 'Circe'; font-family: 'Circe';
font-size: 1.3em; font-size: 1.3em;
@ -124,7 +143,7 @@
margin: 0 10px; margin: 0 10px;
} }
.arrow__right { .candidate-sidebar__arrow--right {
position: relative; position: relative;
width: 30px; width: 30px;
height: 30px; height: 30px;
@ -132,7 +151,7 @@
background-color: #74be4d; background-color: #74be4d;
} }
.arrow__right > img { .candidate-sidebar__arrow--right > img {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
@ -140,18 +159,18 @@
} }
@media (max-width: 1199px) { @media (max-width: 1199px) {
.candidateSidebar { .candidate-sidebar {
flex-direction: row; flex-direction: row;
padding-left: 16px; padding-left: 16px;
} }
.candidateSidebar__info { .candidate-sidebar__info {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.candidateSidebar__info__e, .candidate-sidebar__info__e,
.candidateSidebar__info__y { .candidate-sidebar__info__y {
width: 180px; width: 180px;
} }
} }

View File

@ -1,143 +1,238 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react'
import style from './Description.module.css'; import male from '../../images/medium_male.png'
import male from '../../images/medium_male.png'; import rectangle from '../../images/rectangle_secondPage.png'
import rectangle from '../../images/rectangle_secondPage.png'; import { Link, useHistory } from 'react-router-dom'
import { Link, useHistory } from 'react-router-dom'; import { LEVELS, SKILLS } from '../constants/constants'
import { LEVELS, SKILLS } from '../constants/constants'; import {
import { selectProfiles, selectFilteredCandidates, selectItems, auth } from '../../redux/outstaffingSlice'; selectProfiles,
import { useSelector, useDispatch } from 'react-redux'; selectFilteredCandidates,
import { fetchProfile } from '../../server/server'; selectItems,
import { Loader } from '../Loader/Loader'; auth
import { getRole } from '../../redux/roleSlice'; } from '../../redux/outstaffingSlice'
import { useSelector, useDispatch } from 'react-redux'
import { fetchGet } from '../../server/server'
import { Loader } from '../Loader/Loader'
import { getRole } from '../../redux/roleSlice'
import './description.scss'
const Description = ({ onLoadMore, isLoadingMore }) => { const Description = ({ onLoadMore, isLoadingMore }) => {
const dispatch = useDispatch(); const dispatch = useDispatch()
const [isLoaded, setIsLoaded] = useState(false); const [isLoaded, setIsLoaded] = useState(false)
const history = useHistory(); const history = useHistory()
const role = useSelector(getRole) const role = useSelector(getRole)
const candidatesListArr = useSelector(selectProfiles); const candidatesListArr = useSelector(selectProfiles)
const itemsArr = useSelector(selectItems); const itemsArr = useSelector(selectItems)
const filteredListArr = useSelector(selectFilteredCandidates); const filteredListArr = useSelector(selectFilteredCandidates)
const [allCandidates, getAllCandidates] = useState([]); const [allCandidates, getAllCandidates] = useState([])
useEffect(() => { useEffect(() => {
fetchProfile({ link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`, index: 1000, history, role, logout: dispatch(auth(false)) }).then((p) => { fetchGet({
getAllCandidates(p); link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`,
setIsLoaded(true); params: 1000,
}); history,
}, []); role,
logout: () => dispatch(auth(false))
}).then((p) => {
getAllCandidates(p)
setIsLoaded(true)
})
}, [])
if(!filteredListArr) { if (!filteredListArr) {
return ( return (
<section className={style.description}> <section className='description'>
<div className="container"> <div className='container'>
<div className={style.description__wrapper}> <div className='description__wrapper'>
{ {candidatesListArr && candidatesListArr.length > 0 ? (
candidatesListArr && candidatesListArr.length > 0 ? candidatesListArr.map((el) => ( candidatesListArr.map((el) => (
<div className="row" key={el.id}> <div className='row' key={el.id}>
<div className="col-2 col-xs-12"> <div className='col-2 col-xs-12'>
<img className={style.description__img} src={el.photo} alt="" /> <img className='description__img' src={el.photo} alt='' />
</div>
<div className="col-12 col-xl-6">
<h3 className={style.description__title}>
<Link to={`/candidate/${el.id}`}>{el.specification} {SKILLS[el.position_id]}, {LEVELS[el.level]} </Link>
</h3>
{el.vc_text_short ? (
<div className={style.description__text}>{el.vc_text_short}</div>
) : (
<p className={style.description__textSecondary}>Описание отсутствует...</p>
)}
</div>
<div className="col-12 col-xl-4">
<Link to={`/candidate/${el.id}`}>
<button className={style.description__button}>Подробное резюме</button>
</Link>
</div>
<div className="col-xl-2"></div>
<div className="col-12 col-xl-6">
<ul className={style.description__list}>
{el.skillValues.map((e) => (
<li key={e.id} className={style.description__list__item}>
{e.skill.name}
</li>
))}
</ul>
<img className={style.description__rectangle} src={rectangle} alt="" />
</div>
<div className="col-xl-4"></div>
</div>
)) : <div className={style.description__empty}>{
isLoaded ? 'В данный момент в категории нет свободных специалистов' : 'Загрузка...'
}</div>}
</div>
<div className="row">
<div className="col-12">
<div className={style.description__footer}>
<div className={style.description__footer__btn}>
<button onClick={() => onLoadMore(2)}>
{
isLoadingMore ? <Loader width={40} height={40} /> : 'Загрузить еще'
} </button>
</div>
</div>
</div>
</div>
</div>
</section>
);
}
return (
<section className={style.description}>
<div className="container">
<div className={style.description__wrapper}>
{filteredListArr && filteredListArr.length > 0
? filteredListArr.map((el) => (
<div className="row" key={el.id}>
<div className="col-2">
<img className={style.description__img} src={el.photo} alt="" />
</div> </div>
<div className="col-12 col-xl-6"> <div className='col-12 col-xl-6'>
<h3 className={style.description__title}> <h3 className='description__title'>
<Link to={`/candidate/${el.id}`}> {el.specification} {SKILLS[el.position_id]}, {LEVELS[el.level]} </Link> <Link to={`/candidate/${el.id}`}>
{el.specification} {SKILLS[el.position_id]},{' '}
{LEVELS[el.level]}{' '}
</Link>
</h3> </h3>
{el.vc_text_short ? ( {el.vc_text_short ? (
<div className={style.description__text}>{el.vc_text_short}</div> <div className='description__text'>
{el.vc_text_short}
</div>
) : ( ) : (
<p className={style.description__textSecondary}>Описание отсутствует...</p> <p className='description__text-secondary'>
Описание отсутствует...
</p>
)} )}
</div> </div>
<div className="col-12 col-xl-4"> <div className='col-12 col-xl-4'>
<Link to={`/candidate/${el.id}`}> <Link to={`/candidate/${el.id}`}>
<button className={style.description__button}>Подробное резюме</button> <button className='description__button'>
Подробное резюме
</button>
</Link> </Link>
</div> </div>
<div className="col-xl-2"></div> <div className='col-xl-2'></div>
<div className="col-12 col-xl-6"> <div className='col-12 col-xl-6'>
<ul className={style.description__list}> <ul className='description__list'>
{el.skillValues.map((e) => ( {el.skillValues.map((e) => (
<li key={e.id} className={style.description__list__item}> <li key={e.id} className='description__list-item'>
{e.skill.name} {e.skill.name}
</li> </li>
))} ))}
</ul> </ul>
<img className={style.description__rectangle} src={rectangle} alt="" /> <img
className='description__rectangle'
src={rectangle}
alt=''
/>
</div> </div>
<div className="col-xl-4"></div> <div className='col-xl-4'></div>
</div> </div>
)) ))
: <div className={style.description__empty}>В данный момент в категории нет свободных специалистов</div> } ) : (
<div className='description__empty'>
{isLoaded
? 'В данный момент в категории нет свободных специалистов'
: 'Загрузка...'}
</div>
)}
</div>
<div className='row'>
<div className='col-12'>
<div className='description__footer'>
<div className='description__footer-btn'>
<button onClick={() => onLoadMore(2)}>
{isLoadingMore ? (
<Loader width={40} height={40} />
) : (
'Загрузить еще'
)}{' '}
</button>
</div>
</div>
</div>
</div>
</div>
</section>
)
}
return (
<section className='description'>
<div className='container'>
<div className='description__wrapper'>
{filteredListArr && filteredListArr.length > 0
? filteredListArr.map((el) => (
<div className='row' key={el.id}>
<div className='col-2'>
<img className='description__img' src={el.photo} alt='' />
</div>
<div className='col-12 col-xl-6'>
<h3 className='description__title'>
<Link to={`/candidate/${el.id}`}>
{' '}
{el.specification} {SKILLS[el.position_id]},{' '}
{LEVELS[el.level]}{' '}
</Link>
</h3>
{el.vc_text_short ? (
<div className='description__text'>
{el.vc_text_short}
</div>
) : (
<p className='description__text-secondary'>
Описание отсутствует...
</p>
)}
</div>
<div className='col-12 col-xl-4'>
<Link to={`/candidate/${el.id}`}>
<button className='description__button'>
Подробное резюме
</button>
</Link>
</div>
<div className='col-xl-2'></div>
<div className='col-12 col-xl-6'>
<ul className='description__list'>
{el.skillValues.map((e) => (
<li key={e.id} className='description__list-item'>
{e.skill.name}
</li>
))}
</ul>
<img
className='description__rectangle'
src={rectangle}
alt=''
/>
</div>
<div className='col-xl-4'></div>
</div>
))
: /* : <div className={style.description__empty}>В данный момент в категории нет свободных специалистов</div> } */
candidatesListArr &&
candidatesListArr.map((el) => (
<div className='row' key={el.id}>
<div className='col-2'>
<img className='description__img' src={male} alt='' />
</div>
<div className='col-12 col-xl-6'>
<h3 className='description__title'>
{SKILLS[el.position_id]}, {LEVELS[el.level]}
</h3>
{el.vc_text_short ? (
<div className='description__text'>
{el.vc_text_short}
</div>
) : (
<p className='description__text-secondary'>
Описание отсутствует...
</p>
)}
</div>
<div className='col-12 col-xl-4'>
<Link to={`/candidate/${el.id}`}>
<button className='description__button'>
Подробное резюме
</button>
</Link>
</div>
<div className='col-xl-2'></div>
<div className='col-12 col-xl-6'>
<ul className='description__list'>
{el.skillValues.map((e) => (
<li key={e.id} className='description__list-item'>
{e.skill.name}
</li>
))}
</ul>
<img
className='description__rectangle'
src={rectangle}
alt=''
/>
</div>
<div className='col-xl-4'></div>
</div>
))}
</div> </div>
<div className="row"> <div className='row'>
<div className="col-12"> <div className='col-12'>
<div className={style.description__footer}> <div className='description__footer'>
<div className={style.description__footer__btn}> <div className='description__footer-btn'>
{(candidatesListArr.length !== allCandidates.length && filteredListArr.length > 0) || filteredListArr===null ? ( {allCandidates &&
candidatesListArr &&
candidatesListArr.length !== allCandidates.length &&
filteredListArr.length === 0 ? (
<button onClick={() => onLoadMore(2)}>Загрузить еще</button> <button onClick={() => onLoadMore(2)}>Загрузить еще</button>
) : null} ) : null}
</div> </div>
@ -146,7 +241,7 @@ const Description = ({ onLoadMore, isLoadingMore }) => {
</div> </div>
</div> </div>
</section> </section>
); )
}; }
export default Description; export default Description

View File

@ -1,250 +0,0 @@
.description {
margin-top: 40px;
margin-bottom: 120px;
}
.description__empty {
font-family: 'GT Eesti Pro Display';
font-size: 2.4em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: center;
}
.description__wrapper {
border: 1px solid #efefef;
background-color: #fdfdfd;
padding: 60px 40px 0 40px;
border-bottom: none;
position: relative;
}
.description__img {
margin-top: 16px;
width: 118px;
height: 118px;
border-radius: 100px;
}
@media (max-width: 575.98px) {
.description__img {
position: absolute;
top: 100px;
left: calc(50% - 60px);
}
.description__wrapper {
padding: 45px 40px 0 40px;
}
}
.description__title {
font-family: 'GT Eesti Pro Display';
font-size: 2.6em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
margin-bottom: 10px;
}
.description__title a {
color: #333;
}
@media (max-width: 575.98px) {
.description__title {
text-align: center;
margin-bottom: 170px;
font-size: 3em;
}
}
@media (max-width: 376px) {
.description__title {
font-size: 2.8em;
}
}
@media (max-width: 346px) {
.description__title {
font-size: 2.6em;
}
}
.description__text {
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 24px;
text-align: left;
line-height: 28px;
word-wrap: break-word;
}
.description__textSecondary {
font-family: 'GT Eesti Pro Display';
font-size: 1.7em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 24px;
text-align: left;
line-height: 28px;
}
@media (max-width: 575.98px) {
.description__text {
margin-left: 20px;
font-size: 1.6em;
}
}
.description__button {
width: 280px;
height: 60px;
border-radius: 100px;
background-color: #73c141;
border: none;
color: #ffffff;
font-family: 'Muller';
font-weight: bold;
font-size: 1.6em;
line-height: normal;
letter-spacing: 0.8px;
text-align: center;
margin-top: 74px;
margin-left: 30px;
}
.description__button:hover {
background: rgba(0, 0, 0, 0);
color: #73c141;
box-shadow: inset 0 0 0 3px #73c141;
}
@media (max-width: 575.98px) {
.description__button {
width: 220px;
height: 50px;
z-index: 5;
margin-left: 85px;
margin-top: 40px;
margin-bottom: 40px;
}
}
@media (max-width: 375.98px) {
.description__button {
margin-left: 60px;
}
}
@media (max-width: 325.98px) {
.description__button {
margin-left: 30px;
}
}
.description__list {
padding: 0;
list-style: none;
display: flex;
flex-wrap: wrap;
}
.description__list__item {
font-family: 'GT Eesti Pro Display';
font-size: 1.7em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
text-align: left;
line-height: 36px;
margin-right: 10px;
word-wrap: break-word;
}
@media (max-width: 575.98px) {
.description__list__item {
font-size: 1.5em;
text-align: center;
margin-bottom: 0;
}
}
.description__rectangle {
display: block;
margin: 50px auto;
}
@media (max-width: 575.98px) {
.description__rectangle {
width: 80%;
margin: 50px auto 80px auto;
}
}
.description__footer {
display: flex;
justify-content: center;
margin-top: 60px;
}
.description__footer__btn > button {
width: 200px;
height: 48px;
border-radius: 100px;
border: 1px solid #73c141;
background-color: white;
margin-right: 60px;
color: #a0a0a0;
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 400;
font-style: normal;
letter-spacing: 0.6px;
line-height: normal;
text-align: center;
}
@media (max-width: 575.98px) {
.description {
margin-bottom: 16px;
}
.description__footer {
margin-top: 0;
}
.description__footer__btn > button {
width: 160px;
margin-right: 10px;
font-size: 1.6em;
}
}
.description__footer__box {
display: flex;
align-items: center;
}
.description__footer__sp {
color: #705fa3;
font-family: Circe;
font-size: 1.3em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
text-align: center;
margin: 0 10px;
}
@media (max-width: 1199px) {
.description__button {
margin: 1.5rem 0;
}
}

View File

@ -0,0 +1,252 @@
.description {
margin-top: 40px;
margin-bottom: 120px;
&__title {
font-family: 'GT Eesti Pro Display';
font-size: 2.6em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
margin-bottom: 10px;
a {
color: #333;
}
}
&__empty {
font-family: 'GT Eesti Pro Display';
font-size: 2.4em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: center;
}
&____wrapper {
border: 1px solid #efefef;
background-color: #fdfdfd;
padding: 60px 40px 0 40px;
border-bottom: none;
position: relative;
}
&__img {
margin-top: 16px;
width: 118px;
height: 118px;
border-radius: 100px;
}
&__text {
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 24px;
text-align: left;
line-height: 28px;
word-wrap: break-word;
&-secondary {
font-family: 'GT Eesti Pro Display';
font-size: 1.7em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 24px;
text-align: left;
line-height: 28px;
}
}
&__button {
width: 280px;
height: 60px;
border-radius: 100px;
background-color: #73c141;
border: none;
color: #ffffff;
font-family: 'Muller';
font-weight: bold;
font-size: 1.6em;
line-height: normal;
letter-spacing: 0.8px;
text-align: center;
margin-top: 74px;
margin-left: 30px;
&:hover {
background: rgba(0, 0, 0, 0);
color: #73c141;
box-shadow: inset 0 0 0 3px #73c141;
}
}
&__list {
padding: 0;
list-style: none;
display: flex;
flex-wrap: wrap;
&-item {
font-family: 'GT Eesti Pro Display';
font-size: 1.7em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
text-align: left;
line-height: 36px;
margin-right: 10px;
word-wrap: break-word;
}
}
&__rectangle {
display: block;
margin: 50px auto;
}
&__footer {
display: flex;
justify-content: center;
margin-top: 60px;
&-btn {
button {
width: 200px;
height: 48px;
border-radius: 100px;
border: 1px solid #73c141;
background-color: white;
margin-right: 60px;
color: #a0a0a0;
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 400;
font-style: normal;
letter-spacing: 0.6px;
line-height: normal;
text-align: center;
}
}
&-box {
display: flex;
align-items: center;
}
&-sp {
color: #705fa3;
font-family: Circe;
font-size: 1.3em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
text-align: center;
margin: 0 10px;
}
}
}
@media (max-width: 575.98px) {
.description {
margin-bottom: 16px;
&__wrapper {
padding: 35px 40px 0 40px;
}
&__title {
text-align: center;
margin-bottom: 170px;
font-size: 3em;
}
&__text {
margin-left: 20px;
font-size: 1.6em;
}
&__img {
position: absolute;
top: 100px;
left: calc(50% - 60px);
}
&__button {
width: 220px;
height: 50px;
z-index: 5;
margin-left: 85px;
margin-top: 40px;
margin-bottom: 40px;
}
&__list {
&-item {
font-size: 1.5em;
text-align: center;
margin-bottom: 0;
}
}
&__rectangle {
width: 80%;
margin: 50px auto 80px auto;
}
&__footer {
margin-top: 0;
&-btn {
button {
width: 160px;
margin-right: 10px;
font-size: 1.6em;
}
}
}
}
}
@media (max-width: 1199px) {
.description {
&__button {
margin: 1.5rem 0;
}
}
}
@media (max-width: 376px) {
.description {
&__title {
font-size: 2.8em;
}
&__button {
margin-left: 60px;
}
}
}
@media (max-width: 346px) {
.description {
&__title {
font-size: 2.6em;
}
}
}
@media (max-width: 325.98px) {
.description {
&__button {
margin-left: 30px;
}
}
}

View File

@ -1,46 +1,47 @@
import React from 'react'; import React from 'react'
import './footer.css';
import align from '../../images/align-left.png' import align from '../../images/align-left.png'
import phone from '../../images/phone.png' import phone from '../../images/phone.png'
import telegram from '../../images/telegram.png' import telegram from '../../images/telegram.png'
import './footer.scss'
export const Footer = () => { export const Footer = () => {
return ( return (
<div className='container'> <div className='container'>
<footer> <footer>
<div className='footer row'> <div className='footer row'>
<div className='col-12 col-xl-7'> <div className='col-12 col-xl-7'>
<div className='footer__left'> <div className='footer__left'>
<div className='footer__img'> <div className='footer__img'>
<img src={align} alt='' /> <img src={align} alt='' />
</div> </div>
<div className='footer__description'> <div className='footer__description'>
<span> <span>
Подберем и документально оформим IT-специалистов, после чего передадим исполнителей под ваше руководство. Подберем и документально оформим IT-специалистов, после чего
Вы получаете полное управление над сотрудниками, имея возможность контролировать и заменять IT штат.{' '} передадим исполнителей под ваше руководство. Вы получаете
</span> полное управление над сотрудниками, имея возможность
</div> контролировать и заменять IT штат.{' '}
</div> </span>
</div> </div>
<div className='col-4 col-xl-2'>
<div className='footer__icon'>
<img src={phone} alt='' />
<img src={telegram} alt='' />
</div>
</div>
<div className='col-8 col-xl-3'>
<div className='footer__right'>
<p className='footer__phone'>+7 495 156 78 98</p>
<p className='footer__hours'>Будни с 9:00 до 21:00</p>
</div>
</div> </div>
</div> </div>
<div className='copyright'>2021 © Outstaffing</div>
</footer> <div className='col-4 col-xl-2'>
<div className='footer__icon'>
<img src={phone} alt='' />
<img src={telegram} alt='' />
</div>
</div>
<div className='col-8 col-xl-3'>
<div className='footer__right'>
<p className='footer__phone'>+7 495 156 78 98</p>
<p className='footer__hours'>Будни с 9:00 до 21:00</p>
</div>
</div>
</div> </div>
) <div className='footer__copyright'>2021 © Outstaffing</div>
</footer>
</div>
)
} }

View File

@ -1,101 +0,0 @@
footer {
margin-top: -3rem;
}
footer .copyright {
padding: 1rem 1rem 1rem 5.6rem;
font-family: 'Muller';
font-weight: 300;
font-size: 1.2em;
}
.footer__left {
display: flex;
align-items: center;
}
@media (max-width: 575.98px) {
.footer__left {
margin-top: 80px;
}
}
.footer__description {
padding: 0 100px 0 34px;
}
@media (max-width: 575.98px) {
.footer__description {
padding: 0;
margin-left: 10px;
}
}
.footer__left > div > span {
color: #18586e;
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
}
@media (max-width: 575.98px) {
.footer__left > div > span {
font-size: 1.2em;
}
}
.footer__icon {
text-align: end;
}
.footer__icon > img {
margin-left: 20px;
}
@media (max-width: 575.98px) {
.footer__icon > img {
margin-left: 10px;
}
}
.footer__right {
display: flex;
flex-direction: column;
align-items: left;
}
@media (max-width: 575.98px) {
.footer__right {
margin-bottom: 20px;
}
}
.footer__phone {
color: #003b65;
font-family: 'CeraPro';
font-size: 2.1em;
letter-spacing: normal;
line-height: 25px;
text-align: left;
}
.footer__working-hours {
color: #003b65;
font-family: 'CeraPro';
font-size: 1.2em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: normal;
margin-left: 24px;
}
@media (max-width: 1199px) {
.footer__left {
margin-bottom: 20px;
}
}

View File

@ -0,0 +1,99 @@
.footer {
margin-top: -3rem;
&__left {
display: flex;
align-items: center;
}
&__description {
padding: 0 100px 0 34px;
span {
color: #18586e;
font-family: 'GT Eesti Pro Display';
font-size: 1.6em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
}
}
&__icon {
text-align: end;
img {
margin-left: 20px;
}
}
&__right {
display: flex;
flex-direction: column;
align-items: left;
}
&__phone {
color: #003b65;
font-family: 'CeraPro';
font-size: 2.1em;
letter-spacing: normal;
line-height: 25px;
text-align: left;
}
&__working-hours {
color: #003b65;
font-family: 'CeraPro';
font-size: 1.2em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: normal;
margin-left: 24px;
}
&__copyright {
padding: 1rem 1rem 1rem 5.6rem;
font-family: 'Muller';
font-weight: 300;
font-size: 1.2em;
}
}
@media (max-width: 1199px) {
.footer {
&__left {
margin-bottom: 20px;
}
}
}
@media (max-width: 575.98px) {
.footer {
&__left {
margin-top: 80px;
}
&__description {
padding: 0;
margin-left: 10px;
span {
font-size: 1.2em;
}
}
&__icon {
img {
margin-left: 10px;
}
}
&__right {
margin-bottom: 20px;
}
}
}

View File

@ -1,90 +1,112 @@
import React, { useState } from 'react'; import React, { useState } from 'react'
import style from './Form.module.css'; import { fetchPost } from '../../server/server'
import { fetchForm } from '../../server/server'; import { auth } from '../../redux/outstaffingSlice'
import { auth } from '../../redux/outstaffingSlice'; import { useHistory, useParams, Redirect } from 'react-router-dom'
import { useHistory, useParams, Redirect } from 'react-router-dom'; import { Loader } from '../Loader/Loader'
import { Loader } from '../Loader/Loader';
import PhoneInput from 'react-phone-input-2' import PhoneInput from 'react-phone-input-2'
import 'react-phone-input-2/lib/style.css' import 'react-phone-input-2/lib/style.css'
import './form.css'; import './form.scss'
import { withSwalInstance } from 'sweetalert2-react'; import { withSwalInstance } from 'sweetalert2-react'
import swal from 'sweetalert2'; import swal from 'sweetalert2'
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux'
import { getRole } from '../../redux/roleSlice'; import { getRole } from '../../redux/roleSlice'
const SweetAlert = withSwalInstance(swal); const SweetAlert = withSwalInstance(swal)
const Form = () => { const Form = () => {
const dispatch = useDispatch(); const dispatch = useDispatch()
const history = useHistory(); const history = useHistory()
const role = useSelector(getRole); const role = useSelector(getRole)
const urlParams = useParams(); const urlParams = useParams()
const [status, setStatus] = useState(null); const [status, setStatus] = useState(null)
const [data, setData] = useState({ const [data, setData] = useState({
email: '', email: '',
phone: '', phone: '',
comment: '', comment: ''
}); })
const [isFetching, setIsFetching] = useState(false); const [isFetching, setIsFetching] = useState(false)
const handleChange = (e) => { const handleChange = (e) => {
const { id, value } = e.target; const { id, value } = e.target
setData((prev) => ({ setData((prev) => ({
...prev, ...prev,
[id]: value, [id]: value
})); }))
}; }
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault()
setIsFetching(true) setIsFetching(true)
const formData = new FormData(); const formData = new FormData()
formData.append('profile_id', urlParams.id); formData.append('profile_id', urlParams.id)
formData.append('email', data.email); formData.append('email', data.email)
formData.append('phone', data.phone); formData.append('phone', data.phone)
formData.append('comment', data.comment); formData.append('comment', data.comment)
fetchForm({ link: `${process.env.REACT_APP_API_URL}/api/profile/add-to-interview`, index: { fetchPost({
profile_id: urlParams.id, link: `${process.env.REACT_APP_API_URL}/api/profile/add-to-interview`,
...data, params: {
}, history, role, logout: dispatch(auth(false)) }).then( (res)=> res.json() profile_id: urlParams.id,
.then( resJSON => { ...data
setStatus(resJSON); },
setIsFetching(false); history,
role,
logout: () => dispatch(auth(false))
}).then((res) =>
res.json().then((resJSON) => {
setStatus(resJSON)
setIsFetching(false)
}) })
) )
}; }
return ( return (
<> <>
{status && <SweetAlert {status && (
show={!!status} <SweetAlert
text={status.errors ? status.errors[Object.keys(status.errors)[0]] : 'Форма отправлена'} show={!!status}
onConfirm={status.errors ? () => {setStatus(null);} : () => {setStatus(null); history.push(`/candidate/${urlParams.id}`)}} text={
/>} status.errors
<div className="row"> ? status.errors[Object.keys(status.errors)[0]]
<div className="col-sm-12"> : 'Форма отправлена'
<form className={style.form} id="test"> }
<label htmlFor="email">Емейл:</label> onConfirm={
status.errors
? () => {
setStatus(null)
}
: () => {
setStatus(null)
history.push(`/candidate/${urlParams.id}`)
}
}
/>
)}
<div className='row'>
<div className='col-sm-12'>
<form className='form' id='test'>
<label htmlFor='email'>Емейл:</label>
<input <input
onChange={handleChange} onChange={handleChange}
id="email" id='email'
name="Email" name='Email'
type="email" type='email'
placeholder="Емейл" placeholder='Емейл'
value={data.email} value={data.email}
/> />
<label htmlFor="phone">Номер телефона:</label> <label htmlFor='phone'>Номер телефона:</label>
<PhoneInput <PhoneInput
id="phone" id='phone'
name="Phone" name='Phone'
country={'ru'} country={'ru'}
value={data.phone} value={data.phone}
onChange={e=>handleChange({target: {value:e, id: 'phone' }})} onChange={(e) =>
handleChange({ target: { value: e, id: 'phone' } })
}
/> />
{/* <input {/* <input
onChange={handleChange} onChange={handleChange}
@ -97,22 +119,22 @@ const Form = () => {
<textarea <textarea
onChange={handleChange} onChange={handleChange}
id="comment" id='comment'
rows="5" rows='5'
cols="40" cols='40'
name="Comment" name='Comment'
placeholder="Оставьте комментарий" placeholder='Оставьте комментарий'
value={data.comment} value={data.comment}
></textarea> ></textarea>
<button onClick={handleSubmit} className={style.form__btn} type="submit"> <button onClick={handleSubmit} className='form__btn' type='submit'>
{ isFetching ? <Loader /> : 'Отправить' } {isFetching ? <Loader /> : 'Отправить'}
</button> </button>
</form> </form>
</div> </div>
</div> </div>
</> </>
); )
}; }
export default Form; export default Form

View File

@ -1,104 +0,0 @@
.form {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 120px;
}
.form > label {
color: #48802d;
font-family: 'GT Eesti Pro Display';
font-size: 2.4em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
margin-bottom: 20px;
}
.form > input {
max-width: 366px;
height: 62px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 37px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
margin-bottom: 60px;
color: #a6a6a6;
font-family: 'GT Eesti Pro Display';
font-size: 2.2em;
font-weight: 300;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
padding-left: 45px;
outline: none;
}
.form > textarea {
max-width: 366px;
height: 62px;
margin-bottom: 40px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 37px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
margin-bottom: 60px;
color: #a6a6a6;
font-family: 'GT Eesti Pro Display';
font-size: 1.2em;
font-weight: 300;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
padding-left: 25px;
padding-top: 25px;
outline: none;
}
textarea {
resize: none;
}
.form__btn {
width: 332px;
height: 75px;
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
border-radius: 38px;
background-color: #ffffff;
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%),
linear-gradient(
36deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.16) 47%,
rgba(255, 255, 255, 0.17) 50%,
rgba(255, 255, 255, 0) 100%
);
border: none;
color: #ffffff;
font-family: 'Muller';
font-size: 2.2em;
letter-spacing: normal;
line-height: 71.88px;
text-align: center;
}
@media (max-width: 575.98px) {
.form__arrow {
margin-bottom: 40px;
}
}
@media (max-width: 575.98px) {
.form__arrow__sp > span {
margin-right: 0px;
}
}

View File

@ -1,15 +0,0 @@
.react-tel-input {
width: min-content !important;
margin-bottom: 60px;
}
.react-tel-input .form-control {
padding: 30px 30px 30px 48px;
border-radius: 37px;
width: 332px;
}
.react-tel-input .flag-dropdown {
border-top-left-radius: 37px;
border-bottom-left-radius: 37px;
}

View File

@ -0,0 +1,114 @@
.form {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 120px;
& > label {
color: #48802d;
font-family: 'GT Eesti Pro Display';
font-size: 2.4em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
margin-bottom: 20px;
}
& > input {
max-width: 366px;
height: 62px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 37px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
margin-bottom: 60px;
color: #a6a6a6;
font-family: 'GT Eesti Pro Display';
font-size: 2.2em;
font-weight: 300;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
padding-left: 45px;
outline: none;
}
& > textarea {
max-width: 366px;
height: 62px;
margin-bottom: 40px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 37px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
margin-bottom: 60px;
color: #a6a6a6;
font-family: 'GT Eesti Pro Display';
font-size: 1.2em;
font-weight: 300;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
padding-left: 25px;
padding-top: 25px;
outline: none;
resize: none;
}
&__btn {
width: 332px;
height: 75px;
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
border-radius: 38px;
background-color: #ffffff;
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%),
linear-gradient(
36deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.16) 47%,
rgba(255, 255, 255, 0.17) 50%,
rgba(255, 255, 255, 0) 100%
);
border: none;
color: #ffffff;
font-family: 'Muller';
font-size: 2.2em;
letter-spacing: normal;
line-height: 71.88px;
text-align: center;
}
.react-tel-input {
width: min-content !important;
margin-bottom: 60px;
}
.react-tel-input .form-control {
padding: 30px 30px 30px 48px;
border-radius: 37px;
width: 332px;
}
.react-tel-input .flag-dropdown {
border-top-left-radius: 37px;
border-bottom-left-radius: 37px;
}
}
@media (max-width: 575.98px) {
.form {
.arrow {
margin-bottom: 40px;
&-sp {
span {
margin-right: 0px;
}
}
}
}
}

View File

@ -1,45 +1,58 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux'
import Outstaffing from '../Outstaffing/Outstaffing'; import Outstaffing from '../Outstaffing/Outstaffing'
import Description from '../Description/Description'; import Description from '../Description/Description'
import { fetchProfile, fetchSkills } from '../../server/server'; import { fetchGet } from '../../server/server'
import { profiles, tags, auth } from '../../redux/outstaffingSlice'; import { profiles, tags, auth } from '../../redux/outstaffingSlice'
import { getRole } from '../../redux/roleSlice'; import { getRole } from '../../redux/roleSlice'
import { Footer } from '../Footer/Footer'; import { Footer } from '../Footer/Footer'
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom'
const Home = () => { const Home = () => {
const history = useHistory() const history = useHistory()
const [isLoadingMore, setIsLoadingMore] = useState(false); const [isLoadingMore, setIsLoadingMore] = useState(false)
const [index, setIndex] = useState(4); const [index, setIndex] = useState(4)
const dispatch = useDispatch(); const dispatch = useDispatch()
const role = useSelector(getRole) const role = useSelector(getRole)
useEffect(() => { useEffect(() => {
setIsLoadingMore(true); setIsLoadingMore(true)
fetchProfile({ link:`${process.env.REACT_APP_API_URL}/api/profile?limit=`, index, history, role, logout: dispatch(auth(false)) }).then((profileArr) => { fetchGet({
dispatch(profiles(profileArr)); link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`,
setIsLoadingMore(false); params: index,
}); history,
role,
logout: () => dispatch(auth(false))
}).then((profileArr) => {
dispatch(profiles(profileArr))
setIsLoadingMore(false)
})
fetchSkills({ link: `${process.env.REACT_APP_API_URL}/api/skills/skills-on-main-page`, history, role, logout: dispatch(auth(false)) }).then((skills) => { fetchGet({
if(!skills) { return [] } link: `${process.env.REACT_APP_API_URL}/api/skills/skills-on-main-page`,
const keys = Object.keys(skills); history,
const values = Object.values(skills); role,
logout: () => dispatch(auth(false))
}).then((skills) => {
if (!skills) {
return []
}
const keys = Object.keys(skills)
const values = Object.values(skills)
const tempTags = values.map((value, index) => const tempTags = values.map((value, index) =>
value.map((val) => { value.map((val) => {
return { id: val.id, value: val.tags, name: keys[index] }; return { id: val.id, value: val.tags, name: keys[index] }
}) })
); )
dispatch(tags(tempTags)); dispatch(tags(tempTags))
}); })
}, [dispatch, index]); }, [dispatch, index])
const loadMore = (count) => { const loadMore = (count) => {
setIndex((prev) => prev + count); setIndex((prev) => prev + count)
}; }
return ( return (
<> <>
@ -47,7 +60,7 @@ const Home = () => {
<Description onLoadMore={loadMore} isLoadingMore={isLoadingMore} /> <Description onLoadMore={loadMore} isLoadingMore={isLoadingMore} />
<Footer /> <Footer />
</> </>
); )
}; }
export default Home; export default Home

View File

@ -1,18 +0,0 @@
import React from 'react';
import style from './InputsComponent.module.css';
const InputsComponent = ({ inputsArr, deleteInput, remove }) =>
inputsArr.map((input) => (
<form id={input} key={input} className={style.reportForm__form}>
{/* <span>{input}.</span> */}
<div className={style.input__text}>
<input name="text" type="text" />
</div>
<div className={style.input__number}>
<input name="number" type="number" min="1" />
</div>
<img onClick={() => deleteInput(input)} src={remove} alt="" />
</form>
));
export default InputsComponent;

View File

@ -1,69 +0,0 @@
.reportForm__form {
position: relative;
display: flex;
justify-content: space-between;
margin-left: 14px;
margin-bottom: 28px;
}
.reportForm__form > span {
font-family: 'GT Eesti Pro Display';
font-size: 2em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
text-align: left;
position: absolute;
left: 0;
bottom: 7px;
}
.input__text,
.input__number {
display: flex;
flex-direction: column;
margin-left: 20px;
}
.input__text > input {
width: 460px;
height: 42px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
outline: none;
font-size: 1.8em;
padding-left: 20px;
padding-right: 20px;
}
@media (max-width: 575.98px) {
.input__text > input {
max-width: 460px;
}
}
.input__number > input {
width: 141px;
height: 42px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
outline: none;
font-size: 1.8em;
text-align: center;
}
@media (max-width: 575.98px) {
.input__number > input {
max-width: 141px;
}
}
.reportForm__form > img {
margin-top: 5px;
width: 23px;
height: 23px;
}

View File

@ -1,15 +1,10 @@
import SVGLoader from "react-loader-spinner"; import SVGLoader from 'react-loader-spinner'
import './loader.css' import './loader.scss'
export const Loader = ({ width=50, height=50 }) => { export const Loader = ({ width = 50, height = 50 }) => {
return ( return (
<div className='loader'> <div className='loader'>
<SVGLoader <SVGLoader type='Circles' color='#fff' height={height} width={width} />
type="Circles" </div>
color="#fff" )
height={height}
width={width}
/>
</div>
);
} }

View File

@ -1,11 +0,0 @@
.loader {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.loader:hover path {
fill: #6aaf5c;
}

View File

@ -0,0 +1,13 @@
.loader {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
&:hover {
path {
fill: #6aaf5c;
}
}
}

View File

@ -1,31 +1,31 @@
import React, { useState } from 'react'; import React, { useState } from 'react'
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux'
import { Loader } from '../Loader/Loader'; import { Loader } from '../Loader/Loader'
import { auth } from '../../redux/outstaffingSlice'; import { auth } from '../../redux/outstaffingSlice'
import { getRole } from '../../redux/roleSlice'; import { getRole } from '../../redux/roleSlice'
import './logoutButton.css' import './logoutButton.scss'
export const LogoutButton = () => { export const LogoutButton = () => {
const [isLoggingOut, setIsLoggingOut] = useState(false); const [isLoggingOut, setIsLoggingOut] = useState(false)
const dispatch = useDispatch(); const dispatch = useDispatch()
const userRole = useSelector(getRole); const userRole = useSelector(getRole)
const history = useHistory(); const history = useHistory()
return ( return (
<div className='logout-button'> <div className='logout-button'>
<button onClick={()=>{ <button
setIsLoggingOut(true); onClick={() => {
localStorage.clear(); setIsLoggingOut(true)
dispatch(auth(false)); localStorage.clear()
setIsLoggingOut(false); dispatch(auth(false))
history.push(userRole === 'ROLE_DEV' ? '/authdev' : '/auth') setIsLoggingOut(false)
}}> history.push(userRole === 'ROLE_DEV' ? '/authdev' : '/auth')
}}
{ >
isLoggingOut ? <Loader /> : 'Выйти' {isLoggingOut ? <Loader /> : 'Выйти'}{' '}
} </button> </button>
</div> </div>
) )
} }

View File

@ -1,11 +1,10 @@
.logout-button { .logout-button {
position: absolute; position: absolute;
top: 70px; top: 70px;
right: 2.5rem; right: 2.5rem;
z-index: 100; z-index: 100;
}
.logout-button button { button {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@ -21,19 +20,21 @@
border: 3px solid #6aaf5c; border: 3px solid #6aaf5c;
font-family: 'Muller'; font-family: 'Muller';
text-align: center; text-align: center;
} }
.logout-button:hover button { &:hover {
background-color: #ffffff; button {
color: #6aaf5c; background-color: #ffffff;
border: 3px solid #6aaf5c; color: #6aaf5c;
box-shadow: 3px 2px 5px rgba(82, 151, 34, 0.21); border: 3px solid #6aaf5c;
transition: .3s; box-shadow: 3px 2px 5px rgba(82, 151, 34, 0.21);
transition: 0.3s;
}
}
} }
@media (max-width: 1199px) { @media (max-width: 1199px) {
.logout-button { .logout-button {
top: 16px !important; top: 16px !important;
} }
} }

View File

@ -1,78 +1,96 @@
import React, { useState } from 'react'; import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux'
import style from './Outstaffing.module.css'; import OutstaffingBlock from '../OutstaffingBlock/OutstaffingBlock'
import OutstaffingBlock from './OutstaffingBlock'; import TagSelect from '../Select/TagSelect'
import TagSelect from '../Select/TagSelect'; import {
import { selectTags, getPositionId, setPositionId } from '../../redux/outstaffingSlice'; selectTags,
import front from '../../images/front_end.png'; getPositionId,
import back from '../../images/back_end.png'; setPositionId
import design from '../../images/design.png'; } from '../../redux/outstaffingSlice'
import front from '../../images/front_end.png'
import back from '../../images/back_end.png'
import design from '../../images/design.png'
import './outstaffing.scss'
const createSelectPositionHandler =
const createSelectPositionHandler = ({ positionId, setPositionId, dispatch }) => id => { ({ positionId, setPositionId, dispatch }) =>
if(id===positionId) { (id) => {
dispatch(setPositionId(null)); if (id === positionId) {
} else { dispatch(setPositionId(null))
dispatch(setPositionId(id)); } else {
dispatch(setPositionId(id))
}
} }
}
const Outstaffing = () => { const Outstaffing = () => {
const dispatch = useDispatch(); const dispatch = useDispatch()
const positionId = useSelector(getPositionId) const positionId = useSelector(getPositionId)
const tagsArr = useSelector(selectTags); const tagsArr = useSelector(selectTags)
const onSelectPosition = createSelectPositionHandler({ positionId, setPositionId, dispatch }); const onSelectPosition = createSelectPositionHandler({
positionId,
setPositionId,
dispatch
})
return ( return (
<> <>
<section className={style.outstaffing}> <section className='outstaffing'>
<div className="row"> <div className='row'>
<div className="col-12"> <div className='col-12'>
<div className={style.outstaffing__title}> <div className='outstaffing__title'>
<h2> <h2>
<span>Аутстаффинг</span> it-персонала <span>Аутстаффинг</span> it-персонала
</h2> </h2>
</div>
</div> </div>
</div> </div>
</div>
<div className="row"> <div className='row'>
<div className="col-12 col-xl-4"> <div className='col-12 col-xl-4'>
<OutstaffingBlock <OutstaffingBlock
dataTags={tagsArr && tagsArr.flat().filter((tag) => tag.name === 'skills_front')} dataTags={
img={front} tagsArr &&
header="Frontend" tagsArr.flat().filter((tag) => tag.name === 'skills_front')
positionId='2' }
isSelected={positionId==='2'} img={front}
onSelect={id=>onSelectPosition(id)} header='Frontend'
/> positionId='2'
</div> isSelected={positionId === '2'}
<div className="col-12 col-xl-4"> onSelect={(id) => onSelectPosition(id)}
<OutstaffingBlock />
dataTags={tagsArr.flat().filter((tag) => tag.name === 'skills_back')}
img={back}
header="Backend"
positionId='1'
isSelected={positionId==='1'}
onSelect={id=>onSelectPosition(id)}
/>
</div>
<div className="col-12 col-xl-4">
<OutstaffingBlock
dataTags={tagsArr.flat().filter((tag) => tag.name === 'skills_design')}
img={design}
header="Дизайн"
positionId='5'
isSelected={positionId==='5'}
onSelect={id=>onSelectPosition(id)}
/>
</div>
</div> </div>
<div className='col-12 col-xl-4'>
<OutstaffingBlock
dataTags={
tagsArr &&
tagsArr.flat().filter((tag) => tag.name === 'skills_back')
}
img={back}
header='Backend'
positionId='1'
isSelected={positionId === '1'}
onSelect={(id) => onSelectPosition(id)}
/>
</div>
<div className='col-12 col-xl-4'>
<OutstaffingBlock
dataTags={
tagsArr &&
tagsArr.flat().filter((tag) => tag.name === 'skills_design')
}
img={design}
header='Дизайн'
positionId='5'
isSelected={positionId === '5'}
onSelect={(id) => onSelectPosition(id)}
/>
</div>
</div>
</section> </section>
<TagSelect /> <TagSelect />
</> </>
); )
}; }
export default Outstaffing; export default Outstaffing

View File

@ -1,174 +0,0 @@
.outstaffing__box__selected .outstaffing__box__img {
background-color: #52b70999;
color: #f9f9f9;
}
.outstaffing__title {
margin-top: 60px;
}
.outstaffing__title > h2 {
text-align: center;
color: #52b709;
font-family: 'GT Eesti Pro Display';
font-size: 5em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 77.81px;
}
@media (max-width: 375.98px) {
.outstaffing__title > h2 {
font-size: 4.5em;
line-height: normal;
}
}
@media (min-width: 376px) {
.outstaffing__title > h2 {
font-size: 5em;
line-height: normal;
}
}
.outstaffing__title > h2 > span {
color: #282828;
font-style: normal;
letter-spacing: 0.56px;
line-height: normal;
}
.outstaffing__box {
margin-top: 60px;
display: flex;
flex-direction: column;
align-items: center;
}
@media (max-width: 575.98px) {
.outstaffing__box {
margin-top: 40px;
}
}
.outstaffing__box__img {
min-width: 260px;
min-height: 120px;
background-color: #f9f9f9;
border-radius: 20px;
position: relative;
}
.outstaffing__box__img > h3 {
position: absolute;
right: 25%;
top: 40%;
font-family: 'GT Eesti Pro Display';
font-size: 18px;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
}
.outstaffing__box__img > img {
position: absolute;
}
.outstaffing__box > div {
margin-top: 60px;
}
.outstaffing__box > p {
font-family: 'GT Eesti Pro Display';
font-size: 1.2em;
font-weight: 300;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
margin-bottom: 0;
}
@media (max-width: 575.98px) {
.outstaffing__box__text {
display: none;
}
}
.front {
left: -5%;
}
.back {
top: 11%;
left: -9%;
}
.des {
top: 24%;
left: -9%;
}
@media (max-width: 575.98px) {
.mobile__none {
display: none;
}
}
.mobile__block {
display: block;
animation: fadeInFromNone 0.5s ease-out;
}
@media (max-width: 575.98px) {
@keyframes fadeInFromNone {
0% {
display: none;
opacity: 0;
transform: translateY(-100px);
}
10% {
display: block;
opacity: 0;
transform: translateY(-50px);
}
100% {
display: block;
opacity: 1;
transform: translateY(0px);
}
}
}
.outstaffing__box > div > ul {
padding-left: 0;
}
.border {
border: 2px solid #cdeaba;
}
.items > li {
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
list-style: none;
cursor: pointer;
}
@media (max-width: 1199px) {
.outstaffing__box {
margin-top: 0;
}
.outstaffing__box>div {
margin-top: 32px;
}
}

View File

@ -1,84 +0,0 @@
import React from 'react';
import OutsideClickHandler from 'react-outside-click-handler';
import { useDispatch, useSelector } from 'react-redux';
import { selectItems, selectedItems, filteredCandidates, auth } from '../../redux/outstaffingSlice';
import { fetchItemsForId } from '../../server/server';
import style from './Outstaffing.module.css';
import { fetchProfile } from '../../server/server';
import { useHistory } from 'react-router-dom';
import { getRole } from '../../redux/roleSlice';
const handlePositionClick = ({dispatch, positionId, isSelected, onSelect, history, role}) => {
if(isSelected) {
fetchProfile({ link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`, index: 4, history, role, logout: dispatch(auth(false)) }).then((profileArr) => {
dispatch(filteredCandidates(profileArr));
dispatch(selectedItems([]));
onSelect(positionId);
}
);
} else {
fetchItemsForId({ link: `${process.env.REACT_APP_API_URL}/api/profile?position_id=`, index: positionId, history, role, logout: dispatch(auth(false)) }).then((el) => {
dispatch(filteredCandidates(el));
dispatch(selectedItems([]));
onSelect(positionId);
}
);
}
};
const OutstaffingBlock = ({ dataTags = [], selected, img, header, positionId, isSelected, onSelect }) => {
const history = useHistory();
const role = useSelector(getRole);
const dispatch = useDispatch();
const itemsArr = useSelector(selectItems);
const handleBlockClick = (item, id) => {
if (!itemsArr.find((el) => item === el.value)) {
dispatch(selectedItems([...itemsArr, { id, value: item, label: item }]));
}
};
let classes;
dataTags.forEach((el) => {
if (el.name === 'skills_back') {
classes = style.back;
} else if (el.name === 'skills_design') {
classes = style.des;
} else if (el.name === 'skills_front') {
classes = style.front;
}
});
return (
<OutsideClickHandler
onOutsideClick={() => {
isSelected && onSelect(null)
}}
>
<div className={`${style.outstaffing__box} ${isSelected?style.outstaffing__box__selected:''}`} >
<div className={`${style.outstaffing__box__img} ${selected ? style.border : ''}`} onClick={()=>handlePositionClick({dispatch, positionId, isSelected, onSelect, history, role})}>
<h3>{header}</h3>
<img className={classes} src={img} alt="img" />
</div>
<div className={`${selected ? style.mobile__block : style.mobile__none}`}>
<p className={style.outstaffing__box__text}># Популярный стек</p>
{dataTags && (
<ul className={style.items}>
{dataTags.map((item) => (
<li key={item.id} onClick={() => handleBlockClick(item.value, item.id)}>
{item.value}
</li>
))}
</ul>
)}
</div>
</div>
</OutsideClickHandler>
);
};
export default OutstaffingBlock;

View File

@ -0,0 +1,73 @@
.outstaffing {
&__title {
margin-top: 60px;
h2 {
text-align: center;
color: #52b709;
font-family: 'GT Eesti Pro Display';
font-size: 5em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 77.81px;
span {
color: #282828;
font-style: normal;
letter-spacing: 0.56px;
line-height: normal;
}
}
}
}
@media (max-width: 375.98px) {
.outstaffing__title > h2 {
font-size: 4.5em;
line-height: normal;
}
}
@media (min-width: 376px) {
.outstaffing__title > h2 {
font-size: 5em;
line-height: normal;
}
}
.front {
left: -5%;
}
.back {
top: 11%;
left: -9%;
}
.des {
top: 24%;
left: -9%;
}
@media (max-width: 575.98px) {
@keyframes fadeInFromNone {
0% {
display: none;
opacity: 0;
transform: translateY(-100px);
}
10% {
display: block;
opacity: 0;
transform: translateY(-50px);
}
100% {
display: block;
opacity: 1;
transform: translateY(0px);
}
}
}

View File

@ -0,0 +1,141 @@
import React from 'react'
import OutsideClickHandler from 'react-outside-click-handler'
import { useDispatch, useSelector } from 'react-redux'
import {
selectItems,
selectedItems,
filteredCandidates,
auth
} from '../../redux/outstaffingSlice'
import { fetchGet } from '../../server/server'
import { useHistory } from 'react-router-dom'
import { getRole } from '../../redux/roleSlice'
import './outstaffingBlock.scss'
const handlePositionClick = ({
dispatch,
positionId,
isSelected,
onSelect,
history,
role
}) => {
if (isSelected) {
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`,
params: 4,
history,
role,
logout: () => dispatch(auth(false))
}).then((profileArr) => {
dispatch(filteredCandidates(profileArr))
dispatch(selectedItems([]))
onSelect(positionId)
})
} else {
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/profile?position_id=`,
params: positionId,
history,
role,
logout: () => dispatch(auth(false))
}).then((el) => {
dispatch(filteredCandidates(el))
dispatch(selectedItems([]))
onSelect(positionId)
})
}
}
const OutstaffingBlock = ({
dataTags = [],
selected,
img,
header,
positionId,
isSelected,
onSelect
}) => {
const history = useHistory()
const role = useSelector(getRole)
const dispatch = useDispatch()
const itemsArr = useSelector(selectItems)
const handleBlockClick = (item, id) => {
if (!itemsArr.find((el) => item === el.value)) {
dispatch(selectedItems([...itemsArr, { id, value: item, label: item }]))
}
}
let classes
dataTags.forEach((el) => {
if (el.name === 'skills_back') {
classes = 'back'
} else if (el.name === 'skills_design') {
classes = 'des'
} else if (el.name === 'skills_front') {
classes = 'front'
}
})
return (
<OutsideClickHandler
onOutsideClick={() => {
isSelected && onSelect(null)
}}
>
<div
className={`outstaffing-block${
isSelected ? ' outstaffing-block__selected' : ''
}`}
>
<div
className={`outstaffing-block__img ${
selected ? ' outstaffing-block__border' : ''
}`}
onClick={() =>
handlePositionClick({
dispatch,
positionId,
isSelected,
onSelect,
history,
role
})
}
>
<h3>{header}</h3>
<img className={classes} src={img} alt='img' />
</div>
<div
className={`${
selected
? 'outstaffing-block__mobile--block'
: 'outstaffing-block__mobile--none'
}`}
>
<p className='outstaffing-block__text'># Популярный стек</p>
{dataTags && (
<ul className='outstaffing-block__items'>
{dataTags.map((item) => (
<li
key={item.id}
onClick={() => handleBlockClick(item.value, item.id)}
>
{item.value}
</li>
))}
</ul>
)}
</div>
</div>
</OutsideClickHandler>
)
}
export default OutstaffingBlock

View File

@ -0,0 +1,103 @@
.outstaffing-block__selected .outstaffing-block__img {
background-color: #52b70999;
color: #f9f9f9;
}
.outstaffing-block {
margin-top: 60px;
display: flex;
flex-direction: column;
align-items: center;
& > div {
margin-top: 60px;
& > ul {
padding-left: 0;
}
}
& > p {
font-family: 'GT Eesti Pro Display';
font-size: 1.2em;
font-weight: 300;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
margin-bottom: 0;
}
&__img {
min-width: 260px;
min-height: 120px;
background-color: #f9f9f9;
border-radius: 20px;
position: relative;
h3 {
position: absolute;
right: 25%;
top: 40%;
font-family: 'GT Eesti Pro Display';
font-size: 18px;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
}
img {
position: absolute;
}
}
&__mobile--block {
display: block;
animation: fadeInFromNone 0.5s ease-out;
}
&__border {
border: 2px solid #cdeaba;
}
&__items {
li {
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 36px;
text-align: left;
list-style: none;
cursor: pointer;
}
}
}
@media (max-width: 575.98px) {
.outstaffing-block__mobile--none {
display: none;
}
}
@media (max-width: 1199px) {
.outstaffing-block {
margin-top: 0;
& > div {
margin-top: 32px;
}
}
}
@media (max-width: 575.98px) {
.outstaffing-block {
margin-top: 40px;
&____text {
display: none;
}
}
}

View File

@ -1,93 +1,175 @@
import React, { useState } from 'react'; import React, { useState } from 'react'
import style from './ReportForm.module.css'; import { useSelector, useDispatch } from 'react-redux'
import calendarIcon from '../../images/calendar_icon.png'; import { fetchPost } from '../../server/server'
import ellipse from '../../images/ellipse.png'; import { useHistory, useParams, Redirect } from 'react-router-dom'
import remove from '../../images/remove.png'; import { Loader } from '../Loader/Loader'
import addIcon from '../../images/addIcon.png'; import { auth } from '../../redux/outstaffingSlice'
import { currentMonthAndDayReportPage } from '../Calendar/calendarHelper'; import { getRole } from '../../redux/roleSlice'
import InputsComponent from '../InputsComponent/InputsComponent'; import calendarIcon from '../../images/calendar_icon.png'
import ellipse from '../../images/ellipse.png'
import remove from '../../images/remove.png'
import addIcon from '../../images/addIcon.png'
import { currentMonthAndDayReportPage } from '../Calendar/calendarHelper'
import './reportForm.scss'
const getCreatedDate = () => {
const date = new Date();
const dd = String(date.getDate()).padStart(2, '0')
const mm = String(date.getMonth() + 1).padStart(2, '0')
const yyyy = date.getFullYear()
return `${yyyy}-${mm}-${dd}`
}
const ReportForm = () => { const ReportForm = () => {
const [inputs, setInputs] = useState([1]); const dispatch = useDispatch()
const history = useHistory()
const role = useSelector(getRole)
const [isFetching, setIsFetching] = useState(false)
const [inputs, setInputs] = useState([ { task: '', hours_spent: '', minutes_spent: 0 } ]);
const [troublesInputValue, setTroublesInputValue] = useState('');
const [scheduledInputValue, setScheduledInputValue] = useState('');
const addInput = () => { const addInput = () => {
const lastElement = inputs[inputs.length - 1]; setInputs((prev) => [...prev, { task: '', hours_spent: '', minutes_spent: 0 }])
}
setInputs((prev) => [...prev, lastElement + 1]); const deleteInput = (indexRemove) => {
}; if (indexRemove !== 1) {
setInputs((prev) => prev.filter((el, index) => index !== indexRemove))
const deleteInput = (id) => {
if (id !== 1) {
setInputs((prev) => prev.filter((el) => el !== id));
} }
}; }
return ( return (
<section className="reportForm"> <section className='report-form'>
<div className="row"> <div className='row'>
<div className="col-xl-12"> <div className='col-xl-12'>
<div className={style.reportForm__block}> <div className='report-form__block'>
<div className={style.reportForm__blockTitle}> <div className='report-form__block-title'>
<h2>Добавить отчет</h2> <h2>Добавить отчет</h2>
<h3>Дата заполнения отчета:</h3> <h3>Дата заполнения отчета:</h3>
</div> </div>
<div className={style.reportForm__blockImg}> <div className='report-form__block-img'>
<img className={style.calendarIcon} src={calendarIcon} alt="" /> <img
{currentMonthAndDayReportPage()} className='report-form__calendar-icon'
</div> src={calendarIcon}
<div className={style.reportForm__tasks}> alt=''
<img src={ellipse} alt="" /> />
<span>Какие задачи были выполнены?</span> {currentMonthAndDayReportPage()}
</div> </div>
<div className='report-form__task-list'>
<img src={ellipse} alt='' />
<span>Какие задачи были выполнены?</span>
</div> </div>
</div> </div>
</div> </div>
</div>
<div className="row"> <div className='row'>
<div className="col-8"> <div className='col-8'>
<div className={style.reportForm__text}> <div className='report-form__task-header'>
<p className={style.text1}>Краткое описание задачи</p> <p className='report-form__task-title--description'>
<p className={style.text2}>Количество часов</p> Краткое описание задачи
</div> </p>
<InputsComponent deleteInput={deleteInput} inputsArr={inputs} remove={remove} /> <p className='report-form__task-title--hours'>Количество часов</p>
<div className={style.reportForm__formAdd}>
<img onClick={addInput} src={addIcon} alt="" />
<span>Добавить еще </span>
</div>
</div> </div>
<div className="col-4"></div>
</div>
<div className="row"> {inputs.map((input, index) => {
<div className="col-12"> return (
<div className={style.reportForm__inptuBox}> <form id={'input'} key={`input__${index}`} className='report-form__task-form'>
<div className={style.reportForm__tasks}> <div className='report-form__task-number'>
<img src={ellipse} alt="" /> {index+1}.
<span>Какие сложности возникли?</span> </div>
</div> <div className='report-form__task-input report-form__task-input--description'>
<input type="text" /> <input name='text' type='text' onChange={ e => setInputs(inputs.map( (input, inputIndex) => {
<div className={style.reportForm__tasks}> return index === inputIndex
<img src={ellipse} alt="" /> ? {
<span>Что планируется сделать завтра?</span> ...input,
</div> task: e.target.value
<input type="text" /> }
</div> : input
}))} />
</div>
<div className='report-form__task-input report-form__task-input--hours'>
<input name='number' type='number' min='1' onChange={ e => setInputs(inputs.map( (input, inputIndex) => {
return index === inputIndex
? {
...input,
hours_spent: Number(e.target.value)
}
: input
}))} />
</div>
<div className='report-form__task-remove'>
<img onClick={() => deleteInput(index)} src={remove} alt='' />
</div>
</form>
)
})}
<div className='report-form__form-add'>
<img onClick={addInput} src={addIcon} alt='' />
<span>Добавить еще </span>
</div> </div>
</div> </div>
<div className="row"> <div className='col-4'></div>
<div className="col-12"> </div>
<div className={style.reportForm__footer}>
<button className={style.reportForm__footer__btn} type="submit"> <div className='row'>
Отправить <div className='col-12'>
</button> <div className='report-form__input-box'>
<p className={style.reportForm__footer__text}> <div className='report-form__troubles'>
Всего за день : <span>60 часов</span> <img src={ellipse} alt='' />
</p> <span>Какие сложности возникли?</span>
</div> </div>
<input type='text' value={troublesInputValue} onChange={e => setTroublesInputValue(e.target.value)} />
<div className='report-form__scheduled'>
<img src={ellipse} alt='' />
<span>Что планируется сделать завтра?</span>
</div>
<input type='text' value={scheduledInputValue} onChange={e => setScheduledInputValue(e.target.value)} />
</div> </div>
</div> </div>
</div>
<div className='row'>
<div className='col-12'>
<div className='report-form__footer'>
<button className='report-form__footer-btn' onClick={() => {
fetchPost({
link: `${process.env.REACT_APP_API_URL}/api/reports/create`,
history,
role,
body: {
tasks: inputs,
difficulties: troublesInputValue,
tomorrow: scheduledInputValue,
created_at: getCreatedDate(),
status: 1,
},
logout: () => dispatch(auth(false))
}).then((res) =>
res.json().then((resJSON) => {
setInputs( () => [] )
setTroublesInputValue('');
setScheduledInputValue('');
setIsFetching(false)
setInputs(() => [ { task: '', hours_spent: '', minutes_spent: 0 } ]);
})
)
}}>
{isFetching ? <Loader /> : 'Отправить'}
</button>
<p className='report-form__footer-text'>
Всего за день : <span>60 часов</span>
</p>
</div>
</div>
</div>
</section> </section>
); )
}; }
export default ReportForm; export default ReportForm

View File

@ -1,172 +0,0 @@
.reportForm__blockTitle {
margin-top: 76px;
}
.reportForm__blockTitle > h2 {
color: #282828;
font-family: 'GT Eesti Pro Display';
font-size: 3.3em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 48.74px;
text-align: left;
}
.reportForm__blockTitle > h3 {
font-family: 'GT Eesti Pro Display';
font-size: 2.1em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: 30px;
text-align: left;
margin-top: 52px;
margin-bottom: 35px;
}
.reportForm__blockImg {
width: 280px;
height: 42px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #398208;
background-color: #ffffff;
display: flex;
align-items: center;
font-family: 'GT Eesti Pro Display';
font-size: 13px;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
}
.reportForm__blockImg > img {
margin-left: 20px;
margin-right: 20px;
}
.reportForm__tasks {
margin-top: 40px;
display: flex;
align-items: center;
}
.reportForm__tasks > img {
width: 6px;
height: 6px;
margin-left: 12px;
margin-right: 12px;
}
.reportForm__tasks > span {
color: #18586e;
font-family: 'GT Eesti Pro Display';
font-size: 2em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
}
.reportForm__text {
display: flex;
justify-content: space-between;
margin-top: 40px;
}
.reportForm__text > p {
font-family: 'GT Eesti Pro Display';
font-size: 1.3em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
margin-bottom: 26px;
}
.text1 {
margin-left: 20px;
}
.text2 {
margin-right: 86px;
}
.reportForm__formAdd {
margin-left: 28px;
}
.reportForm__formAdd > span {
font-family: 'GT Eesti Pro Display';
font-size: 1.3em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
margin-left: 20px;
}
.reportForm__inptuBox > input {
margin-left: 10px;
margin-top: 30px;
margin-bottom: 40px;
width: 460px;
height: 42px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
font-size: 1.8em;
padding-left: 20px;
padding-right: 20px;
outline: none;
}
.reportForm__footer {
display: flex;
align-items: center;
}
.reportForm__footer__btn {
width: 166px;
height: 62px;
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
border-radius: 31px;
background-color: #ffffff;
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%),
linear-gradient(
36deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.16) 47%,
rgba(255, 255, 255, 0.17) 50%,
rgba(255, 255, 255, 0) 100%
);
color: #ffffff;
font-family: 'Muller';
font-size: 1.6em;
letter-spacing: normal;
text-align: center;
border: none;
}
.reportForm__footer__text {
font-family: 'GT Eesti Pro Display';
font-size: 1.9em;
font-weight: 600;
font-style: normal;
letter-spacing: normal;
line-height: 22.38px;
text-align: left;
margin-left: 40px;
margin-bottom: 0;
}
.reportForm__footer__text > span {
font-weight: 100;
}

View File

@ -0,0 +1,310 @@
.report-form {
&__block-title {
margin-top: 76px;
h2 {
color: #282828;
font-family: 'GT Eesti Pro Display';
font-size: 3.3em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 48.74px;
text-align: left;
}
h3 {
font-family: 'GT Eesti Pro Display';
font-size: 2.1em;
font-weight: 400;
font-style: normal;
letter-spacing: normal;
line-height: 30px;
text-align: left;
margin-top: 52px;
margin-bottom: 35px;
}
}
&__block-img {
width: 280px;
height: 42px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #398208;
background-color: #ffffff;
display: flex;
align-items: center;
font-family: 'GT Eesti Pro Display';
font-size: 13px;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
img {
margin-left: 20px;
margin-right: 20px;
}
}
&__task {
&-number {
height: 14px;
color: #282828;
font-family: "GT Eesti Pro Display";
font-size: 20px;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 48.74px;
width: 40px;
}
&-list {
margin-top: 40px;
display: flex;
align-items: center;
& > span {
color: #18586e;
font-family: 'GT Eesti Pro Display';
font-size: 2em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
}
img {
width: 6px;
height: 6px;
margin-left: 12px;
margin-right: 12px;
}
}
&-header {
display: flex;
justify-content: flex-start;
margin-top: 40px;
margin-left: 50px;
p {
font-family: 'GT Eesti Pro Display';
font-size: 1.3em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
margin-bottom: 26px;
}
}
&-title {
&--description {
margin-left: 20px;
}
&--hours {
margin-left: 330px;
}
}
&-remove {
margin-left: 20px;
display: flex;
align-items: center;
}
&-form {
display: flex;
margin-left: 20px;
margin-bottom: 28px;
}
&-input {
display: flex;
flex-direction: column;
margin-left: 20px;
&--description {
input {
width: 460px;
height: 42px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
outline: none;
font-size: 1.8em;
padding-left: 20px;
padding-right: 20px;
}
}
&--hours {
input {
width: 141px;
height: 42px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
outline: none;
font-size: 1.8em;
text-align: center;
}
}
}
}
&__form-add {
margin-left: 28px;
span {
font-family: 'GT Eesti Pro Display';
font-size: 1.3em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: normal;
text-align: left;
margin-left: 20px;
}
}
&__input-box {
input {
margin-left: 10px;
margin-top: 30px;
margin-bottom: 20px;
width: 460px;
height: 42px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #c4c4c4;
background-color: #ffffff;
font-size: 1.8em;
padding-left: 20px;
padding-right: 20px;
outline: none;
}
}
&__tasks,
&__troubles,
&__scheduled {
margin-top: 40px;
display: flex;
align-items: center;
span {
color: #18586e;
font-family: 'GT Eesti Pro Display';
font-size: 2em;
font-weight: 500;
font-style: normal;
letter-spacing: normal;
line-height: 16.81px;
text-align: left;
}
img {
width: 6px;
height: 6px;
margin-left: 12px;
margin-right: 12px;
}
}
&__footer {
display: flex;
align-items: center;
margin-top: 20px;
&-btn {
width: 166px;
height: 62px;
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
border-radius: 31px;
background-color: #ffffff;
background-image: linear-gradient(to top, #6aaf5c 0%, #52b709 100%),
linear-gradient(
36deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.16) 47%,
rgba(255, 255, 255, 0.17) 50%,
rgba(255, 255, 255, 0) 100%
);
color: #ffffff;
font-family: 'Muller';
font-size: 1.6em;
letter-spacing: normal;
text-align: center;
border: none;
}
&-text {
font-family: 'GT Eesti Pro Display';
font-size: 1.9em;
font-weight: 600;
font-style: normal;
letter-spacing: normal;
line-height: 22.38px;
text-align: left;
margin-left: 40px;
margin-bottom: 0;
span {
font-weight: 100;
}
}
}
&__form {
position: relative;
display: flex;
justify-content: space-between;
margin-left: 14px;
margin-bottom: 28px;
span {
font-family: 'GT Eesti Pro Display';
font-size: 2em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
text-align: left;
position: absolute;
left: 0;
bottom: 7px;
}
img {
margin-top: 5px;
width: 23px;
height: 23px;
}
}
}
@media (max-width: 575.98px) {
.report-form {
&__task {
&-input {
&--description {
max-width: 460px;
}
&--hours {
max-width: 141px;
}
}
}
}
}

View File

@ -1,64 +1,90 @@
import React, { useState } from 'react'; import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux'
import Select from 'react-select'; import Select from 'react-select'
import { Loader } from '../Loader/Loader'; import { Loader } from '../Loader/Loader'
import style from './TagSelect.module.css'; import style from './TagSelect.module.css'
import { selectedItems, selectItems, selectTags, filteredCandidates, setPositionId, auth } from '../../redux/outstaffingSlice'; import {
import { fetchItemsForId } from '../../server/server'; selectedItems,
import { useHistory } from 'react-router-dom'; selectItems,
import { getRole } from '../../redux/roleSlice'; selectTags,
filteredCandidates,
setPositionId,
auth
} from '../../redux/outstaffingSlice'
import { fetchGet } from '../../server/server'
import { useHistory } from 'react-router-dom'
import { getRole } from '../../redux/roleSlice'
const TagSelect = () => { const TagSelect = () => {
const history = useHistory; const history = useHistory
const role = useSelector(getRole); const role = useSelector(getRole)
const [searchLoading, setSearchLoading] = useState(false); const [searchLoading, setSearchLoading] = useState(false)
const dispatch = useDispatch(); const dispatch = useDispatch()
const itemsArr = useSelector(selectItems); const itemsArr = useSelector(selectItems)
const tagsArr = useSelector(selectTags); const tagsArr = useSelector(selectTags)
const handleSubmit = ({ dispatch, setSearchLoading }) => { const handleSubmit = ({ dispatch, setSearchLoading }) => {
setSearchLoading(true) setSearchLoading(true)
dispatch(setPositionId(null)); dispatch(setPositionId(null))
const filterItemsId = itemsArr.map((item) => item.id).join(); const filterItemsId = itemsArr.map((item) => item.id).join()
fetchItemsForId({ link: `${process.env.REACT_APP_API_URL}/api/profile?skills=`, index: filterItemsId, history, role, logout: dispatch(auth(false)) }).then((el) => { fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/profile?skills=`,
params: filterItemsId,
history,
role,
logout: () => dispatch(auth(false))
}).then((el) => {
dispatch(filteredCandidates(el)) dispatch(filteredCandidates(el))
setSearchLoading(false) setSearchLoading(false)
}); })
// dispatch(selectedItems([])); // dispatch(selectedItems([]));
}; }
return ( return (
<> <>
<section className={style.search}> <section className={style.search}>
<div className="row"> <div className='row'>
<div className="col-12"> <div className='col-12'>
<h2 className={style.search__title}>Найти специалиста по навыкам</h2> <h2 className={style.search__title}>
<div className={style.search__box}> Найти специалиста по навыкам
<Select </h2>
value={itemsArr} <div className={style.search__box}>
onChange={(value) => dispatch(selectedItems(value))} <Select
isMulti value={itemsArr}
name="tags" onChange={(value) => dispatch(selectedItems(value))}
className={style.select} isMulti
classNamePrefix={style.select} name='tags'
options={tagsArr.flat().map((item) => { className={style.select}
return { id: item.id, value: item.value, label: item.value }; classNamePrefix={style.select}
})} options={
/> tagsArr &&
<button onClick={()=>handleSubmit({dispatch, setSearchLoading})} type="submit" className={style.search__submit}> tagsArr.flat().map((item) => {
{ searchLoading ? <Loader width={30} height={30} /> : 'Поиск' } return {
</button> id: item.id,
</div> value: item.value,
label: item.value
}
})
}
/>
<button
onClick={() => handleSubmit({ dispatch, setSearchLoading })}
type='submit'
className={style.search__submit}
>
{searchLoading ? <Loader width={30} height={30} /> : 'Поиск'}
</button>
</div> </div>
</div> </div>
</div>
</section> </section>
</> </>
); )
}; }
export default TagSelect; export default TagSelect

View File

@ -1,53 +0,0 @@
import React from 'react'
import { Link } from 'react-router-dom'
import { Achievement } from '../Achievement/Achievement'
import maleBig from '../../images/medium_male_big.png'
import './sidebar.scss'
const getYearsString = (years) => {
let yearsString
if (years % 10 === 1) {
yearsString = 'год'
} else if (years === 11 || years === 12 || years === 13 || years === 14) {
yearsString = 'лет'
} else if (years % 10 === 2 || years % 10 === 3 || years % 10 === 4) {
yearsString = 'года'
} else {
yearsString = 'лет'
}
return `${years} ${yearsString}`
}
const Sidebar = ({ candidate }) => {
console.log('c', candidate)
return (
<div className='candidateSidebar'>
<div className='candidateSidebar__info'>
<img src={candidate.photo} alt='' />
{candidate && candidate.years_of_exp && (
<>
<p className='candidateSidebar__experience-title'>Опыт работы</p>
<p className='candidateSidebar__experience'>
{getYearsString(candidate.years_of_exp)}
</p>
</>
)}
<Link to={`/candidate/${candidate.id}/form`}>
<button className='candidateSidebar__select'>
Выбрать к собеседованию
</button>
</Link>
<div className='candidateSidebar__achievements'>
{candidate &&
candidate.achievements &&
candidate.achievements.map((item) => {
return <Achievement achievement={item.achievement} />
})}
</div>
</div>
</div>
)
}
export default Sidebar

View File

@ -0,0 +1,18 @@
import React from 'react'
import './skillSection.scss'
const SkillSection = ({ skillsArr }) => {
return (
<div className='skill-section'>
<h3>Навыки:</h3>
<ul>
{skillsArr &&
skillsArr.map((skills) => (
<li key={skills.id}>{skills.skill.name}</li>
))}
</ul>
</div>
)
}
export default SkillSection

View File

@ -0,0 +1,42 @@
.skill-section {
display: flex;
border: 1px solid #69bf2c;
padding: 28px 40px 16px 30px;
margin-top: 60px;
border-radius: 10px;
margin-bottom: 60px;
h3 {
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 700;
font-style: normal;
letter-spacing: normal;
line-height: 28px;
text-align: left;
}
ul {
list-style: none;
display: flex;
padding-left: 0;
flex-wrap: wrap;
li {
font-family: 'GT Eesti Pro Display';
font-size: 1.8em;
font-weight: 100;
font-style: normal;
letter-spacing: normal;
line-height: 28px;
text-align: left;
margin-left: 20px;
}
}
}
@media (max-width: 575.98px) {
.skill-section {
display: none;
}
}

View File

@ -0,0 +1,38 @@
import React from 'react';
export const GetOptionTask = ({type, answer, handleChange, inputValue}) => {
switch (type) {
case "3":
return (
<div className="form-task__group" key={answer.id}>
<input className='form-task__check' type="checkbox" value={answer.answer_body} id={answer.id}
onChange={handleChange}/>
<label htmlFor={answer.id}>{answer.answer_body}</label>
</div>
)
case "2":
case "4":
return (
<div className="form-task__group" key={answer.id}>
<input className='form-task__check' type="radio" value={answer.answer_body} name={'radio'} id={answer.id}
onChange={handleChange}/>
<label htmlFor={answer.id}>{answer.answer_body}</label>
</div>
)
case "1":
return (
<div className="form-task__group">
<textarea className='form-task__field' value={inputValue}
onChange={handleChange}/>
</div>
)
default:
return (
<div className="form-task__group" key={answer.id}>
<input className='form-task__check' type="checkbox" value={answer.answer_body} id={answer.id}
onChange={handleChange}/>
<label htmlFor={answer.id}>{answer.answer_body}</label>
</div>
)
}
}

View File

@ -0,0 +1,30 @@
import {Link} from 'react-router-dom'
import './quiz.scss'
import {useSelector} from "react-redux";
import {selectedTest, selectUserInfo} from "../../../redux/quizSlice";
export const HeaderPageTestsQuiz = ({isVisibilityButton}) => {
const test = useSelector(selectedTest)
const userInfo = useSelector(selectUserInfo);
return (
<div className="header-quiz">
<div className="header-quiz__container">
<div className="header-quiz__body">
<div className="header-quiz__avatar">
{userInfo.photo && <img src={userInfo.photo} alt={userInfo.photo}/>}
</div>
<div className="header-quiz__description">
<div className="header-quiz__title-test title">{test.questionnaire_title}</div>
<div className="header-quiz__subtitle subtitle">Тест на основе выступление
"{test.questionnaire_title}" {userInfo.fio}</div>
</div>
</div>
{isVisibilityButton &&
<Link to={'/quiz-instruction'} className='quiz-btn quiz-btn_restriction'>Пройти</Link>}
</div>
</div>
)
}

View File

@ -0,0 +1,46 @@
import {useEffect} from 'react'
import {useDispatch} from 'react-redux'
import {useSelector} from 'react-redux'
import {fetchGet} from '../../../server/server'
import './quiz.scss'
import {selectUserInfo, setQuestionnairesList, setUserInfo,} from "../../../redux/quizSlice";
export const HeaderQuiz = ({header}) => {
const dispatch = useDispatch()
const userId = localStorage.getItem('id');
const userInfo = useSelector(selectUserInfo);
useEffect(() => {
dispatch(setUserInfo(userId))
}, [dispatch])
useEffect(() => {
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/user-questionnaire/questionnaires-list?user_id=${userId}`,
Origin: `${process.env.REACT_APP_BASE_URL}`,
}
).then(response => {
dispatch(setQuestionnairesList(response))
})
}, [dispatch])
return (
<div className="header-quiz">
<div className="header-quiz__container">
{!userInfo ? <h2>Loading...</h2> :
<>
{header && <h2 className={'header-quiz__title-main'}>Добрый день, {userInfo.fio}</h2>}
<div className="header-quiz__body header-quiz__body_interjacent">
<div className="header-quiz__avatar">
<img src={userInfo.photo} alt={userInfo.photo}/>
</div>
<div className="header-quiz__name-user">{userInfo.fio}</div>
<div className="header-quiz__title">{userInfo.position_name}</div>
</div>
</>
}
</div>
</div>
)
}

View File

@ -0,0 +1,53 @@
import {Link} from 'react-router-dom'
import {CodeSnippetlighter} from '../../../pages/CodeSnippetPage'
import comment from './../../../images/comment.jpg'
import './quiz.scss'
import {useEffect, useState} from "react";
import {useSelector} from "react-redux";
import {selectedTest} from "../../../redux/quizSlice";
import {fetchGet} from "../../../server/server";
export const Instruction = () => {
const [countQuestions, setCountQuestions] = useState(null)
const test = useSelector(selectedTest)
useEffect(async () => {
const response = await fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/user-questionnaire/get-question-number?user_questionnaire_uuid=${test.uuid}`,
Origin: `${process.env.REACT_APP_BASE_URL}`,
}
)
setCountQuestions(response.question_number)
}, [])
return (
<div className="instruction">
<div className="instruction__container">
{!countQuestions ? <h2>Loading...</h2> :
<>
<h3 className="instruction__title quiz-title_h3">Инструкция к тесту</h3>
<div className="instruction__text">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempoLorem ipsum dolor sit
amet,Lo
rem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempo consectetur adipisicing
e
lit, sed do eiusmod tempo
</div>
<Link to="/quiz-test" className='instruction__btn quiz-btn quiz-btn_restriction'>Далее</Link>
<div className="instruction__info">
<div className="instruction__icon">
<img src={comment} alt=""/>
</div>
<div className="instruction__text instruction__text_info">Количество вопросов в
тесте: <span>{countQuestions}</span></div>
</div>
</>
}
</div>
</div>
)
}

View File

@ -0,0 +1,73 @@
import {Link} from 'react-router-dom'
import calendarImage from './../../../images/calendar.svg'
import './quiz.scss'
import {useDispatch} from "react-redux";
import {setSelectedTest} from "../../../redux/quizSlice";
export const MyTestsQuiz = ({listTests}) => {
const formationEndingOfScore = (score) => {
const lastNumber = score % 10
if(score === 11 ||score === 12 ||score === 13 ||score === 14 ){
return 'баллов'
}else if(lastNumber === 2 || lastNumber === 3 || lastNumber === 4 ){
return 'балла'
}else if(lastNumber === 1){
return 'балл'
}else{
return 'баллов'
}
}
const dispatch = useDispatch()
const recordSelectedTest = (item) => dispatch(setSelectedTest(item))
return (
<div className="my-tests">
<div className="my-tests__container">
<div className="my-tests__title">Мои тесты</div>
<div className="my-tests__items">
{listTests.map(item => {
switch (item.status) {
case 1:
return <article className="my-tests__item item-test" key={item.questionnaire_title}>
<h3 className="item-test__name-test">
{item.questionnaire_title}
</h3>
<div className="item-test__body test-data">
<Link to={'/quiz-interjacent'} className='quiz-btn'
onClick={() => recordSelectedTest(item)}>Пройти</Link>
</div>
</article>
case 2:
return <article className="my-tests__item item-test" key={item.questionnaire_title}>
<h3 className="item-test__name-test">
{item.questionnaire_title}
</h3>
<div className="item-test__body test-data">
<div className="test-data__calendar ">
<img src={calendarImage} alt=""/>
{item.testing_date}
</div>
<div className="test-data__hr"></div>
<div className="test-data__score quiz-text">{item.score} {formationEndingOfScore(item.score)}</div>
</div>
</article>
case 3:
return <article className="my-tests__item item-test" key={item.questionnaire_title}>
<h3 className="item-test__name-test">
{item.questionnaire_title}
</h3>
<div className="item-test__body test-data">
<div className='quiz-btn'>На проверке</div>
</div>
</article>
default:
break
}
})}
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,18 @@
import { Link } from 'react-router-dom'
import avatar from './../../../images/medium_male.png'
import './quiz.scss'
export const Progressbar = ({indexQuestion, width}) => {
return (
<div className="progressbar">
<div className="progressbar__body">
<div className="progressbar__value">
{indexQuestion}
</div>
<div className="progressbar__strip" >
<div style={{width: width+'%'}}></div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,37 @@
import React, {useEffect, useState} from 'react';
import {useDispatch, useSelector} from "react-redux";
import {fetchResultTest, selectedTest, selectResult} from "../../../redux/quizSlice";
import {fetchGet} from "../../../server/server";
export const Results = () => {
const result = useSelector(selectResult)
const test = useSelector(selectedTest)
const [maxScore, setMaxScore] = useState('')
const dispatch = useDispatch()
useEffect(async () => {
dispatch(fetchResultTest(test.uuid))
const response = await fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/user-questionnaire/get-points-number?user_questionnaire_uuid=${test.uuid}`,
Origin: `${process.env.REACT_APP_BASE_URL}`,
}
)
setMaxScore(response.sum_point)
}, [])
return (
<div className={'result _container'}>
{
!result ? <h1 style={{display: "block"}}>Ожидайте результата...</h1> :
<div className="result__body">
<div className="result__text">Благодарим за прохождение теста</div>
<div className="result__text">Ваш Результат: <span
className="result__score">{result.score}</span> из {maxScore} </div>
</div>
}
</div>
);
};

View File

@ -0,0 +1,149 @@
import React, {useEffect} from 'react'
import {useHistory} from "react-router"
import {CodeSnippetlighter} from '../../../pages/CodeSnippetPage'
import './quiz.scss'
import {useDispatch} from 'react-redux'
import {useState} from 'react'
import {
fetchGetAnswers,
selectAnswer,
selectedTest
} from '../../../redux/quizSlice'
import {useSelector} from 'react-redux'
import {Progressbar} from './ProgressbarQuiz'
import {fetchUserAnswersMany, fetchUserAnswerOne} from './../../../redux/quizSlice'
import {GetOptionTask} from './GetOptionTask'
import {fetchGet} from "../../../server/server";
export const TaskQuiz = () => {
const history = useHistory();
const dispatch = useDispatch()
const listAnswers = useSelector(selectAnswer)
const dataTest = useSelector(selectedTest)
const [index, setIndex] = useState(0);
const [checkedValues, setCheckedValues] = useState([])
const [stripValue, setStripValue] = useState(0);
const [inputValue, setInputValue] = useState('')
const id = localStorage.getItem('id');
const [questions, setQuestions] = useState([])
useEffect(async () => {
const response = await fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/question/get-questions?uuid=${dataTest.uuid}`,
Origin: `${process.env.REACT_APP_BASE_URL}`,
}
)
setQuestions(response)
dispatch(fetchGetAnswers(response[0].id))
setStripValue((+index + 1) * 100 / response.length)
}, [dispatch])
const nextQuestion = async (e) => {
e.preventDefault()
//Проверка на валидацию ответов
if (checkedValues.length || inputValue) {
switch (questions[index].question_type_id) {
case '3':
await dispatch(fetchUserAnswersMany(checkedValues))
break;
case '2':
case '1':
case '4':
await dispatch(fetchUserAnswerOne(checkedValues))
break;
default:
break;
}
//Проверка на существование следующего запроса
if (index < questions.length - 1) {
await dispatch(fetchGetAnswers(questions[index + 1].id))
setIndex(prev => prev >= questions.length - 1 ? prev : prev + 1)
setStripValue((prev => prev + (100 / questions.length)))
setCheckedValues([]);
setInputValue('')
} else {
history.push(`/quiz-result`)
alert("Тест пройден!")
}
} else {
alert("Вы не ответили на вопрос")
}
}
const handleChange = (e) => {
const checked = e.target.checked;
switch (questions[index].question_type_id) {
case '3':
checked ? setCheckedValues(prev => [...prev, {
user_id: id,
user_questionnaire_uuid: dataTest.uuid,
question_id: questions[index].id,
response_body: e.target.value
}]) :
setCheckedValues(prev => [...prev.filter(item => item.response_body !== e.target.value)])
break
case '1':
case '2':
case '4':
setCheckedValues([{
user_id: id,
user_questionnaire_uuid: dataTest.uuid,
question_id: questions[index].id,
response_body: e.target.value
}])
}
};
return (
<React.StrictMode>
<Progressbar indexQuestion={index + 1} width={stripValue}/>
<div className="task">
{!questions.length || !stripValue || !listAnswers.length ?
<h1 className={'_container'} style={{display: "block"}}>Loading....</h1>
:
<div className="task__container">
<div className="task__code code">
{/* <CodeSnippetlighter /> */}
</div>
<h3 className="task__title quiz-title_h3">{questions[index].question_body}</h3>
<div className="task__body">
<form className='task__form form-task'>
{
questions[index].question_type_id === 1 ?
<GetOptionTask
type={1}
inputValue={checkedValues.length ? checkedValues[0].response_body : ''}
handleChange={handleChange}
/>
:
listAnswers.map((answer) => (
<GetOptionTask
key={answer.id}
type={questions[index].question_type_id}
handleChange={handleChange}
answer={answer}
/>
))
}
<div className="form-task__buttons">
{questions.length !== index + 1 &&
<button type='submit' className='quiz-btn'
onClick={(e) => nextQuestion(e)}>Далее</button>}
{questions.length === index + 1 && <button onClick={(e) => nextQuestion(e)}
className='quiz-btn quiz-btn_dark-green'>Завершить</button>}
</div>
</form>
</div>
</div>
}
</div>
</React.StrictMode>
)
}

View File

@ -0,0 +1,45 @@
//перевод в %
@function prc($pxOne, $pxTwo) {
$result: math.div($pxOne, $pxTwo) * 100%;
@return $result;
}
//перевод в rem
@function rem($px) {
$result: math.div($px, 8) + rem;
@return $result;
}
//перевод в em
@function em($px, $size:16) {
$result: math.div($px, $size) + em;
@return $result;
}
//адаптивное свойство
@mixin adaptiv-value($property, $startSize, $minSize, $type) {
$addSize: $startSize - $minSize;
@if $type == 1 {
//только если меньше контейнера, присутствует ограничение
#{$property}: $startSize+px;
@media (max-width: em($maxWidthContainer)) {
#{$property}: calc(
#{$minSize+px} + #{$addSize} *
((100vw - 320px) / #{$maxWidthContainer - 320})
);
}
} @else if $type == 2 {
//только если больше контейнера, min-width
#{$property}: rem($startSize);
@media (min-width: #{$maxWidthContainer + px}) {
#{$property}: calc(
#{rem($minSize)} + #{$addSize} * ((100vw - 320px) / #{$maxWidth - 320})
);
}
} @else {
//всегда на всех экранах
#{$property}: calc(
#{rem($minSize)} + #{$addSize} * ((100vw - 320px) / #{$maxWidth - 320})
);
}
}

View File

@ -0,0 +1,402 @@
@use 'sass:math';
@import 'functions.scss';
$maxWidthContainer: 1123;
._container{
max-width: 1123px;
margin: 0 auto;
padding: 0 10px;
}
.quiz-text{
font-size: 20px;
font-weight: 400;
line-height: math.div(30, 20);
}
.title{
color: #282828;
font-family: "GT Eesti Pro Display";
font-size: 33px;
font-weight: 700;
line-height: math.div(48, 33);
letter-spacing: 0.56px;
}
.subtitle{
color: #373936;
font-family: "GT Eesti Pro Display";
font-size: 20px;
font-weight: 400;
line-height: math.div(25, 20);
}
.quiz-btn{
display: flex;
align-items: center;
justify-content: center;
box-shadow: 6px 5px 20px rgba(82, 151, 34, 0.21);
border-radius: 23px;
color: #ffffff;
outline: none;
border: 2px solid #52b709;
background: #52b709;
transition: 0 all ease 0.8s;
padding: 14px 38px;
line-height: 1;
font-family: "Muller Extra Bold";
font-weight: 400;
font-size: 16px;
text-decoration: none;
&:hover{
text-decoration: none;
color: #52b709;
background: #fff;
}
&_dark-green{
background-color: #1a310c;
border: 2px solid #1a310c;
&:hover{
text-decoration: none;
color: #1a310c;
background: #fff;
}
}
&_restriction{
max-width: 131px;
}
}
.quiz-title_h3{
color: #000000;
font-family: "GT Eesti Pro Display";
font-size: 25px;
font-weight: 400;
line-height: math.div(30, 25);
}
//=============================================
.header-quiz{
@include adaptiv-value("padding-top", 48, 30, 1);
@include adaptiv-value("padding-bottom", 85, 30, 1);
&__container{
max-width: $maxWidthContainer+px;
margin: 0 auto;
padding: 0 10px;
}
&__title-main{
font-family: "GT Eesti Pro Display";
font-size: 24px;
font-weight: bold;
@include adaptiv-value("margin-bottom", 80, 30, 1);
}
&__body{
display: flex;
flex-wrap: wrap;
row-gap: 30px;
margin-bottom: 39px;
}
&__body_interjacent{
align-items: center;
}
&__description{
max-width: 576px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
&__avatar{
position: relative;
display: block;
flex: 0 0 133px;
min-height: 133px;
margin-right: 31px;
img{
position: absolute;
width: 100%;
height: 100%;
object-fit: cover;
top: 0;
left: 0;
}
}
&__name-user{
color: #000000;
font-family: "GT Eesti Pro Display";
font-size: 16px;
font-weight: 400;
line-height: math.div(30, 20);
margin-right: 70px;
}
&__title{
color: #000000;
font-family: "GT Eesti Pro Display";
@include adaptiv-value("font-size", 25, 16, 1);
font-weight: 700;
line-height: math.div(36, 25);
position: relative;
&::before{
position: absolute;
content: "";
width: prc(316, 370);
height: 5px;
border-radius: 3px;
background-color: #54b611;
bottom: -26px;
}
}
}
.my-tests{
font-family: "GT Eesti Pro Display";
// @include adaptiv-value("padding-top", 85, 30, 1);
// @include adaptiv-value("padding-bottom", 85, 30, 1);
&__container{
max-width: $maxWidthContainer+px;
margin: 0 auto;
padding: 0 10px;
}
&__title{
color: #000000;
font-size: 25px;
font-weight: 400;
line-height: math.div(30, 25);
@include adaptiv-value("margin-bottom", 80, 30, 1);
}
&__items{
display: flex;
align-items: center;
flex-wrap: wrap;
margin: 0 -41px -42px;
}
}
.item-test{
flex: 0 1 50%;
padding: 0 41px;
margin: 0 0 42px 0;
@media (max-width: 760px) {
flex: 0 1 100%;
}
&__name-test{
color: #373936;
margin-bottom: 29px;
}
.active{
color: #54b611;
}
&__body{
}
}
.test-data{
display: flex;
align-items: center;
&__calendar{
padding: 13px 21px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #c4c4c4;
background-color: #f0f7e0;
font-weight: 400;
font-size: 13px;
display: flex;
align-items: center;
margin-right: 17px;
img{
display: block;
margin-right: 11px;
}
}
&__hr{
width: 28px;
height: 5px;
border-radius: 3px;
background-color: #54b611;
margin-right: 26px;
}
&__select{
max-width: 131px;
}
}
.progressbar{
max-width: $maxWidthContainer+px;
margin: 0 auto;
padding: 0 10px;
&__body{
display: flex;
align-items: center;
}
&__value{
flex: 0 0 39px;
height: 39px;
background-color: #5cb42b;
border-radius: 50%;
color: #fff;
letter-spacing: 1.1px;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
// flex: 1 1 auto;
margin-right: 19px;
}
&__strip{
flex: 1 1 auto;
height: 19px;
div{
border-radius: 10px;
height: 100%;
background-color: #5cb42b;
}
}
}
.task{
@include adaptiv-value("padding-top", 35, 10, 1);
&__container{
max-width: $maxWidthContainer+px;
margin: 0 auto;
padding: 0 10px;
}
&__title{
margin-bottom: 39px;
}
&__form{
}
}
.form-task{
&__field{
padding: 10px 15px;
font-size: 17px;
font-family: 'GT Eesti Pro Display';
letter-spacing: 0.3px;
outline: none;
border: 1px solid #52b709;
width: 100%;
resize: vertical;
}
&__check {
padding: 0;
height: initial;
width: initial;
margin-bottom: 0;
display: none;
cursor: pointer;
}
&__buttons{
display: flex;
gap: 56px;
flex-wrap: wrap;
align-items: center;
@include adaptiv-value("margin-top", 60, 30, 1);
}
}
.form-task__group{
display: block;
margin-bottom: 15px;
label {
color: #373936;
font-family: "GT Eesti Pro Display";
font-size: 18px;
font-weight: 300;
line-height: math.div(28,18);
position: relative;
cursor: pointer;
&:before {
content:'';
-webkit-appearance: none;
background-color: transparent;
border: 2px solid #52b709;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), inset 0px -15px 10px -12px rgba(0, 0, 0, 0.05);
padding: 11px;
display: inline-block;
position: relative;
vertical-align: middle;
cursor: pointer;
margin-right: 30px;
}
}
input[type="radio"] + label:before{
border-radius: 50%;
}
input:checked + label:before{
background: #52b709;
}
input:checked + label:after {
content: "";
display: block;
position: absolute;
top: 6px;
left: 9px;
width: 7px;
height: 14px;
border: solid #fff;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
}
.instruction{
&__container{
max-width: $maxWidthContainer+px;
margin: 0 auto;
padding: 0 10px;
}
&__title{
@include adaptiv-value("margin-bottom", 62, 20, 1);
}
&__text{
color: #373936;
font-family: "GT Eesti Pro Display";
font-size: 18px;
font-weight: 300;
line-height: math.div(28, 18);
@include adaptiv-value("margin-bottom", 50, 20, 1);
span{
color: #54b611;
font-weight: 700;
}
&_info{
margin-bottom: 0;
}
}
&__info{
display: flex;
gap: 24px;
align-items: center;
}
&__icon{
width: 36px;
height: 33px;
}
&__btn{
margin-bottom: 34px;
}
}
.result{
font-family: "GT Eesti Pro Display";
&__body{
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 10px;
}
&__text{
font-size: 24px;
font-weight: bold;
}
&__score{
color: #5cb42b;
}
}

1
src/images/calendar.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16"><g><g><image width="16" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAfUlEQVQ4jeXSMQrCUBCE4S9tII1HsU7hyXIQD+EZPEDwLIJi9Yg2+1QQMftaB/5iB3bYgeWlTaDBA1OQ9vYouCcpsWvB0HDBgJtI+9btl3euAcdGSg3YNXJ9rwDbYO38rFB1CNbOHwH/fEEnrw4XOGGWf6I5dvUY5Z9oRP8AEMJY2tGN6tUAAAAASUVORK5CYII="/></g></g></svg>

After

Width:  |  Height:  |  Size: 457 B

BIN
src/images/comment.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 423 B

After

Width:  |  Height:  |  Size: 423 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="5" height="9" viewBox="0 0 5 9"><g><g><image width="5" height="9" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAJCAYAAAD6reaeAAAAaklEQVQImVWOvRFAQBSE10jez6XXGCOkABrQBw0Q0w89CDRgnuDO3Nlw55tvF8ijPELpTIWTHkIG4TkS1ELIoLzCrAAcVxB6ILyjacrPc0P5gvcuc3EdyS2RwdkFpyzBmdaHuD79bsafxwt/CxfNS2+70AAAAABJRU5ErkJggg=="/></g></g></svg>

Before

Width:  |  Height:  |  Size: 427 B

View File

@ -1,9 +1,11 @@
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router';
import { WithLogout } from '../hoc/withLogout'; import { WithLogout } from '../hoc/withLogout';
import Calendar from '../components/Calendar/Calendar'; import Calendar from '../components/Calendar/Calendar';
const CalendarPage = () => { const CalendarPage = () => {
return <WithLogout><Calendar /></WithLogout>; const history = useHistory();
return <WithLogout><Calendar onSelect={() => { history.push('/report/0') }} /></WithLogout>;
}; };
export default CalendarPage; export default CalendarPage;

View File

@ -0,0 +1,21 @@
import { useEffect, useState } from "react";
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { a11yDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
export const CodeSnippetlighter = () => {
const [codeString, setCodeString] = useState(``)
useEffect(()=>{
fetch('/code.txt')
.then((r) => r.text())
.then(text => {
setCodeString(text)
})
}, [])
return (
<SyntaxHighlighter language={"javascript"} style={a11yDark} wrapLongLines={false} customStyle={{fontSize:14}} showLineNumbers={true}>
{codeString}
</SyntaxHighlighter>
);
};

View File

@ -1,82 +1,99 @@
import React, { useState } from 'react'; import React, { useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux'
import { useHistory, useParams, Link } from 'react-router-dom'; import { useHistory, useParams, Link } from 'react-router-dom'
import { currentCandidate, selectCurrentCandidate, auth } from '../redux/outstaffingSlice'; import {
import SVG from 'react-inlinesvg'; currentCandidate,
import { WithLogout } from '../hoc/withLogout'; selectCurrentCandidate,
import Form from '../components/Form/Form'; auth
import { LEVELS, SKILLS } from '../components/constants/constants'; } from '../redux/outstaffingSlice'
import { fetchItemsForId } from '../server/server'; import SVG from 'react-inlinesvg'
import { Footer } from '../components/Footer/Footer'; import { WithLogout } from '../hoc/withLogout'
import Form from '../components/Form/Form'
import { LEVELS, SKILLS } from '../components/constants/constants'
import { fetchGet } from '../server/server'
import { Footer } from '../components/Footer/Footer'
import arrow from '../images/right-arrow.png'; import arrow from '../images/right-arrow.png'
import rectangle from '../images/rectangle_secondPage.png'; import rectangle from '../images/rectangle_secondPage.png'
import telegramIcon from '../images/telegram-icon.svg'; import telegramIcon from '../images/telegram-icon.svg'
import './formPage.scss'; import './formPage.scss'
import { getRole } from '../redux/roleSlice'; import { getRole } from '../redux/roleSlice'
const goBack = (history) => { const goBack = (history) => {
history.goBack(); history.goBack()
};
const FormPage = () => {
const params = useParams();
const history = useHistory();
const dispatch = useDispatch();
const candidate = useSelector(selectCurrentCandidate);
const role = useSelector(getRole);
if(!candidate.id) {
fetchItemsForId({ link: `${process.env.REACT_APP_API_URL}/api/profile/`, index: Number(params.id), history, role, logout: dispatch(auth(false)) }).then((el) =>
dispatch(currentCandidate(el))
);
}
return (
<WithLogout>
<div className='form-page'>
<div className='form-page__back'>
<div className='form-page__arrow' onClick={() => goBack(history)}>
<div className='form-page__arrow-img'>
<img src={arrow} alt="" />
</div>
<div className='form-page__back-to-candidate'>
<span>Вернуться к кандидату</span>
</div>
</div>
</div>
<div className='form-page__candidate'>
<div className='form-page__avatar'>
<img src={candidate.photo} />
</div>
<div className='form-page__candidate-info'>
<div className='form-page__position'>
<span>{candidate.specification} {SKILLS[candidate.position_id]}, {LEVELS[candidate.level]}</span>
</div>
<div className='form-page__selected'>
<img src={rectangle} />
<span>Выбранный кандидат</span>
</div>
</div>
</div>
<div className='form-page__interview'>
<div className='form-page__form'><Form /></div>
<div className='form-page__separator'>
<div className='form-page__line'></div>
<div className='form-page__option'>или</div>
</div>
<div className='form-page__telegram'>
<div className='form-page__telegram-text'>Заявка на собеседование через телеграм</div>
<div className='form-page__telegram-icon'>
<a href='https://t.me/st0kir' target='_blank'><SVG src={telegramIcon} /></a>
</div>
</div>
</div>
<Footer />
</div>
</WithLogout>
)
} }
export default FormPage; const FormPage = () => {
const params = useParams()
const history = useHistory()
const dispatch = useDispatch()
const candidate = useSelector(selectCurrentCandidate)
const role = useSelector(getRole)
if (!candidate.id) {
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/profile/`,
params: Number(params.id),
history,
role,
logout: () => dispatch(auth(false))
}).then((el) => dispatch(currentCandidate(el)))
}
return (
<WithLogout>
<div className='form-page'>
<div className='form-page__back'>
<div className='form-page__arrow' onClick={() => goBack(history)}>
<div className='form-page__arrow-img'>
<img src={arrow} alt='' />
</div>
<div className='form-page__back-to-candidate'>
<span>Вернуться к кандидату</span>
</div>
</div>
</div>
<div className='form-page__candidate'>
<div className='form-page__avatar'>
<img src={candidate.photo} />
</div>
<div className='form-page__candidate-info'>
<div className='form-page__position'>
<span>
{candidate.specification} {SKILLS[candidate.position_id]},{' '}
{LEVELS[candidate.level]}
</span>
</div>
<div className='form-page__selected'>
<img src={rectangle} />
<span>Выбранный кандидат</span>
</div>
</div>
</div>
<div className='form-page__interview'>
<div className='form-page__form'>
<Form />
</div>
<div className='form-page__separator'>
<div className='form-page__line'></div>
<div className='form-page__option'>или</div>
</div>
<div className='form-page__telegram'>
<div className='form-page__telegram-text'>
Заявка на собеседование через телеграм
</div>
<div className='form-page__telegram-icon'>
<a href='https://t.me/st0kir' target='_blank'>
<SVG src={telegramIcon} />
</a>
</div>
</div>
</div>
<Footer />
</div>
</WithLogout>
)
}
export default FormPage

View File

@ -4,11 +4,12 @@ import arrowLeft from '../images/right-arrow.png'
import SVG from 'react-inlinesvg' import SVG from 'react-inlinesvg'
import prevDateArrowIcon from '../images/prevDateArrow.svg' import dateArrowIcon from '../images/dateArrow.svg'
import nextDateArrowIcon from '../images/nextDateArrow.svg' import calendarIcon from '../images/calendar.svg'
import { TaskItem } from '../components/TaskItem/TaskItem'
import './singleReportPage.scss' import './singleReportPage.scss'
import { TaskItem } from '../components/TaskItem/TaskItem'
const tasks = [ const tasks = [
{ {
@ -41,16 +42,16 @@ const SingleReportPage = () => {
<div className='single-report-page__title-date'> <div className='single-report-page__title-date'>
<div className='single-report-page__title-date--prev'> <div className='single-report-page__title-date--prev'>
<button> <button>
<SVG src={prevDateArrowIcon} /> <SVG src={dateArrowIcon} />
</button> </button>
</div> </div>
<div className='single-report-page__title-date--actual'> <div className='single-report-page__title-date--actual'>
<img src='' /> <SVG src={calendarIcon} />
<p></p> <p>15 июня</p>
</div> </div>
<div className='single-report-page__title-date--next single-report-page__title-date--enabled'> <div className='single-report-page__title-date--next single-report-page__title-date--enabled'>
<button> <button>
<SVG src={nextDateArrowIcon} /> <SVG src={dateArrowIcon} />
</button> </button>
</div> </div>
</div> </div>

1
src/pages/code.txt Normal file
View File

@ -0,0 +1 @@
Hellow

View File

@ -0,0 +1,23 @@
import {Redirect} from "react-router-dom"
import { HeaderPageTestsQuiz } from "../../components/features/quiz/HeaderPageTests"
import { Instruction } from "../../components/features/quiz/Instructions"
import React from "react";
import {useSelector} from "react-redux";
import {selectedTest} from "../../redux/quizSlice";
export const InstructionPage = () => {
const test = useSelector(selectedTest)
if(!test){
return <Redirect to={'/quiz'} />
}
return (
<>
<HeaderPageTestsQuiz isVisibilityButton={false}/>
<Instruction />
</>
)
}

View File

@ -0,0 +1,24 @@
import {Redirect} from "react-router-dom"
import {HeaderPageTestsQuiz} from "../../components/features/quiz/HeaderPageTests"
import {MyTestsQuiz} from "../../components/features/quiz/MyTestsQuiz"
import {useSelector} from "react-redux";
import {selectedTest, selectPassedTests} from "../../redux/quizSlice";
import React from "react";
export const InterjacentPage = () => {
const test = useSelector(selectedTest)
const passedTests = useSelector(selectPassedTests)
if (!test) {
return <Redirect to={'/quiz'}/>
}
return (
<>
<HeaderPageTestsQuiz isVisibilityButton={true}/>
<MyTestsQuiz listTests={passedTests}/>
</>
)
}

View File

@ -0,0 +1,17 @@
import React from 'react'
import {HeaderQuiz} from "../../components/features/quiz/HeaderQuiz"
import {MyTestsQuiz} from "../../components/features/quiz/MyTestsQuiz"
import {useSelector} from "react-redux";
import {selectQuestionnairesOfUser} from "../../redux/quizSlice";
export const QuizPage = () => {
const allTests = useSelector(selectQuestionnairesOfUser)
return (
<>
<HeaderQuiz header={true}/>
<MyTestsQuiz listTests={allTests}/>
</>
)
}

View File

@ -0,0 +1,22 @@
import {Link, Redirect} from 'react-router-dom'
import {HeaderPageTestsQuiz} from '../../components/features/quiz/HeaderPageTests'
import {Progressbar} from '../../components/features/quiz/ProgressbarQuiz'
import {TaskQuiz} from '../../components/features/quiz/Task'
import {useSelector} from "react-redux";
import {selectedTest} from "../../redux/quizSlice";
import React from "react";
export const QuizTestPage = () => {
const test = useSelector(selectedTest)
if (!test) {
return <Redirect to={'/quiz'}/>
}
return (
<>
<HeaderPageTestsQuiz isVisibilityButton={false}/>
<TaskQuiz/>
</>
)
}

View File

@ -0,0 +1,23 @@
import {Link, Redirect} from "react-router-dom"
import {HeaderPageTestsQuiz} from "../../components/features/quiz/HeaderPageTests"
import {Results} from "../../components/features/quiz/Results";
import {useSelector} from "react-redux";
import {selectedTest} from "../../redux/quizSlice";
import React from "react";
export const ResultPage = () => {
const test = useSelector(selectedTest)
if (!test) {
return <Redirect to={'/quiz'}/>
}
return (
<>
<HeaderPageTestsQuiz isVisibilityButton={false}/>
<Results/>
</>
)
}

View File

@ -9,9 +9,9 @@
&-text { &-text {
margin-left: 3.1rem; margin-left: 3.1rem;
color: #000000; color: #000000;
font-family: 'GT Eesti Pro Display - Thin'; font-family: 'GT Eesti Pro Display';
font-size: 18px; font-size: 18px;
font-weight: 400; font-weight: 300;
letter-spacing: normal; letter-spacing: normal;
line-height: 36px; line-height: 36px;
text-align: left; text-align: left;
@ -40,6 +40,37 @@
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
&--prev {
transform: rotateZ(180deg);
}
&--actual {
width: 125px;
height: 42px;
box-shadow: 0 0 59px rgba(44, 44, 44, 0.05);
border-radius: 5px;
border: 1px solid #c4c4c4;
background-color: #f0f7e0;
display: flex;
justify-content: center;
align-items: center;
margin: 0 65px;
p {
margin-left: 10px;
margin-bottom: 0;
color: #000000;
font-family: "GT Eesti Pro Display";
font-size: 13px;
font-weight: 300;
font-style: normal;
letter-spacing: normal;
line-height: normal;
}
}
} }
button { button {
@ -47,7 +78,7 @@
outline: none; outline: none;
width: 31px; width: 31px;
height: 31px; height: 31px;
background-color: #f6f6f6; background-color: #54b611;
border-radius: 50%; border-radius: 50%;
display: flex; display: flex;
justify-content: center; justify-content: center;

137
src/redux/quizSlice.js Normal file
View File

@ -0,0 +1,137 @@
import {createAsyncThunk, createSlice} from '@reduxjs/toolkit';
import {fetchGet, fetchPost} from './../server/server'
import axios from "axios";
const initialState = {
// questions: [],
answer: [],
result: null,
isLoading: false,
dataQuestionnairesOfUser: [],
passedTests: [],
selectedTest: null,
userInfo: null
};
export const setUserInfo = createAsyncThunk(
'userInfo',
async (id) => {
try{
const response = await fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/user-card/get-user-card?user_id=${id}`,
Origin: `${process.env.REACT_APP_BASE_URL}`,
}
)
return response
}catch (e) {
console.log(e)
}
}
)
export const fetchUserAnswersMany = createAsyncThunk(
'answersUserMany',
async (checkedValues) => {
try{
const response = await axios.post(`${process.env.REACT_APP_API_URL}/api/user-response/set-responses`,
{"userResponses": checkedValues}, {
headers: {
Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
}
})
return response.data
}catch (e) {
console.log(e)
}
}
)
export const fetchUserAnswerOne = createAsyncThunk(
'answersUserOne',
async (checkedValues) => {
try{
const response = await axios.post(`${process.env.REACT_APP_API_URL}/api/user-response/set-response`,
checkedValues[0], {
headers: {
Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
}
})
return response.data
}catch (e) {
console.log(e)
}
}
)
export const fetchGetAnswers = createAsyncThunk(
'answers',
async (question_id) => {
const resp = await fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/answer/get-answers?question_id=${question_id}`,
Origin: `${process.env.REACT_APP_BASE_URL}`,
}
)
return resp
}
)
// export const fetchGetQuestion = createAsyncThunk(
// 'questions',
// async (uuid) => {
// const resp = await fetchGet({
// link: `${process.env.REACT_APP_API_URL}/api/question/get-questions?uuid=${uuid}`,
// Origin: `${process.env.REACT_APP_BASE_URL}`,
// }
// )
// return resp
// }
// )
export const fetchResultTest = createAsyncThunk(
'result',
async (uuid) => {
const resp = await fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/user-questionnaire/questionnaire-completed?user_questionnaire_uuid=${uuid}`,
Origin: `${process.env.REACT_APP_BASE_URL}`,
}
)
return resp
}
)
export const quizSlice = createSlice({
name: 'quiz',
initialState,
reducers: {
setQuestionnairesList: (state, action) => {
state.dataQuestionnairesOfUser = action.payload
state.passedTests = action.payload.filter(item=>item.status === 2)
},
setSelectedTest: (state, action) => {
state.selectedTest = action.payload
},
},
extraReducers: {
[setUserInfo.fulfilled]: (state, action) => {
state.userInfo = action.payload;
},
[fetchGetAnswers.fulfilled]: (state, action) => {
state.answer = action.payload;
},
[fetchResultTest.fulfilled]: (state, action) => {
state.result = action.payload;
},
},
});
export const {setQuestionnairesList, setSelectedTest} = quizSlice.actions;
// export const selectQuestions = (state) => state.quiz.questions;
export const selectAnswer = (state) => state.quiz.answer;
export const selectQuestionnairesOfUser = (state) => state.quiz.dataQuestionnairesOfUser;
export const selectResult = (state) => state.quiz.result;
export const selectIsLoading = (state) => state.quiz.isLoading;
export const selectedTest = (state) => state.quiz.selectedTest;
export const selectPassedTests = (state) => state.quiz.passedTests;
export const selectUserInfo = (state) => state.quiz.userInfo;
export default quizSlice.reducer;

21
src/redux/reportSlice.js Normal file
View File

@ -0,0 +1,21 @@
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
dateSelected: '',
};
export const reportSlice = createSlice({
name: 'report',
initialState,
reducers: {
dateSelected: (state, action) => {
state.dateSelected = action.payload;
},
},
});
export const { dateSelected, } = reportSlice.actions;
export const selectDate = (state) => state.report.dateSelected;
export default reportSlice.reducer;

View File

@ -1,17 +1,22 @@
export const withAuthRedirect = actionCall => ({link, index, history, role, logout}) => { export const withAuthRedirect =
return actionCall(link, index) (actionCall) =>
.then(res => { ({ link, params, history, role, logout, body }) => {
if(res.status && res.status == 401) { const linkWithParams = params
localStorage.clear(); ? `${link}?${new URLSearchParams(params)}`
logout(); : link
history.push(role === 'ROLE_DEV' ? '/authdev' : '/auth') ; return actionCall(linkWithParams, body)
} .then((res) => {
if (res.status && res.status == 401) {
localStorage.clear()
logout && logout()
history.push(role === 'ROLE_DEV' ? '/authdev' : '/auth')
}
return res; return res
}) })
.catch(err => { .catch((err) => {
localStorage.clear(); localStorage.clear()
logout(); logout && logout()
history.push(role === 'ROLE_DEV' ? '/authdev' : '/auth'); history.push(role === 'ROLE_DEV' ? '/authdev' : '/auth')
}) })
} }

View File

@ -1,59 +1,4 @@
import { withAuthRedirect } from "./authRedirect" import { withAuthRedirect } from './authRedirect'
export const fetchProfile = withAuthRedirect(async (link, index) => {
try {
const response = await fetch(`${link}${index}`, {
method: 'GET',
headers: {
// 'Access-Control-Request-Headers': 'authorization',
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`,
// 'Origin': `${process.env.REACT_APP_BASE_URL}`,
}
})
let data = await response.json()
return data
} catch (error) {
console.log('Query error', error)
}
})
export const fetchSkills = withAuthRedirect(async (link) => {
try {
const response = await fetch(link, {
method: 'GET',
headers: {
// 'Access-Control-Request-Headers': 'authorization',
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`,
// 'Origin': `${process.env.REACT_APP_BASE_URL}`,
}
})
let data = await response.json()
return data
} catch (error) {
console.log('Query error', error)
}
})
export const fetchItemsForId = withAuthRedirect(async (link, id) => {
console.log(`Bearer ${localStorage.getItem('auth_token')}`);
try {
const response = await fetch(`${link}${id}`, {
method: 'GET',
headers: {
// 'Access-Control-Request-Headers': 'authorization',
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`,
// 'Origin': `${process.env.REACT_APP_BASE_URL}`,
}
})
let data = await response.json()
return data
} catch (error) {
console.log('Query error', error)
}
})
export const fetchForm = withAuthRedirect(async (link, info) => { export const fetchForm = withAuthRedirect(async (link, info) => {
try { try {
@ -61,9 +6,9 @@ export const fetchForm = withAuthRedirect(async (link, info) => {
method: 'POST', method: 'POST',
headers: { headers: {
// 'Access-Control-Request-Headers': 'authorization', // 'Access-Control-Request-Headers': 'authorization',
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`, Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
// 'Origin': `${process.env.REACT_APP_BASE_URL}`, Origin: `${process.env.REACT_APP_BASE_URL}`,
'Content-Type': 'application/json', 'Content-Type': 'application/json'
}, },
body: JSON.stringify(info) body: JSON.stringify(info)
}) })
@ -74,38 +19,100 @@ export const fetchForm = withAuthRedirect(async (link, info) => {
} }
}) })
export const fetchAuth = async ({ username, password, dispatch, catchError }) => { export const fetchAuth = async ({
const baseURL = process.env.REACT_APP_BASE_URL; username,
const apiURL = process.env.REACT_APP_API_URL; password,
dispatch,
catchError
}) => {
const baseURL = process.env.REACT_APP_BASE_URL
const apiURL = process.env.REACT_APP_API_URL
try { try {
const response = await fetch( const response = await fetch(`${apiURL}/api/user/login`, {
`${apiURL}/api/user/login`, method: 'POST',
{ mode: 'cors',
method: 'POST', headers: {
headers: { 'Access-Control-Request-Headers': 'authorization',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
// 'Origin': `${baseURL}`, // Origin: `http://localhost`
}, },
body: JSON.stringify({ body: JSON.stringify({
username, username,
password password
}) })
} })
)
if(!response.ok) { if (!response.ok) {
catchError(); catchError()
return response.statusText; return response.statusText
} }
response response.json().then((resJSON) => {
.json() localStorage.setItem('auth_token', resJSON.access_token)
.then((resJSON) => { localStorage.setItem('id', resJSON.id)
localStorage.setItem('auth_token', resJSON.access_token) localStorage.setItem(
localStorage.setItem('access_token_expired_at', resJSON.access_token_expired_at) 'access_token_expired_at',
dispatch(); resJSON.access_token_expired_at
}) )
dispatch()
})
} catch (error) { } catch (error) {
console.error('Error occured: ', error) console.error('Error occured: ', error)
} }
} }
export const fetchReportList = withAuthRedirect(async (link) => {
try {
const response = await fetch(
`https://guild.loc/api/reports/index?user_id=26&fromDate=2021-10-18`,
// link,
{
method: 'GET',
headers: {
Authorization: `Bearer ${localStorage.getItem('auth_token')}`
}
}
)
let data = await response.json()
return data
} catch (error) {
console.log('Query error', error)
}
})
export const fetchGet = withAuthRedirect(async (link) => {
try {
const response = await fetch(link, {
method: 'GET',
headers: {
Authorization: `Bearer ${localStorage.getItem('auth_token')}`
}
})
let data = await response.json()
return data
} catch (error) {
console.log('Query error', error)
}
})
export const fetchPost = withAuthRedirect(async (link, body) => {
console.log('i',body)
try {
const response = await fetch(link, {
method: 'POST',
headers: {
Authorization: `Bearer ${localStorage.getItem('auth_token')}`,
'Content-Type': 'application/json',
//Origin: `http://localhost:3000`
Origin: `${process.env.REACT_APP_BASE_URL}`,
},
body: JSON.stringify(body)
})
return response
} catch (error) {
console.log('Query error', error)
}
})

View File

@ -2,11 +2,20 @@ import { configureStore } from '@reduxjs/toolkit';
import outstaffingReducer from '../redux/outstaffingSlice'; import outstaffingReducer from '../redux/outstaffingSlice';
import loaderReducer from '../redux/loaderSlice'; import loaderReducer from '../redux/loaderSlice';
import roleReducer from '../redux/roleSlice'; import roleReducer from '../redux/roleSlice';
import reportReducer from '../redux/reportSlice';
import quizSlice from '../redux/quizSlice';
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
outstaffing: outstaffingReducer, outstaffing: outstaffingReducer,
loader: loaderReducer, loader: loaderReducer,
role: roleReducer role: roleReducer,
report: reportReducer,
quiz: quizSlice,
}, },
middleware: getDefaultMiddleware =>
getDefaultMiddleware({
serializableCheck: false,
}),
}); });