diff --git a/package-lock.json b/package-lock.json index fef38906..f6aaad30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1938,6 +1938,24 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz", "integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==" }, + "@reduxjs/toolkit": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.6.0.tgz", + "integrity": "sha512-eGL50G+Vj5AG5uD0lineb6rRtbs96M8+hxbcwkHpZ8LQcmt0Bm33WyBSnj5AweLkjQ7ZP+KFRDHiLMznljRQ3A==", + "requires": { + "immer": "^9.0.1", + "redux": "^4.1.0", + "redux-thunk": "^2.3.0", + "reselect": "^4.0.0" + }, + "dependencies": { + "immer": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.3.tgz", + "integrity": "sha512-mONgeNSMuyjIe0lkQPa9YhdmTv8P19IeHV0biYhcXhbd5dhdB9HSK93zBpyKjp6wersSUgT5QyU0skmejUVP2A==" + } + } + }, "@restart/context": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz", @@ -2371,6 +2389,15 @@ "@types/node": "*" } }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/html-minifier-terser": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", @@ -2466,6 +2493,17 @@ "csstype": "^3.0.2" } }, + "@types/react-redux": { + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.16.tgz", + "integrity": "sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "@types/react-transition-group": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.1.tgz", @@ -12695,6 +12733,19 @@ "warning": "^4.0.3" } }, + "react-redux": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.4.tgz", + "integrity": "sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA==", + "requires": { + "@babel/runtime": "^7.12.1", + "@types/react-redux": "^7.1.16", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + } + }, "react-refresh": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", @@ -12975,6 +13026,24 @@ "strip-indent": "^3.0.0" } }, + "redux": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.0.tgz", + "integrity": "sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "redux-devtools-extension": { + "version": "2.13.9", + "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz", + "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==" + }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -13129,6 +13198,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, + "reselect": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz", + "integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==" + }, "resolve": { "version": "1.18.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", diff --git a/package.json b/package.json index 0c57fda0..1cc26e81 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "homepage": "https://html.craft-group.xyz/outstaffing/", "dependencies": { + "@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", @@ -13,9 +14,11 @@ "react": "^17.0.2", "react-bootstrap": "^1.6.0", "react-dom": "^17.0.2", + "react-redux": "^7.2.4", "react-router-dom": "^5.2.0", "react-scripts": "4.0.3", "react-select": "^4.3.1", + "redux-devtools-extension": "^2.13.9", "sass": "^1.34.0", "sass-loader": "^11.1.1", "web-vitals": "^1.1.2" diff --git a/src/App.js b/src/App.js index 6e92da47..eaf0af78 100644 --- a/src/App.js +++ b/src/App.js @@ -13,11 +13,17 @@ const ReportPage = lazy(() => import('./pages/ReportFormPage.js')); const App = () => { const [isAuth, setIsAuth] = useState(true); const [candidates, setCandidates] = useState([]); + const [candidateForCalendar, setCandidateForCalendar] = useState([]); const getCandidate = (candidateArr) => { + console.log('candidateArr ', candidateArr); setCandidates(candidateArr); }; + const getCandidateForCalendar = (candidate) => { + setCandidateForCalendar(candidate); + }; + return ( Loading...}> @@ -27,10 +33,10 @@ const App = () => { - + - + diff --git a/src/components/Calendar/Calendar.js b/src/components/Calendar/Calendar.js index 375a2a3c..5aac10b9 100644 --- a/src/components/Calendar/Calendar.js +++ b/src/components/Calendar/Calendar.js @@ -6,13 +6,17 @@ import rectangle from '../../images/rectangle_secondPage.png'; import CalendarComponent from './CalendarComponent'; import { currentMonth } from './calendarHelper'; -const Calendar = () => { +const Calendar = ({ candidateForCalendar }) => { const [month, setMonth] = useState(''); useEffect(() => { setMonth(currentMonth); }, [month]); + const { name, skillsName } = candidateForCalendar; + + const abbreviatedName = name.substring(0, name.lastIndexOf(' ')); + return (
@@ -23,12 +27,10 @@ const Calendar = () => {
img -

- Александр
Комов -

+

{abbreviatedName}

-

Frontend разработчик, Middle

+

{skillsName} разработчик

img
diff --git a/src/components/Candidate/Candidate.js b/src/components/Candidate/Candidate.js index 9e7b297b..cdb13c86 100644 --- a/src/components/Candidate/Candidate.js +++ b/src/components/Candidate/Candidate.js @@ -11,7 +11,7 @@ import SectionFour from './sections/SectionFour'; import SectionFive from './sections/SectionFive'; import SectionSkills from './sections/SectionSkills'; -const Candidate = ({ candidatesArr }) => { +const Candidate = ({ candidatesArr, getCandidateForCalendar }) => { const history = useHistory(); const { id: candidateId } = useParams(); @@ -66,7 +66,7 @@ const Candidate = ({ candidatesArr }) => {
- +
diff --git a/src/components/Description/Description.js b/src/components/Description/Description.js index f3bef20b..a5d33440 100644 --- a/src/components/Description/Description.js +++ b/src/components/Description/Description.js @@ -17,9 +17,7 @@ const Description = ({ candidatesListArr, getCandidate, onLoadMore }) => {
-

- {el.name} - {el.skillsName} -

+

{el.header} разработчик

- 10 лет пишу приложения под IOS, отлично владею Objective-C и Swift.

diff --git a/src/components/Home/Home.js b/src/components/Home/Home.js index 9d235d9d..c53cc6c2 100644 --- a/src/components/Home/Home.js +++ b/src/components/Home/Home.js @@ -1,80 +1,26 @@ import React, { useState, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import Outstaffing from '../Outstaffing/Outstaffing'; import Description from '../Description/Description'; import front from '../../images/front_end.png'; import back from '../../images/back_end.png'; import design from '../../images/design.png'; import { fetchProfile, fetchSkills } from '../../server/server'; - -const tabsList = [ - { - name: 'Frontend', - img: front, - text: '# Популярный стек', - header: 'Фронтенд', - }, - { - name: 'Backend', - img: back, - text: '# Популярный стек', - header: 'Бэкенд', - }, - { - name: 'Design', - img: design, - text: '# Популярный стек', - header: 'Дизайн', - }, -]; +import { profiles, selectProfiles, tags, candidates, selectCandidates, selectTab } from '../../redux/outstaffingSlice'; const Home = ({ getCandidate }) => { - const [tabs, setTabs] = useState([]); - const [tags, setTags] = useState([]); - const [profiles, setProfiles] = useState([]); - const [selectedTab, setSelectedTab] = useState(''); - const [countArr, setCountArr] = useState(2); - const [candidatesArray, setCandidatesArray] = useState([]); + const [count, setCount] = useState(2); + + const dispatch = useDispatch(); + const profilesArr = useSelector(selectProfiles); + const candidatesArr = useSelector(selectCandidates); + const selectedTab = useSelector(selectTab); useEffect(() => { - setTabs(tabsList); - fetchProfile('https://guild.craft-group.xyz/api/profile') - .then((profileArr) => setProfiles(profileArr)) + .then((profileArr) => dispatch(profiles(profileArr))) .catch((e) => console.log(e)); - }, []); - useEffect(() => { - if (profiles.length) { - setCandidatesArray( - profiles.map((profile) => { - let skillsName = ''; - let img; - - if (Number(profile.position_id) === 1) { - skillsName = 'Frontend'; - img = front; - } else if (Number(profile.position_id) === 2) { - skillsName = 'Backend'; - img = back; - } else if (Number(profile.position_id) === 3) { - skillsName = 'Marketer'; - img = design; - } - - return { - id: profile.id, - profileId: profile.position_id, - name: profile.fio, - skills: profile.skillValues, - skillsName: skillsName, - img: img, - }; - }) - ); - } - }, [profiles]); - - useEffect(() => { fetchSkills('https://guild.craft-group.xyz/api/skills/skills-on-main-page').then((skills) => { const keys = Object.keys(skills); const values = Object.values(skills); @@ -84,26 +30,58 @@ const Home = ({ getCandidate }) => { return { id: val.id, value: val.tags, name: keys[index] }; }) ); - setTags(tempTags); + dispatch(tags(tempTags)); }); - }, []); + }, [dispatch]); - const shorthandArray = candidatesArray.slice(0, countArr); + useEffect(() => { + dispatch( + candidates( + profilesArr.map((profile) => { + let skillsName = ''; + let img; + let header; + + if (Number(profile.position_id) === 1) { + skillsName = 'Frontend'; + img = front; + header = 'Фронтенд'; + } else if (Number(profile.position_id) === 2) { + skillsName = 'Backend'; + img = back; + header = 'Бэкенд'; + } else if (Number(profile.position_id) === 3) { + skillsName = 'Marketer'; + img = design; + header = 'Дизайн'; + } + + return { + id: profile.id, + profileId: profile.position_id, + name: profile.fio, + skills: profile.skillValues, + skillsName, + img, + header, + }; + }) + ) + ); + }, [profilesArr, dispatch]); + + const shorthandArray = candidatesArr.slice(0, count); const loadMore = (count) => { - setCountArr((prev) => prev + count); - }; - - const handleBlockClick = (name) => { - setSelectedTab(name); + setCount((prev) => prev + count); }; return ( <> - handleBlockClick(name)} selected={selectedTab} tabs={tabs} tags={tags} /> + item.skillsName === selectedTab) : shorthandArray + selectedTab ? candidatesArr.filter((item) => item.skillsName === selectedTab) : shorthandArray } getCandidate={getCandidate} onLoadMore={loadMore} diff --git a/src/components/Outstaffing/Outstaffing.js b/src/components/Outstaffing/Outstaffing.js index 796406be..253a834d 100644 --- a/src/components/Outstaffing/Outstaffing.js +++ b/src/components/Outstaffing/Outstaffing.js @@ -1,12 +1,17 @@ import React, { useState } from 'react'; +import { useSelector } from 'react-redux'; import style from './Outstaffing.module.css'; import OutstaffingBlock from './OutstaffingBlock'; import TagSelect from '../Select/TagSelect'; +import { selectTags } from '../../redux/outstaffingSlice'; -const Outstaffing = ({ onhandleTabBar, selected, tabs, tags }) => { +const Outstaffing = ({ selected, candidatesArray }) => { const [selectedItems, setSelectedItems] = useState([]); + const tagsArr = useSelector(selectTags); + const handleBlockClick = (item) => { + console.log('item ', item); if (!selectedItems.find((el) => item === el.value)) { setSelectedItems([...selectedItems, { value: item, label: item }]); } @@ -37,28 +42,25 @@ const Outstaffing = ({ onhandleTabBar, selected, tabs, tags }) => {
item.name === 'Frontend')} - dataTags={tags.flat().filter((tag) => tag.name === 'skills_front')} + data={candidatesArray.find((item) => item.skillsName === 'Frontend')} + dataTags={tagsArr.flat().filter((tag) => tag.name === 'skills_front')} onClick={(item) => handleBlockClick(item)} - onTabBarClick={(name) => onhandleTabBar(name)} selected={selected === 'Frontend'} />
item.name === 'Backend')} - dataTags={tags.flat().filter((tag) => tag.name === 'skills_back')} + data={candidatesArray.find((item) => item.skillsName === 'Backend')} + dataTags={tagsArr.flat().filter((tag) => tag.name === 'skills_back')} onClick={(item) => handleBlockClick(item)} - onTabBarClick={(name) => onhandleTabBar(name)} selected={selected === 'Backend'} />
item.name === 'Design')} - dataTags={tags.flat().filter((tag) => tag.name === 'skills_design')} + data={candidatesArray.find((item) => item.skillsName === 'Marketer')} + dataTags={tagsArr.flat().filter((tag) => tag.name === 'skills_design')} onClick={(item) => handleBlockClick(item)} - onTabBarClick={(name) => onhandleTabBar(name)} selected={selected === 'Design'} />
@@ -66,7 +68,7 @@ const Outstaffing = ({ onhandleTabBar, selected, tabs, tags }) => {
{ - const { header, img, text, name } = data; +const OutstaffingBlock = ({ dataTags = [], data = {}, onClick, selected }) => { + const dispatch = useDispatch(); + + const { header, img, skillsName } = data; let classes; @@ -20,13 +24,13 @@ const OutstaffingBlock = ({ dataTags = [], data = {}, onClick, onTabBarClick, se
onTabBarClick(name)} + onClick={() => dispatch(selectedTab(skillsName))} >

{header}

img
-

{text}

+

# Популярный стек

{dataTags && (
    {dataTags.map((item) => ( diff --git a/src/components/Sidebar/Sidebar.js b/src/components/Sidebar/Sidebar.js index 74f03456..b162c118 100644 --- a/src/components/Sidebar/Sidebar.js +++ b/src/components/Sidebar/Sidebar.js @@ -5,14 +5,14 @@ import arrowLeft from '../../images/arrow_left.png'; import arrowRight from '../../images/arrow_right.png'; import style from './Sidebar.module.css'; -const Sidebar = () => { +const Sidebar = ({ getCandidateForCalendar, currentCandidateObj }) => { return (

    Опыт работы

    4+ лет

    - + getCandidateForCalendar(currentCandidateObj)}>

    Посмотреть ещё

    diff --git a/src/index.js b/src/index.js index f825e872..aea2241d 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,14 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { store } from './store/store'; +import { Provider } from 'react-redux'; import App from './App'; import './index.css'; -ReactDOM.render(, document.getElementById('root')); +ReactDOM.render( + + + , + document.getElementById('root') +); diff --git a/src/pages/CalendarPage.js b/src/pages/CalendarPage.js index 960b2d1a..b89bb7ea 100644 --- a/src/pages/CalendarPage.js +++ b/src/pages/CalendarPage.js @@ -1,8 +1,8 @@ import React from 'react'; import Calendar from '../components/Calendar/Calendar'; -const CalendarPage = () => { - return ; +const CalendarPage = ({ candidateForCalendar }) => { + return ; }; export default CalendarPage; diff --git a/src/pages/CandidatePage.js b/src/pages/CandidatePage.js index 0c36df02..737e50ea 100644 --- a/src/pages/CandidatePage.js +++ b/src/pages/CandidatePage.js @@ -2,6 +2,8 @@ import React from 'react'; import Candidate from '../components/Candidate/Candidate'; -const CandidatePage = ({ candidatesArr }) => ; +const CandidatePage = ({ candidatesArr, getCandidateForCalendar }) => ( + +); export default CandidatePage; diff --git a/src/redux/outstaffingSlice.js b/src/redux/outstaffingSlice.js new file mode 100644 index 00000000..35ebd8ac --- /dev/null +++ b/src/redux/outstaffingSlice.js @@ -0,0 +1,41 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { + tags: [], + profiles: [], + candidates: [], + selectedTab: '', + selectedItems: [], +}; + +export const outstaffingSlice = createSlice({ + name: 'outstaffing', + initialState, + reducers: { + tags: (state, action) => { + state.tags = action.payload; + }, + profiles: (state, action) => { + state.profiles = action.payload; + }, + candidates: (state, action) => { + state.candidates = action.payload; + }, + selectedTab: (state, action) => { + state.selectedTab = action.payload; + }, + selectedItem: (state, action) => { + state.selectedItem = action.payload; + }, + }, +}); + +export const { tags, profiles, candidates, selectedTab, selectedItems } = outstaffingSlice.actions; + +export const selectProfiles = (state) => state.outstaffing.profiles; +export const selectTags = (state) => state.outstaffing.tags; +export const selectCandidates = (state) => state.outstaffing.candidates; +export const selectTab = (state) => state.outstaffing.selectedTab; +export const selectItems = (state) => state.outstaffing.selectedItems; + +export default outstaffingSlice.reducer; diff --git a/src/store/store.js b/src/store/store.js new file mode 100644 index 00000000..13d57d76 --- /dev/null +++ b/src/store/store.js @@ -0,0 +1,8 @@ +import { configureStore } from '@reduxjs/toolkit'; +import outstaffingReducer from '../redux/outstaffingSlice'; + +export const store = configureStore({ + reducer: { + outstaffing: outstaffingReducer, + }, +});