Переписываю спорные решения

This commit is contained in:
Дмитрий Савенко 2023-01-16 15:24:08 +03:00
parent adc50b62f9
commit 6f6ab5c4ce
17 changed files with 619 additions and 644 deletions

View File

@ -4,7 +4,6 @@ import {useSelector, useDispatch} from 'react-redux'
import { import {
currentCandidate, currentCandidate,
selectCurrentCandidate, selectCurrentCandidate,
auth
} from '../../redux/outstaffingSlice' } from '../../redux/outstaffingSlice'
import {getRole} from '../../redux/roleSlice' import {getRole} from '../../redux/roleSlice'
import {useState} from 'react' import {useState} from 'react'
@ -16,10 +15,11 @@ import SkillSection from '../SkillSection/SkillSection'
import front from '../../images/front_end.png' import front from '../../images/front_end.png'
import back from '../../images/back_end.png' import back from '../../images/back_end.png'
import design from '../../images/design.png' import design from '../../images/design.png'
import {fetchGet} from '../../server/server'
import {Footer} from '../Footer/Footer' import {Footer} from '../Footer/Footer'
import './candidate.scss' import './candidate.scss'
import {useRequest} from "../../hooks/useRequest";
const Candidate = () => { const Candidate = () => {
const {id: candidateId} = useParams(); const {id: candidateId} = useParams();
@ -28,16 +28,15 @@ const Candidate = () => {
const role = useSelector(getRole); const role = useSelector(getRole);
const [activeSnippet, setActiveSnippet] = useState(true); const [activeSnippet, setActiveSnippet] = useState(true);
const {apiRequest} = useRequest();
useEffect(() => { useEffect(() => {
window.scrollTo(0, 0) window.scrollTo(0, 0)
}, []); }, []);
useEffect(() => { useEffect(() => {
fetchGet({ apiRequest(`/profile/${candidateId}`,{
link: `${process.env.REACT_APP_API_URL}/api/profile/${candidateId}`,
params: Number(candidateId), params: Number(candidateId),
role,
logout: () => dispatch(auth(false))
}).then((el) => dispatch(currentCandidate(el))) }).then((el) => dispatch(currentCandidate(el)))
}, [dispatch, candidateId]); }, [dispatch, candidateId]);

View File

@ -12,33 +12,27 @@ import {getRole} from '../../redux/roleSlice'
import {useRequest} from "../../hooks/useRequest"; import {useRequest} from "../../hooks/useRequest";
const Home = () => { const Home = () => {
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 {apiRequest} = useRequest(); const {apiRequest} = useRequest();
useEffect(() => { useEffect(() => {
setIsLoadingMore(true); setIsLoadingMore(true);
apiRequest('/profile',{ apiRequest('/profile', {
//Корс блокирует все фильтры в гет параметрах params: {limit: 1000},
params: {"offset": 1000},
role,
// logout: () => dispatch(auth(false))
}).then((profileArr) => { }).then((profileArr) => {
dispatch(profiles(profileArr)); dispatch(profiles(profileArr));
setIsLoadingMore(false) setIsLoadingMore(false)
}); });
apiRequest('/skills/skills-on-main-page',{ apiRequest('/skills/skills-on-main-page', {
role,
// logout: () => dispatch(auth(false))
}).then((skills) => { }).then((skills) => {
if (!skills) { if (!skills) {
return [] return []

View File

@ -4,7 +4,7 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
position: relative;
&:hover { &:hover {
path { path {
fill: #6aaf5c; fill: #6aaf5c;

View File

@ -1,43 +1,38 @@
import React from 'react' import React from 'react'
import OutsideClickHandler from 'react-outside-click-handler' import OutsideClickHandler from 'react-outside-click-handler'
import { useDispatch, useSelector } from 'react-redux' import {useDispatch, useSelector} from 'react-redux'
import { import {
selectItems, selectItems,
selectedItems, selectedItems,
filteredCandidates, filteredCandidates,
auth
} from '../../redux/outstaffingSlice' } from '../../redux/outstaffingSlice'
import { fetchGet } from '../../server/server' import {useRequest} from "../../hooks/useRequest";
import { getRole } from '../../redux/roleSlice'
import './outstaffingBlock.scss' import './outstaffingBlock.scss'
const handlePositionClick = ({
dispatch, const handlePositionClick = (
positionId, {
isSelected, dispatch,
onSelect, positionId,
role isSelected,
}) => { onSelect,
apiRequest
}) => {
if (isSelected) { if (isSelected) {
fetchGet({ apiRequest('/profile', {
link: `${process.env.REACT_APP_API_URL}/api/profile?limit=`, params: {limit: 1000},
params: 4,
role,
logout: () => dispatch(auth(false))
}).then((profileArr) => { }).then((profileArr) => {
dispatch(filteredCandidates(profileArr)); dispatch(filteredCandidates(profileArr));
dispatch(selectedItems([])); dispatch(selectedItems([]));
onSelect(positionId) onSelect(positionId)
}) })
} else { } else {
fetchGet({ apiRequest('/profile', {
link: `${process.env.REACT_APP_API_URL}/api/profile?position_id=`, params: {
params: positionId, limit: '1000',
role, position_id: positionId},
logout: () => dispatch(auth(false))
}).then((el) => { }).then((el) => {
dispatch(filteredCandidates(el)); dispatch(filteredCandidates(el));
dispatch(selectedItems([])); dispatch(selectedItems([]));
@ -46,25 +41,27 @@ const handlePositionClick = ({
} }
}; };
const OutstaffingBlock = ({ const OutstaffingBlock = (
dataTags = [], {
selected, dataTags = [],
img, selected,
header, img,
positionId, header,
isSelected, positionId,
onSelect isSelected,
}) => { onSelect
}) => {
const role = useSelector(getRole);
const dispatch = useDispatch(); const dispatch = useDispatch();
const itemsArr = useSelector(selectItems); const itemsArr = useSelector(selectItems);
const {apiRequest} = useRequest();
const handleBlockClick = (item, id) => { const handleBlockClick = (item, id) => {
if (!itemsArr.find((el) => item === el.value)) { if (!itemsArr.find((el) => item === el.value)) {
dispatch(selectedItems([...itemsArr, { id, value: item, label: item }])) dispatch(selectedItems([...itemsArr, {id, value: item, label: item}]))
} }
}; };
@ -81,56 +78,56 @@ const OutstaffingBlock = ({
}); });
return ( return (
<OutsideClickHandler <OutsideClickHandler
onOutsideClick={() => { onOutsideClick={() => {
isSelected && onSelect(null) isSelected && onSelect(null)
}} }}
>
<div
className={`outstaffing-block${
isSelected ? ' outstaffing-block__selected' : ''
}`}
> >
<div <div
className={`outstaffing-block__img ${ className={`outstaffing-block${
selected ? ' outstaffing-block__border' : '' isSelected ? ' outstaffing-block__selected' : ''
}`} }`}
onClick={() =>
handlePositionClick({
dispatch,
positionId,
isSelected,
onSelect,
role
})
}
> >
<h3>{header}</h3> <div
<img className={classes} src={img} alt='img' /> className={`outstaffing-block__img ${
selected ? ' outstaffing-block__border' : ''
}`}
onClick={() =>
handlePositionClick({
dispatch,
positionId,
isSelected,
onSelect,
apiRequest
})
}
>
<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> </div>
<div </OutsideClickHandler>
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>
) )
}; };

View File

@ -2,7 +2,6 @@ import React, { useEffect, useState } from 'react'
import {useDispatch, useSelector} from 'react-redux' import {useDispatch, useSelector} from 'react-redux'
import { getProfileInfo } from '../../redux/outstaffingSlice' import { getProfileInfo } from '../../redux/outstaffingSlice'
import { setReportDate } from '../../redux/reportSlice'; import { setReportDate } from '../../redux/reportSlice';
import {fetchGet} from "../../server/server";
import arrow from "../../images/right-arrow.png"; import arrow from "../../images/right-arrow.png";
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import moment from "moment"; import moment from "moment";
@ -12,40 +11,42 @@ import {ProfileCalendarComponent} from "./ProfileCalendarComponent";
import { Footer } from '../Footer/Footer' import { Footer } from '../Footer/Footer'
import './profileCalendar.scss' import './profileCalendar.scss'
import {useRequest} from "../../hooks/useRequest";
export const ProfileCalendar = () => { export const ProfileCalendar = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const profileInfo = useSelector(getProfileInfo) const profileInfo = useSelector(getProfileInfo);
const [month, setMonth] = useState('') const [month, setMonth] = useState('');
const [reports, setReports] = useState([]) const [reports, setReports] = useState([]);
const [totalHours, setTotalHours] = useState(0) const [totalHours, setTotalHours] = useState(0);
const [value, setValue] = useState(moment()) const [value, setValue] = useState(moment());
const [requestDates, setRequestDates] = useState('') const [requestDates, setRequestDates] = useState('');
const {apiRequest} = useRequest();
useEffect(() => { useEffect(() => {
setRequestDates(getReports(value)) setRequestDates(getReports(value))
}) });
useEffect(async () => { useEffect(async () => {
if (!requestDates) { if (!requestDates) {
return return
} }
const response = await fetchGet({ apiRequest(`/reports/reports-by-date?${requestDates}&user_id=${localStorage.getItem('id')}`)
link: `${process.env.REACT_APP_API_URL}/api/reports/reports-by-date?${requestDates}&user_id=${localStorage.getItem('id')}`, .then((reports) => {
}).then((reports) => { let spendTime = 0;
let spendTime = 0 reports.map((report) => {
reports.map((report)=> { if (report.spendTime) {
if (report.spendTime) { spendTime += Number(report.spendTime)
spendTime += Number(report.spendTime) }
} });
setTotalHours(spendTime);
setReports(reports)
}) })
setTotalHours(spendTime) }, [requestDates]);
setReports(reports)
})
},[requestDates])
useEffect(() => { useEffect(() => {
setMonth(currentMonth) setMonth(currentMonth)
}, [month]) }, [month]);
return ( return (
<section className='calendar'> <section className='calendar'>

View File

@ -28,7 +28,6 @@ const getCreatedDate = (day) => {
const ReportForm = () => { const ReportForm = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const navigate = useNavigate();
const reportDate = useSelector(getReportDate); const reportDate = useSelector(getReportDate);
const role = useSelector(getRole); const role = useSelector(getRole);

View File

@ -1,41 +1,39 @@
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 {useRequest} from "../../hooks/useRequest";
import { import {
selectedItems, selectedItems,
selectItems, selectItems,
selectTags, selectTags,
filteredCandidates, filteredCandidates,
setPositionId, setPositionId
auth
} from '../../redux/outstaffingSlice' } from '../../redux/outstaffingSlice'
import { fetchGet } from '../../server/server'
import { getRole } from '../../redux/roleSlice' import style from './TagSelect.module.css'
const TagSelect = () => { const TagSelect = () => {
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 {apiRequest} = useRequest();
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();
const params = filterItemsId ? {skill: filterItemsId} : '';
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/profile?skills=`, apiRequest('/profile', {
params: filterItemsId, params: {...params, limit: 1000},
role,
logout: () => dispatch(auth(false))
}).then((el) => { }).then((el) => {
dispatch(filteredCandidates(el)); dispatch(filteredCandidates(el));
setSearchLoading(false) setSearchLoading(false)
@ -45,44 +43,44 @@ const TagSelect = () => {
}; };
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 className={style.search__title}>
Найти специалиста по навыкам Найти специалиста по навыкам
</h2> </h2>
<div className={style.search__box}> <div className={style.search__box}>
<Select <Select
value={itemsArr} value={itemsArr}
onChange={(value) => dispatch(selectedItems(value))} onChange={(value) => {console.log(value) ;return dispatch(selectedItems(value))}}
isMulti isMulti
name='tags' name='tags'
className={style.select} className={style.select}
classNamePrefix={style.select} classNamePrefix={style.select}
options={ options={
tagsArr && tagsArr &&
tagsArr.flat().map((item) => { tagsArr.flat().map((item) => {
return { return {
id: item.id, id: item.id,
value: item.value, value: item.value,
label: item.value label: item.value
}
})
} }
}) />
} <button
/> onClick={() => handleSubmit({dispatch, setSearchLoading})}
<button type='submit'
onClick={() => handleSubmit({ dispatch, setSearchLoading })} className={style.search__submit}
type='submit' >
className={style.search__submit} {searchLoading ? <Loader width={30} height={30}/> : 'Поиск'}
> </button>
{searchLoading ? <Loader width={30} height={30} /> : 'Поиск'} </div>
</button>
</div> </div>
</div> </div>
</div> </section>
</section> </>
</>
) )
}; };

View File

@ -1,102 +1,101 @@
import React, { useEffect, useState } from 'react'; import React, {useEffect, useState} from 'react';
import { ContentTitle } from "../ContentTitle/ContentTitle" import {Link} from "react-router-dom"
import { ContentButton } from "../ContentButton/ContentButton" import {ContentTitle} from "../ContentTitle/ContentTitle"
import { BookkeepingFormField } from "../BookkeepingFormField/BookkeepingFormField" import {ContentButton} from "../ContentButton/ContentButton"
import { BookkepingSelect } from '../BookkepingSelect/BookkepingSelect'; import {BookkeepingFormField} from "../BookkeepingFormField/BookkeepingFormField"
import { BookkepingInput } from '../BookkepingInput/BookkepingInput'; import {BookkepingSelect} from '../BookkepingSelect/BookkepingSelect';
import { fetchGet } from '../../../../server/server' import {BookkepingInput} from '../BookkepingInput/BookkepingInput';
import { Link } from "react-router-dom"
import {useRequest} from "../../../../hooks/useRequest";
import "./actContent.css" import "./actContent.css"
export const ActContent = ()=> { export const ActContent = () => {
const [templates, setTemplates] = useState([]) const [templates, setTemplates] = useState([]);
const [selectedTemplate, setSelectedTemplate] = useState() const [selectedTemplate, setSelectedTemplate] = useState();
const [templatedFields, setTemplatedFields] = useState([]) const [templatedFields, setTemplatedFields] = useState([]);
useEffect(() => { const {apiRequest} = useRequest();
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/template/get-template-list`,
}).then((res) => {
setTemplates(res)
})
}, [])
useEffect(() => { useEffect(() => {
if (selectedTemplate === undefined) { apiRequest('/template/get-template-list')
return .then(res => setTemplates(res))
} }, []);
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/template/get-template-fields?template_id=${selectedTemplate}`, useEffect(() => {
}) if (selectedTemplate === undefined) {
.then((res) => { return
setTemplatedFields(res[0].templateDocumentFields) }
}) apiRequest(`/template/get-template-fields?template_id=${selectedTemplate}`)
}, [selectedTemplate]) .then(res => setTemplatedFields(res[0].templateDocumentFields))
}, [selectedTemplate]);
return (
<div>
<div className="content__info">
<ContentTitle title="Создание акта" description="# Описание"/>
<div className="content__info-main">
<form className='contract'>
<div className="contract__create">
<div className="contract__title">Создание акта </div>
<input type="text" className="contract__number" placeholder="#"/>
<span>от</span>
<input type="date" className="contract__date"/>
</div>
<BookkeepingFormField
title="Шаблон документа"
Component={BookkepingSelect}
innerComponentProps={{
onSelect: setSelectedTemplate,
textField: "title",
options: templates,
defaultIndexSelected: 0,
}}
action={{
text: "Добавить свой шаблон",
method: () => {
}
}}
/>
{templatedFields.map((field, index) =>
<BookkeepingFormField
title={field.field.title} key={index}
Component={BookkepingInput}
innerComponentProps={{
placeholder: "Введите данные",
}}
/>
)}
return( <div className="content__btn-list">
<div> <ContentButton styles={{
<div className="content__info"> width: "290px",
<ContentTitle title="Создание акта" description="# Описание" /> height: "75px",
<div className="content__info-main"> boxShadow: "6px 5px 20px rgba(182, 75, 62, 0.21)",
<form className='contract'> borderRadius: "38px",
<div className="contract__create"> backgroundColor: "#b64b3e",
<div className="contract__title">Создание акта </div> border: "none",
<input type="text" className="contract__number" placeholder="#" /> color: "#ffffff",
<span>от</span> }}>Сохранить</ContentButton>
<input type="date" className="contract__date" /> <Link to="/documents" className="link-act-button">
</div> <div className='act-Button'>
<ContentButton styles={{
<BookkeepingFormField title="Шаблон документа" color: "#282828",
Component={BookkepingSelect} marginLeft: "40px",
innerComponentProps={{ background: "none",
onSelect: setSelectedTemplate, border: "none"
textField: "title", }}>Отменить</ContentButton>
options: templates, </div>
defaultIndexSelected: 0, </Link>
}} </div>
action={{ </form>
text: "Добавить свой шаблон", </div>
method: () => {}
}}
/>
{templatedFields.map((field, index ) =>
<BookkeepingFormField title={field.field.title} key={index}
Component={BookkepingInput}
innerComponentProps={{
placeholder: "Введите данные",
}}
/>
)}
<div className="content__btn-list">
<ContentButton styles={{ width: "290px",
height: "75px",
boxShadow: "6px 5px 20px rgba(182, 75, 62, 0.21)",
borderRadius: "38px",
backgroundColor: "#b64b3e",
border: "none",
color: "#ffffff",
}}>Сохранить</ContentButton>
<Link to="/documents" className="link-act-button">
<div className='act-Button'>
<ContentButton styles={{color: "#282828",
marginLeft: "40px",
background: "none",
border: "none"
}}>Отменить</ContentButton>
</div>
</Link>
</div>
</form>
</div>
</div> </div>
</div> </div>
) )
} };

View File

@ -1,97 +1,98 @@
import React, { useEffect, useState } from "react"; import React, {useEffect, useState} from "react";
import { ContentTitle } from "../ContentTitle/ContentTitle" import {ContentTitle} from "../ContentTitle/ContentTitle"
import { ContentButton } from "../ContentButton/ContentButton" import {ContentButton} from "../ContentButton/ContentButton"
import { BookkeepingFormField } from "../BookkeepingFormField/BookkeepingFormField" import {BookkeepingFormField} from "../BookkeepingFormField/BookkeepingFormField"
import { BookkepingSelect } from '../BookkepingSelect/BookkepingSelect'; import {BookkepingSelect} from '../BookkepingSelect/BookkepingSelect';
import { BookkepingInput } from '../BookkepingInput/BookkepingInput'; import {BookkepingInput} from '../BookkepingInput/BookkepingInput';
import { fetchGet } from '../../../../server/server' import {Link} from "react-router-dom"
import { Link } from "react-router-dom" import {useRequest} from "../../../../hooks/useRequest";
export const ContractContent = () => { export const ContractContent = () => {
const [templates, setTemplates] = useState([]) const [templates, setTemplates] = useState([]);
const [selectedTemplate, setSelectedTemplate] = useState() const [selectedTemplate, setSelectedTemplate] = useState();
const [templatedFields, setTemplatedFields] = useState([]) const [templatedFields, setTemplatedFields] = useState([]);
useEffect(() => { const {apiRequest} = useRequest();
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/document/get-document-list`,
}).then((res) => {
setTemplates(res)
})
}, [])
useEffect(() => { useEffect(() => {
if (selectedTemplate === undefined) { apiRequest(`/document/get-document-list`)
return .then(res => setTemplates(res))
} }, []);
fetchGet({
link: `${process.env.REACT_APP_API_URL}/api/document/get-document?document_id=${selectedTemplate}`,
})
.then((res) => {
setTemplatedFields(res[0].templateDocumentFields)
})
}, [selectedTemplate])
return ( useEffect(() => {
<div> if (selectedTemplate === undefined) {
<div className="content__info"> return
<ContentTitle title="Создание договора" description="# Описание" /> }
<div className="content__info-main"> apiRequest(`/document/get-document?document_id=${selectedTemplate}`)
.then(res => setTemplatedFields(res[0].templateDocumentFields)
)
}, [selectedTemplate]);
return (
<div>
<div className="content__info">
<ContentTitle title="Создание договора" description="# Описание"/>
<div className="content__info-main">
<form className='contract'> <form className='contract'>
<div className="contract__create"> <div className="contract__create">
<div className="contract__title">Создание договора </div> <div className="contract__title">Создание договора </div>
<input type="text" className="contract__number" placeholder="#" /> <input type="text" className="contract__number" placeholder="#"/>
<span>от</span> <span>от</span>
<input type="date" className="contract__date" /> <input type="date" className="contract__date"/>
</div> </div>
<BookkeepingFormField title="Шаблон документа" <BookkeepingFormField
Component={BookkepingSelect} title="Шаблон документа"
innerComponentProps={{ Component={BookkepingSelect}
onSelect: setSelectedTemplate, innerComponentProps={{
textField: "title", onSelect: setSelectedTemplate,
options: templates, textField: "title",
defaultIndexSelected: 0, options: templates,
}} defaultIndexSelected: 0,
action={{ }}
text: "Добавить свой шаблон", action={{
method: () => {} text: "Добавить свой шаблон",
}} method: () => {
/> }
}}
/>
{templatedFields.map((field, index ) => {templatedFields.map((field, index) =>
<BookkeepingFormField title={field.field.title} key={index} <BookkeepingFormField
Component={BookkepingInput} title={field.field.title} key={index}
innerComponentProps={{ Component={BookkepingInput}
placeholder: "Введите данные", innerComponentProps={{
}} placeholder: "Введите данные",
/> }}
)} />
)}
<div className="content__btn-list"> <div className="content__btn-list">
<ContentButton styles={{ width: "290px", <ContentButton styles={{
height: "75px", width: "290px",
boxShadow: "6px 5px 20px rgba(182, 75, 62, 0.21)", height: "75px",
borderRadius: "38px", boxShadow: "6px 5px 20px rgba(182, 75, 62, 0.21)",
backgroundColor: "#b64b3e", borderRadius: "38px",
border: "none", backgroundColor: "#b64b3e",
color: "#ffffff", border: "none",
}}>Сохранить</ContentButton> color: "#ffffff",
<Link to="/documents" className="link-act-button"> }}>Сохранить</ContentButton>
<div className='act-Button'> <Link to="/documents" className="link-act-button">
<ContentButton styles={{color: "#282828", <div className='act-Button'>
marginLeft: "40px", <ContentButton styles={{
background: "none", color: "#282828",
border: "none" marginLeft: "40px",
}}>Отменить</ContentButton> background: "none",
</div> border: "none"
</Link> }}>Отменить</ContentButton>
</div> </div>
</form> </Link>
</div> </div>
</form>
</div>
</div> </div>
</div> </div>
) )
} };

View File

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

View File

@ -5,23 +5,26 @@ import './quiz.scss'
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import {useSelector} from "react-redux"; import {useSelector} from "react-redux";
import {selectedTest} from "../../../redux/quizSlice"; import {selectedTest} from "../../../redux/quizSlice";
import {fetchGet} from "../../../server/server";
import {useRequest} from "../../../hooks/useRequest";
export const Instruction = () => { export const Instruction = () => {
const [countQuestions, setCountQuestions] = useState(null) const [countQuestions, setCountQuestions] = useState(null);
const test = useSelector(selectedTest) const test = useSelector(selectedTest);
useEffect(async () => { const {apiRequest} = useRequest();
const response = await fetchGet({ useEffect( () => {
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)
}, []) apiRequest('/user-questionnaire/get-question-number', {
params: {user_questionnaire_uuid: test.uuid},
}
).then((res)=> setCountQuestions(res.question_number))
}, []);
return ( return (
<div className="instruction"> <div className="instruction">
@ -49,5 +52,5 @@ export const Instruction = () => {
</div> </div>
</div> </div>
) )
} };

View File

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

View File

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

View File

@ -1,4 +1,3 @@
import React from "react";
import {useDispatch, useSelector} from "react-redux"; import {useDispatch, useSelector} from "react-redux";
import {getRole} from "../redux/roleSlice"; import {getRole} from "../redux/roleSlice";
import {useNavigate} from "react-router-dom"; import {useNavigate} from "react-router-dom";
@ -12,7 +11,6 @@ export const useLogout = () => {
const logout = () => { const logout = () => {
localStorage.clear(); localStorage.clear();
dispatch(auth(false)); dispatch(auth(false));
console.log('logout')
navigate(userRole === 'ROLE_DEV' ? '/authdev' : '/auth') navigate(userRole === 'ROLE_DEV' ? '/authdev' : '/auth')
}; };

View File

@ -1,7 +1,5 @@
import React from "react";
import axios from 'axios'; import axios from 'axios';
import {getToken, urlHasParams} from "../helper"; import {getToken, urlHasParams} from "../helper";
import {useNavigate} from "react-router";
import {useLogout} from "./useLogout"; import {useLogout} from "./useLogout";
@ -38,7 +36,6 @@ export const useRequest = () => {
}) })
.then(response => new Promise(resolve => { .then(response => new Promise(resolve => {
if (response.data.redirect || response.status === 401) { if (response.data.redirect || response.status === 401) {
console.log(response, 'LOGUTATAT')
logout() logout()
} }
return resolve(response) return resolve(response)

View File

@ -4,13 +4,11 @@ import {useParams, useNavigate} from 'react-router-dom'
import { import {
currentCandidate, currentCandidate,
selectCurrentCandidate, selectCurrentCandidate,
auth
} from '../redux/outstaffingSlice' } from '../redux/outstaffingSlice'
import SVG from 'react-inlinesvg' import SVG from 'react-inlinesvg'
import {WithLogout} from '../hoc/withLogout' import {WithLogout} from '../hoc/withLogout'
import Form from '../components/Form/Form' import Form from '../components/Form/Form'
import {LEVELS, SKILLS} from '../components/constants/constants' import {LEVELS, SKILLS} from '../components/constants/constants'
import {fetchGet} from '../server/server'
import {Footer} from '../components/Footer/Footer' import {Footer} from '../components/Footer/Footer'
import arrow from '../images/right-arrow.png' import arrow from '../images/right-arrow.png'
@ -18,7 +16,7 @@ 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 {useRequest} from "../hooks/useRequest";
const FormPage = () => { const FormPage = () => {
@ -26,20 +24,19 @@ const FormPage = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const dispatch = useDispatch(); const dispatch = useDispatch();
const candidate = useSelector(selectCurrentCandidate); const candidate = useSelector(selectCurrentCandidate);
const role = useSelector(getRole);
const {apiRequest} = useRequest();
const goBack = () => { const goBack = () => {
navigate(-1) navigate(-1)
}; };
if (!candidate.id) { if (!candidate.id) {
fetchGet({ apiRequest('/api/profile', {
link: `${process.env.REACT_APP_API_URL}/api/profile/`, params: Number(params.id)
params: Number(params.id), })
navigate, .then((el) => dispatch(currentCandidate(el)))
role,
logout: () => dispatch(auth(false))
}).then((el) => dispatch(currentCandidate(el)))
} }
return ( return (

View File

@ -1,104 +1,101 @@
import React, {useEffect, useState} from 'react'; import React, {useEffect, useState} from 'react';
import {useSelector} from "react-redux";
import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader"; import {ProfileHeader} from "../../components/ProfileHeader/ProfileHeader";
import {getProfileInfo} from "../../redux/outstaffingSlice"; import {getProfileInfo} from "../../redux/outstaffingSlice";
import {useSelector} from "react-redux";
import {transformHtml} from "../../helper";
import {Footer} from '../../components/Footer/Footer' import {Footer} from '../../components/Footer/Footer'
import {transformHtml} from "../../helper";
import {useRequest} from "../../hooks/useRequest";
import arrow from "../../images/right-arrow.png"; import arrow from "../../images/right-arrow.png";
import rightArrow from "../../images/arrowRight.png" import rightArrow from "../../images/arrowRight.png"
import gitImgItem from "../../images/gitItemImg.png" import gitImgItem from "../../images/gitItemImg.png"
import {fetchGet} from "../../server/server";
import './summary.scss' import './summary.scss'
export const Summary = () => { export const Summary = () => {
const profileInfo = useSelector(getProfileInfo); const profileInfo = useSelector(getProfileInfo);
const [openGit, setOpenGit] = useState(false); const [openGit, setOpenGit] = useState(false);
const [gitInfo, setGitInfo] = useState([]); const [gitInfo, setGitInfo] = useState([]);
useEffect(() => {
fetchGet({ const {apiRequest} = useRequest();
link: `${process.env.REACT_APP_API_URL}/api/profile/portfolio-projects?card_id=${localStorage.getItem('cardId')}`, useEffect(() => {
}).then((responseGit) => { apiRequest(`/profile/portfolio-projects?card_id=${localStorage.getItem('cardId')}`)
setGitInfo(responseGit) .then(responseGit => setGitInfo(responseGit))
}) }, [apiRequest]);
}, []); return (
return( <div className='summary'>
<div className='summary'> <ProfileHeader/>
<ProfileHeader/> <div className='container'>
<div className='container'> <div className='summary__content'>
<div className='summary__content'> <h2 className='summary__title'>Ваше резюме {openGit && <span>- Git</span>}</h2>
<h2 className='summary__title'>Ваше резюме {openGit && <span>- Git</span>}</h2> {openGit && <div className='summary__back' onClick={() => setOpenGit(false)}>
{openGit && <div className='summary__back' onClick={() => setOpenGit(false)}> <img src={arrow} alt='arrow'/>
<img src={arrow} alt='arrow'/> <p>Вернуться</p>
<p>Вернуться</p> </div>}
</div>} <div className='summary__info'>
<div className='summary__info'> <div className='summary__person'>
<div className='summary__person'> <img src={profileInfo.photo} className='summary__avatar' alt='avatar'/>
<img src={profileInfo.photo} className='summary__avatar' alt='avatar'/> <p className='summary__name'>{profileInfo.fio} {profileInfo.specification}</p>
<p className='summary__name'>{profileInfo.fio} {profileInfo.specification}</p> </div>
</div> {!openGit &&
{!openGit && <button className='summary__git' onClick={() => setOpenGit(true)}>Git</button>
<button className='summary__git' onClick={()=> setOpenGit(true)}>Git</button> }
}
</div>
</div>
{!openGit &&
<div className='summary__skills skills__section'>
<div className='summary__sections__head'>
<h3>Основной стек</h3>
<button>Редактировать раздел</button>
</div>
<div className='skills__section__items'>
<div className='skills__section__items__wrapper'>
{profileInfo.skillValues && profileInfo.skillValues.map((skill) => {
return <span key={skill.id} className='skill_item'>{skill.skill.name}</span>
})}
</div>
</div>
</div>
}
{profileInfo.vc_text && !openGit &&
<div className='summary__experience' dangerouslySetInnerHTML={transformHtml(profileInfo.vc_text)}>
</div>
}
{openGit &&
<div className='summary__sectionGit'>
<div className='summary__sections__head'>
<h3>Страница портфолио кода разработчика</h3>
<button>Редактировать раздел</button>
</div>
<div className='summary__sectionGitItems'>
{gitInfo.length && gitInfo.map((itemGit) => {
return <div key={itemGit.id} className='summary__sectionGitItem gitItem'>
<div className='gitItem__info'>
<div className='gitItem__info__about'>
<img src={gitImgItem} alt='gitImg' />
<div className='gitItem__info__name'>
<h4>{itemGit.title}</h4>
<p>{itemGit.description}</p>
</div>
</div>
<div className='gitItem__info__specification'>
<span className='gitItem__lineSkill'/>
<p>{itemGit.main_stack}</p>
</div>
</div>
<a className='gitItem__link' href={itemGit.link} target="_blank" rel="noreferrer">
<img src={rightArrow} alt='arrowRight' />
</a>
</div>
})
}
</div>
</div>
}
</div> </div>
<Footer/> </div>
{!openGit &&
<div className='summary__skills skills__section'>
<div className='summary__sections__head'>
<h3>Основной стек</h3>
<button>Редактировать раздел</button>
</div>
<div className='skills__section__items'>
<div className='skills__section__items__wrapper'>
{profileInfo.skillValues && profileInfo.skillValues.map((skill) =>
<span key={skill.id} className='skill_item'>{skill.skill.name}</span>
)}
</div>
</div>
</div>
}
{profileInfo.vc_text && !openGit &&
<div className='summary__experience' dangerouslySetInnerHTML={transformHtml(profileInfo.vc_text)}>
</div>
}
{openGit &&
<div className='summary__sectionGit'>
<div className='summary__sections__head'>
<h3>Страница портфолио кода разработчика</h3>
<button>Редактировать раздел</button>
</div>
<div className='summary__sectionGitItems'>
{gitInfo.length && gitInfo.map((itemGit) => {
return <div key={itemGit.id} className='summary__sectionGitItem gitItem'>
<div className='gitItem__info'>
<div className='gitItem__info__about'>
<img src={gitImgItem} alt='gitImg'/>
<div className='gitItem__info__name'>
<h4>{itemGit.title}</h4>
<p>{itemGit.description}</p>
</div>
</div>
<div className='gitItem__info__specification'>
<span className='gitItem__lineSkill'/>
<p>{itemGit.main_stack}</p>
</div>
</div>
<a className='gitItem__link' href={itemGit.link} target="_blank" rel="noreferrer">
<img src={rightArrow} alt='arrowRight'/>
</a>
</div>
})
}
</div>
</div>
}
</div> </div>
) <Footer/>
</div>
)
}; };