From 23a1ff18bc68b45f26d92c2e671f8453b829489d Mon Sep 17 00:00:00 2001 From: Nikita Mashchenko Date: Tue, 4 Jul 2023 22:57:16 -0500 Subject: [PATCH 1/2] Finished logic for updating --- client/src/assets/LanguageLogo/CSS.js | 12 + client/src/assets/LanguageLogo/Kotlin.js | 12 + client/src/assets/LanguageLogo/Lua.js | 20 ++ client/src/assets/LanguageLogo/R.js | 18 ++ client/src/assets/LanguageLogo/Rust.js | 14 + client/src/assets/LanguageLogo/TS.js | 16 + .../src/components/NavBar/Profile/Profile.jsx | 2 - client/src/components/Profile/Profile.jsx | 12 +- .../EditingComponentEducation.jsx | 299 ------------------ .../EditingComponentEducation.jsx | 152 +++++++++ .../States/Adding.jsx | 104 ++++++ .../States/Editing.jsx | 120 +++++++ .../EditingComponentEducation/States/Main.jsx | 99 ++++++ .../EditingComponents/EditingComponentJob.jsx | 279 ---------------- .../EditingComponentJob.jsx | 149 +++++++++ .../EditingComponentJob/States/Adding.jsx | 83 +++++ .../EditingComponentJob/States/Editing.jsx | 103 ++++++ .../EditingComponentJob/States/Main.jsx | 92 ++++++ .../EditingComponentProfile.jsx | 13 +- .../components/ResumeInfo/ResumeInfo.jsx | 4 +- .../UserEducationForm/UserEducationForm.jsx | 2 +- .../UserJobForm/UserJobForm.jsx | 6 +- client/src/constants/concentrations.js | 48 ++- client/src/constants/frameworkColors.js | 64 ++-- client/src/constants/frameworks.js | 18 +- client/src/constants/programmingLanguages.js | 22 +- client/src/http/index.js | 2 - client/src/schemas/index.js | 137 ++++++++ .../CheckboxWithLabel/CheckboxWithLabel.js | 0 .../CheckboxWithLabel.styles.js | 0 .../Formik/CustomInput/CustomInput.jsx | 4 + client/src/utils/convertStringToDate.js | 14 +- server/src/maintenance/maintenance.service.ts | 30 +- 33 files changed, 1315 insertions(+), 635 deletions(-) create mode 100644 client/src/assets/LanguageLogo/CSS.js create mode 100644 client/src/assets/LanguageLogo/Kotlin.js create mode 100644 client/src/assets/LanguageLogo/Lua.js create mode 100644 client/src/assets/LanguageLogo/R.js create mode 100644 client/src/assets/LanguageLogo/Rust.js create mode 100644 client/src/assets/LanguageLogo/TS.js delete mode 100644 client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation.jsx create mode 100644 client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/EditingComponentEducation.jsx create mode 100644 client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/States/Adding.jsx create mode 100644 client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/States/Editing.jsx create mode 100644 client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/States/Main.jsx delete mode 100644 client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob.jsx create mode 100644 client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/EditingComponentJob.jsx create mode 100644 client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Adding.jsx create mode 100644 client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Editing.jsx create mode 100644 client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Main.jsx rename client/src/{components/RegistrationPipeline => shared}/components/CheckboxWithLabel/CheckboxWithLabel.js (100%) rename client/src/{components/RegistrationPipeline => shared}/components/CheckboxWithLabel/CheckboxWithLabel.styles.js (100%) diff --git a/client/src/assets/LanguageLogo/CSS.js b/client/src/assets/LanguageLogo/CSS.js new file mode 100644 index 000000000..1c3d992d9 --- /dev/null +++ b/client/src/assets/LanguageLogo/CSS.js @@ -0,0 +1,12 @@ +function CSS() { + return ( + + + + ) +} + +export default CSS diff --git a/client/src/assets/LanguageLogo/Kotlin.js b/client/src/assets/LanguageLogo/Kotlin.js new file mode 100644 index 000000000..808d18953 --- /dev/null +++ b/client/src/assets/LanguageLogo/Kotlin.js @@ -0,0 +1,12 @@ +function Kotlin() { + return ( + + + + ) +} + +export default Kotlin diff --git a/client/src/assets/LanguageLogo/Lua.js b/client/src/assets/LanguageLogo/Lua.js new file mode 100644 index 000000000..f3a8f71d9 --- /dev/null +++ b/client/src/assets/LanguageLogo/Lua.js @@ -0,0 +1,20 @@ +function Lua() { + return ( + + + + + + ) +} + +export default Lua diff --git a/client/src/assets/LanguageLogo/R.js b/client/src/assets/LanguageLogo/R.js new file mode 100644 index 000000000..6de2f3cf7 --- /dev/null +++ b/client/src/assets/LanguageLogo/R.js @@ -0,0 +1,18 @@ +function R() { + return ( + + + + + ) +} + +export default R diff --git a/client/src/assets/LanguageLogo/Rust.js b/client/src/assets/LanguageLogo/Rust.js new file mode 100644 index 000000000..c6fd44f60 --- /dev/null +++ b/client/src/assets/LanguageLogo/Rust.js @@ -0,0 +1,14 @@ +function Rust() { + return ( + + + + ) +} + +export default Rust diff --git a/client/src/assets/LanguageLogo/TS.js b/client/src/assets/LanguageLogo/TS.js new file mode 100644 index 000000000..bab55baec --- /dev/null +++ b/client/src/assets/LanguageLogo/TS.js @@ -0,0 +1,16 @@ +function TS() { + return ( + + + + + ) +} + +export default TS diff --git a/client/src/components/NavBar/Profile/Profile.jsx b/client/src/components/NavBar/Profile/Profile.jsx index 5297d2417..d8fdfb7ee 100644 --- a/client/src/components/NavBar/Profile/Profile.jsx +++ b/client/src/components/NavBar/Profile/Profile.jsx @@ -14,8 +14,6 @@ let defaultData = { } const changeData = (data) => { - console.log(data) - return { userRealName: data.fullName, userUsername: data.username, diff --git a/client/src/components/Profile/Profile.jsx b/client/src/components/Profile/Profile.jsx index 0650a1411..68bcca2e2 100644 --- a/client/src/components/Profile/Profile.jsx +++ b/client/src/components/Profile/Profile.jsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react' import { useParams } from 'react-router-dom' -import { Formik, useFormikContext } from 'formik' +import { Formik } from 'formik' import { useCheckAuth } from '../../api/hooks/auth/useCheckAuth' import { useUpdateAvatar } from '../../api/hooks/shared/useUpdateAvatar' @@ -12,6 +12,7 @@ import { usePrompt } from '../../hooks/usePrompt' import { editProfileValidation } from '../../schemas' import Loader from '../../shared/components/Loader/Loader' import ModalComponent from '../../shared/components/Modal/Modal' +import { formatDateString } from '../../utils/convertStringToDate' import Page404Form from '../Forms/Page404Form/Page404Form' import ProfileInfo from './components/ProfileInfo/ProfileInfo' @@ -46,6 +47,7 @@ const Profile = () => { github, telegram, linkedIn, + behance, programmingLanguages, frameworks, dateOfBirth, @@ -54,6 +56,9 @@ const Profile = () => { universityData, file, } = values + + const formattedDOB = formatDateString(dateOfBirth) + const modifiedUserData = { email: showingUser.email, fullName, @@ -65,10 +70,11 @@ const Profile = () => { github, telegram, linkedIn, + behance, }, programmingLanguages, frameworks, - dateOfBirth, + dateOfBirth: formattedDOB, projectData, jobData, universityData, @@ -95,6 +101,7 @@ const Profile = () => { github: showingUser?.links?.github, linkedIn: showingUser?.links?.linkedIn, telegram: showingUser?.links?.telegram, + behance: showingUser?.links?.behance, description: showingUser?.description, concentration: showingUser?.concentration, country: showingUser?.country, @@ -112,6 +119,7 @@ const Profile = () => { onSubmit={handleSubmit} > {({ values, errors, dirty }) => { + console.log(values, dirty) usePrompt('You have unsaved changes. Do you want to discard them?', dirty) return ( diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation.jsx deleted file mode 100644 index 78b61e3e3..000000000 --- a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation.jsx +++ /dev/null @@ -1,299 +0,0 @@ -import { useState } from 'react' -import { ThreeDots } from 'react-loader-spinner' -import { FieldArray, useFormikContext } from 'formik' - -import { useEditUserDetails } from '../../../../../api/hooks/user/useEditUserDetails' -import LongArrowLeft from '../../../../../assets/Arrows/LongArrowLeft' -import CrossIcon from '../../../../../assets/UserProfile/CrossIcon' -import EditIcon from '../../../../../assets/UserProfile/EditIcon' -import PlusIconWhite from '../../../../../assets/UserProfile/PlusIconWhite' -import FlexWrapper from '../../../../../shared/components/FlexWrapper/FlexWrapper' -import CustomInput from '../../../../../shared/components/Formik/CustomInput/CustomInput' -import ModalComponent from '../../../../../shared/components/Modal/Modal' -import { ActionButton, EditIconContainer, Text } from '../ResumeInfo.styles' - -function EditingComponentEducation({ handleBack, showingUser }) { - const [modalActive, setModalActive] = useState('') - const { values, setFieldValue, resetForm } = useFormikContext() - const [currentAction, setCurrentAction] = useState('main') // 'main', 'editing', 'adding' - const [index, setIndex] = useState(0) - - const handleReset = () => { - setModalActive('') - setCurrentAction('main') - } - - const { mutate: editUserDetails, isLoading } = useEditUserDetails(handleReset) - - const handleRemoveUniversity = () => { - const universities = [...values.universityData] - - const updatedUniversityData = universities.filter((_, i) => i !== index) - - setFieldValue('universityData', updatedUniversityData) - - editUserDetails({ email: showingUser.email, universityData: updatedUniversityData }) - } - - const handleEditUniversity = (index) => { - setIndex(index) - setCurrentAction('editing') - } - - const submitEditUniversity = () => { - editUserDetails({ email: showingUser.email, universityData: values.universityData }) - } - - const handleOpenModal = (index) => { - setModalActive('DeleteUniversity') - setIndex(index) - } - - const handleClose = () => { - setModalActive('') - } - - const handleCancel = () => { - setCurrentAction('main') - resetForm() - } - - return ( - <> - - - - Education - - {currentAction === 'main' && ( - - {({ push, remove }) => ( - <> - {values?.universityData?.length > 0 ? ( - values?.universityData.map((university, index) => ( - - - - {university.degree} - {university.major} - - - {university.university} - - - {university?.addmissionDate?.split('-')[0]} -{' '} - {university?.graduationDate?.split('-')[0]} - - - - handleEditUniversity(index)} - > - - - handleOpenModal(index)} - > - - - - - )) - ) : ( - - No universities added yet. - - )} - - - - Back - - { - push({ - university: '', - degree: ``, - major: '', - addmissionDate: '', - graduationDate: '', - }) - setCurrentAction('adding') - }} - type="button" - disabled={values.universityData.length === 2 ? true : false} - > - Add new - - - - - )} - - )} - - {currentAction === 'editing' && ( - <> - - - - - - - - - - - Cancel - - - {isLoading ? ( - - ) : ( - 'Save' - )} - - - - - )} - - {currentAction === 'adding' && ( - <> - - - - - - - - - - - Cancel - - - {isLoading ? ( - - ) : ( - 'Save' - )} - - - - - )} - - - ) -} - -export default EditingComponentEducation diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/EditingComponentEducation.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/EditingComponentEducation.jsx new file mode 100644 index 000000000..b5f0a556a --- /dev/null +++ b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/EditingComponentEducation.jsx @@ -0,0 +1,152 @@ +import { useEffect, useState } from 'react' +import { useFormikContext } from 'formik' +import { cloneDeep } from 'lodash' + +import { useEditUserDetails } from '../../../../../../api/hooks/user/useEditUserDetails' +import FlexWrapper from '../../../../../../shared/components/FlexWrapper/FlexWrapper' +import ModalComponent from '../../../../../../shared/components/Modal/Modal' +import { errorToaster } from '../../../../../../shared/components/Toasters/Error.toaster' +import { Text } from '../../ResumeInfo.styles' + +import Adding from './States/Adding' +import Editing from './States/Editing' +import Main from './States/Main' + +function EditingComponentEducation({ handleBack, showingUser }) { + const [modalActive, setModalActive] = useState('') + const { values, setFieldValue, resetForm, validateForm, setErrors } = useFormikContext() + const [currentAction, setCurrentAction] = useState('main') // 'main', 'editing', 'adding' + const [index, setIndex] = useState(0) + const [checkbox, setCheckbox] = useState(false) + + useEffect(() => { + values && values?.universityData[index]?.graduationDate === null + ? setCheckbox(true) + : setCheckbox(false) + }, [index]) + + const handleReset = () => { + setModalActive('') + setCurrentAction('main') + } + + const { mutate: editUserDetails, isLoading } = useEditUserDetails(handleReset) + + const handleRemoveUniversity = () => { + const universities = [...values.universityData] + + const updatedUniversityData = universities.filter((_, i) => i !== index) + + setFieldValue('universityData', updatedUniversityData) + setIndex((prev) => prev - 1) + + editUserDetails({ email: showingUser.email, universityData: updatedUniversityData }) + } + + const handleEditUniversity = (index) => { + setIndex(index) + setCurrentAction('editing') + } + + const submitEditUniversity = async () => { + const validation = await validateForm() + + console.log(validation) + + if (!validation?.universityData) { + const initialObject = cloneDeep(values.universityData) + + initialObject[index].addmissionDate = new Date( + String(initialObject[index].addmissionDate), + ).toISOString() + initialObject[index].graduationDate = initialObject[index].graduationDate + ? new Date(String(initialObject[index].graduationDate)).toISOString() + : null + + setFieldValue(`universityData[${index}].addmissionDate`, initialObject[index].addmissionDate) + setFieldValue(`universityData[${index}].graduationDate`, initialObject[index].graduationDate) + + editUserDetails({ email: showingUser.email, universityData: initialObject }) + } else { + setErrors({ universityData: validation.universityData[index] }) + errorToaster('Fill in data before submission!') + } + } + + const handleOpenModal = (index) => { + setModalActive('DeleteUniversity') + setIndex(index) + } + + const handleClose = () => { + setModalActive('') + } + + const handleCancel = () => { + setCurrentAction('main') + resetForm() + setIndex(0) + } + + const handleClick = () => { + if (checkbox) { + setCheckbox(false) + setFieldValue(`universityData[${index}].graduationDate`, '') + } else { + setCheckbox(true) + setFieldValue(`universityData[${index}].graduationDate`, null) + } + } + + return ( + <> + + + + Education + + {currentAction === 'main' && ( +
+ )} + + {currentAction === 'editing' && ( + + )} + + {currentAction === 'adding' && ( + + )} + + + ) +} + +export default EditingComponentEducation diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/States/Adding.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/States/Adding.jsx new file mode 100644 index 000000000..b7d8b1360 --- /dev/null +++ b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/States/Adding.jsx @@ -0,0 +1,104 @@ +import { ThreeDots } from 'react-loader-spinner' +import { useFormikContext } from 'formik' + +import { degrees } from '../../../../../../../constants/degrees' +import { majors } from '../../../../../../../constants/majors' +import CheckboxWithLabel from '../../../../../../../shared/components/CheckboxWithLabel/CheckboxWithLabel' +import FlexWrapper from '../../../../../../../shared/components/FlexWrapper/FlexWrapper' +import CustomInput from '../../../../../../../shared/components/Formik/CustomInput/CustomInput' +import CustomSelectAutocomplete from '../../../../../../../shared/components/Formik/CustomSelectAutocomplete/CustomSelectAutocomplete' +import { ActionButton } from '../../../ResumeInfo.styles' + +const Adding = ({ + handleCancel, + submitEditUniversity, + isLoading, + handleClick, + checkbox, + index, +}) => { + const { values, errors } = useFormikContext() + + return ( + + + + + + + + {!checkbox && ( + + )} + + + + + + Cancel + + + {isLoading ? ( + + ) : ( + 'Save' + )} + + + + ) +} + +export default Adding diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/States/Editing.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/States/Editing.jsx new file mode 100644 index 000000000..681b13936 --- /dev/null +++ b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/States/Editing.jsx @@ -0,0 +1,120 @@ +import { useEffect } from 'react' +import { ThreeDots } from 'react-loader-spinner' +import { useFormikContext } from 'formik' + +import { degrees } from '../../../../../../../constants/degrees' +import CheckboxWithLabel from '../../../../../../../shared/components/CheckboxWithLabel/CheckboxWithLabel' +import FlexWrapper from '../../../../../../../shared/components/FlexWrapper/FlexWrapper' +import CustomInput from '../../../../../../../shared/components/Formik/CustomInput/CustomInput' +import CustomSelectAutocomplete from '../../../../../../../shared/components/Formik/CustomSelectAutocomplete/CustomSelectAutocomplete' +import { ActionButton } from '../../../ResumeInfo.styles' + +const Editing = ({ + index, + isLoading, + handleCancel, + submitEditUniversity, + checkbox, + handleClick, +}) => { + const { setFieldValue, errors, values } = useFormikContext() + + useEffect(() => { + setFieldValue( + `universityData[${index}].addmissionDate`, + Number(values?.universityData[index]?.addmissionDate?.slice(0, 4)), + ) + + if (values?.universityData[index]?.graduationDate) { + setFieldValue( + `universityData[${index}].graduationDate`, + Number(values?.universityData[index]?.graduationDate.slice(0, 4)), + ) + } + }, []) + + return ( + + + + + + + + {!checkbox && ( + + )} + + + + + + + Cancel + + + {isLoading ? ( + + ) : ( + 'Save' + )} + + + + ) +} + +export default Editing diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/States/Main.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/States/Main.jsx new file mode 100644 index 000000000..7831e36df --- /dev/null +++ b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/States/Main.jsx @@ -0,0 +1,99 @@ +import { FieldArray } from 'formik' + +import LongArrowLeft from '../../../../../../../assets/Arrows/LongArrowLeft' +import CrossIcon from '../../../../../../../assets/UserProfile/CrossIcon' +import EditIcon from '../../../../../../../assets/UserProfile/EditIcon' +import PlusIconWhite from '../../../../../../../assets/UserProfile/PlusIconWhite' +import FlexWrapper from '../../../../../../../shared/components/FlexWrapper/FlexWrapper' +import { ActionButton, EditIconContainer, Text } from '../../../ResumeInfo.styles' + +const Main = ({ + values, + handleEditUniversity, + handleOpenModal, + handleBack, + setCurrentAction, + setIndex, + index, +}) => { + return ( + + {({ push, remove }) => ( + <> + {values?.universityData?.length > 0 ? ( + values?.universityData.map((university, index) => ( + + + + {university.degree} - {university.major} + + + {university.university} + + + {university?.addmissionDate?.split('-')[0]} -{' '} + {university?.addmissionDate && university?.graduationDate === null + ? 'Present' + : university?.graduationDate?.split('-')[0]} + + + + handleEditUniversity(index)} + > + + + handleOpenModal(index)} + > + + + + + )) + ) : ( + + No universities added yet. + + )} + + + + Back + + { + values.universityData.length !== 0 && setIndex((prev) => prev + 1) + push({ + university: '', + degree: ``, + major: '', + addmissionDate: '', + graduationDate: '', + }) + setCurrentAction('adding') + }} + type="button" + disabled={values.universityData.length === 2 ? true : false} + > + Add new + + + + + )} + + ) +} + +export default Main diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob.jsx deleted file mode 100644 index 65d5c2c5a..000000000 --- a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob.jsx +++ /dev/null @@ -1,279 +0,0 @@ -import { useState } from 'react' -import { ThreeDots } from 'react-loader-spinner' -import { FieldArray, useFormikContext } from 'formik' - -import { useEditUserDetails } from '../../../../../api/hooks/user/useEditUserDetails' -import LongArrowLeft from '../../../../../assets/Arrows/LongArrowLeft' -import CrossIcon from '../../../../../assets/UserProfile/CrossIcon' -import EditIcon from '../../../../../assets/UserProfile/EditIcon' -import PlusIconWhite from '../../../../../assets/UserProfile/PlusIconWhite' -import FlexWrapper from '../../../../../shared/components/FlexWrapper/FlexWrapper' -import CustomInput from '../../../../../shared/components/Formik/CustomInput/CustomInput' -import ModalComponent from '../../../../../shared/components/Modal/Modal' -import { ActionButton, EditIconContainer, Text } from '../ResumeInfo.styles' - -function EditingComponentJob({ handleBack, showingUser }) { - const [modalActive, setModalActive] = useState('') - const { values, setFieldValue, resetForm } = useFormikContext() - const [currentAction, setCurrentAction] = useState('main') // 'main', 'editing', 'adding' - const [index, setIndex] = useState(0) - - const handleReset = () => { - setModalActive('') - setCurrentAction('main') - } - - const { mutate: editUserDetails, isLoading } = useEditUserDetails(handleReset) - - const handleRemoveUniversity = () => { - const jobs = [...values.jobData] - - const updatedJobData = jobs.filter((_, i) => i !== index) - - setFieldValue('jobData', updatedJobData) - - editUserDetails({ email: showingUser.email, jobData: updatedJobData }) - } - - const handleEditJob = (index) => { - setIndex(index) - setCurrentAction('editing') - } - - const submitEditJob = () => { - editUserDetails({ email: showingUser.email, jobData: values.jobData }) - } - - const handleOpenModal = (index) => { - setModalActive('DeleteJob') - setIndex(index) - } - - const handleClose = () => { - setModalActive('') - } - - const handleCancel = () => { - setCurrentAction('main') - resetForm() - } - - return ( - <> - - - - Work Experience - - {currentAction === 'main' && ( - - {({ push, remove }) => ( - <> - {values?.jobData?.length > 0 ? ( - values?.jobData.map((job, index) => ( - - - - {job.title} - {job.company} - - - {job.startDate.split('-')[0]} - {job.endDate.split('-')[0]} - - - - handleEditJob(index)} - > - - - handleOpenModal(index)} - > - - - - - )) - ) : ( - - No jobs added yet. - - )} - - - - Back - - { - push({ - title: '', - company: ``, - startDate: '', - endDate: '', - }) - setCurrentAction('adding') - }} - type="button" - disabled={values.jobData.length === 2 ? true : false} - > - Add new - - - - - )} - - )} - - {currentAction === 'editing' && ( - <> - - - - - - - - - - Cancel - - - {isLoading ? ( - - ) : ( - 'Save' - )} - - - - - )} - - {currentAction === 'adding' && ( - <> - - - - - - - - - - Cancel - - - {isLoading ? ( - - ) : ( - 'Save' - )} - - - - - )} - - - ) -} - -export default EditingComponentJob diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/EditingComponentJob.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/EditingComponentJob.jsx new file mode 100644 index 000000000..97030261c --- /dev/null +++ b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/EditingComponentJob.jsx @@ -0,0 +1,149 @@ +import { useEffect, useState } from 'react' +import { useFormikContext } from 'formik' +import { cloneDeep } from 'lodash' + +import { useEditUserDetails } from '../../../../../../api/hooks/user/useEditUserDetails' +import FlexWrapper from '../../../../../../shared/components/FlexWrapper/FlexWrapper' +import ModalComponent from '../../../../../../shared/components/Modal/Modal' +import { errorToaster } from '../../../../../../shared/components/Toasters/Error.toaster' +import { Text } from '../../ResumeInfo.styles' + +import Adding from './States/Adding' +import Editing from './States/Editing' +import Main from './States/Main' + +function EditingComponentJob({ handleBack, showingUser }) { + const [modalActive, setModalActive] = useState('') + const { values, setFieldValue, resetForm, validateForm, setErrors } = useFormikContext() + const [currentAction, setCurrentAction] = useState('main') // 'main', 'editing', 'adding' + const [index, setIndex] = useState(0) + const [checkbox, setCheckbox] = useState(false) + + useEffect(() => { + console.log(index) + values && values?.jobData[index]?.endDate === null ? setCheckbox(true) : setCheckbox(false) + }, [index]) + + const handleReset = () => { + setModalActive('') + setCurrentAction('main') + } + + const { mutate: editUserDetails, isLoading } = useEditUserDetails(handleReset) + + const handleRemoveJob = () => { + const jobs = [...values.jobData] + + const updatedJobData = jobs.filter((_, i) => i !== index) + + setFieldValue('jobData', updatedJobData) + setIndex((prev) => prev - 1) + + editUserDetails({ email: showingUser.email, jobData: updatedJobData }) + } + + const handleEditJob = (index) => { + setIndex(index) + setCurrentAction('editing') + } + + const submitEditJob = async () => { + const validation = await validateForm() + + if (!validation?.jobData) { + const initialObject = cloneDeep(values.jobData) + + initialObject[index].startDate = new Date( + String(initialObject[index].startDate), + ).toISOString() + initialObject[index].endDate = initialObject[index].endDate + ? new Date(String(initialObject[index].endDate)).toISOString() + : null + + setFieldValue(`jobData[${index}].startDate`, initialObject[index].startDate) + setFieldValue(`jobData[${index}].endDate`, initialObject[index].endDate) + + editUserDetails({ email: showingUser.email, jobData: initialObject }) + } else { + setErrors({ jobData: validation.jobData[index] }) + errorToaster('Fill in data before submission!') + } + } + + const handleOpenModal = (index) => { + setModalActive('DeleteJob') + setIndex(index) + } + + const handleClose = () => { + setModalActive('') + } + + const handleCancel = () => { + setCurrentAction('main') + resetForm() + setIndex(0) + } + + const handleClick = () => { + if (checkbox) { + setCheckbox(false) + setFieldValue(`jobData[${index}].endDate`, '') + } else { + setCheckbox(true) + setFieldValue(`jobData[${index}].endDate`, null) + } + } + + return ( + <> + + + + Work Experience + + {currentAction === 'main' && ( +
+ )} + + {currentAction === 'editing' && ( + + )} + + {currentAction === 'adding' && ( + + )} + + + ) +} + +export default EditingComponentJob diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Adding.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Adding.jsx new file mode 100644 index 000000000..9f2a94158 --- /dev/null +++ b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Adding.jsx @@ -0,0 +1,83 @@ +import { ThreeDots } from 'react-loader-spinner' +import { useFormikContext } from 'formik' + +import CheckboxWithLabel from '../../../../../../../shared/components/CheckboxWithLabel/CheckboxWithLabel' +import FlexWrapper from '../../../../../../../shared/components/FlexWrapper/FlexWrapper' +import CustomInput from '../../../../../../../shared/components/Formik/CustomInput/CustomInput' +import { ActionButton } from '../../../ResumeInfo.styles' + +const Adding = ({ handleCancel, submitEditJob, isLoading, handleClick, checkbox }) => { + const { values, errors } = useFormikContext() + + return ( + + + + + + {!checkbox && ( + + )} + + + + + Cancel + + + {isLoading ? ( + + ) : ( + 'Save' + )} + + + + ) +} + +export default Adding diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Editing.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Editing.jsx new file mode 100644 index 000000000..7090ab1f4 --- /dev/null +++ b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Editing.jsx @@ -0,0 +1,103 @@ +import { useEffect } from 'react' +import { ThreeDots } from 'react-loader-spinner' +import { useFormikContext } from 'formik' + +import CheckboxWithLabel from '../../../../../../../shared/components/CheckboxWithLabel/CheckboxWithLabel' +import FlexWrapper from '../../../../../../../shared/components/FlexWrapper/FlexWrapper' +import CustomInput from '../../../../../../../shared/components/Formik/CustomInput/CustomInput' +import { ActionButton } from '../../../ResumeInfo.styles' + +const Editing = ({ index, checkbox, handleCancel, handleClick, submitEditJob, isLoading }) => { + const { setFieldValue, errors, values } = useFormikContext() + + useEffect(() => { + setFieldValue( + `jobData[${index}].startDate`, + Number(values?.jobData[index]?.startDate?.slice(0, 4)), + ) + + if (values?.jobData[index]?.endDate) { + setFieldValue( + `jobData[${index}].endDate`, + Number(values?.jobData[index]?.endDate.slice(0, 4)), + ) + } + }, []) + + return ( + + + + + + + {!checkbox && ( + + )} + + + + + Cancel + + + {isLoading ? ( + + ) : ( + 'Save' + )} + + + + ) +} + +export default Editing diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Main.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Main.jsx new file mode 100644 index 000000000..b8c950d9d --- /dev/null +++ b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Main.jsx @@ -0,0 +1,92 @@ +import { FieldArray } from 'formik' + +import LongArrowLeft from '../../../../../../../assets/Arrows/LongArrowLeft' +import CrossIcon from '../../../../../../../assets/UserProfile/CrossIcon' +import EditIcon from '../../../../../../../assets/UserProfile/EditIcon' +import PlusIconWhite from '../../../../../../../assets/UserProfile/PlusIconWhite' +import FlexWrapper from '../../../../../../../shared/components/FlexWrapper/FlexWrapper' +import { ActionButton, EditIconContainer, Text } from '../../../ResumeInfo.styles' + +const Main = ({ + values, + handleEditJob, + handleOpenModal, + handleBack, + setCurrentAction, + setIndex, + index, +}) => { + return ( + + {({ push, remove }) => ( + <> + {values?.jobData?.length > 0 ? ( + values?.jobData.map((job, index) => ( + + + + {job.title} - {job.company} + + + {job?.startDate?.split('-')[0]} -{' '} + {job?.startDate && job?.endDate === null + ? 'Present' + : job?.endDate?.split('-')[0]} + + + + handleEditJob(index)}> + + + handleOpenModal(index)} + > + + + + + )) + ) : ( + + No jobs added yet. + + )} + + + + Back + + { + values.jobData.length !== 0 && setIndex((prev) => prev + 1) + push({ + title: '', + company: ``, + startDate: '', + endDate: '', + }) + setCurrentAction('adding') + }} + type="button" + disabled={values?.jobData?.length === 2 ? true : false} + > + Add new + + + + + )} + + ) +} + +export default Main diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentProfile.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentProfile.jsx index 57e443354..ada47aea1 100644 --- a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentProfile.jsx +++ b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentProfile.jsx @@ -1,7 +1,7 @@ -import { useState } from 'react' +import { useEffect, useState } from 'react' import { useFormikContext } from 'formik' -import concentrationOptions from '../../../../../constants/concentrations' +import { concentrationOptions } from '../../../../../constants/concentrations' import { countries } from '../../../../../constants/countries' import { userExperienceOptions } from '../../../../../constants/finishRegistrationData' import FlexWrapper from '../../../../../shared/components/FlexWrapper/FlexWrapper' @@ -12,7 +12,11 @@ import { ResumePartBox, ResumePartBtn } from '../ResumeInfo.styles' function EditingComponentProfile() { const [isActive, setIsActive] = useState('general') - const { values } = useFormikContext() + const { values, setFieldValue } = useFormikContext() + + useEffect(() => { + setFieldValue('dateOfBirth', '') + }, []) return ( @@ -38,6 +42,7 @@ function EditingComponentProfile() { name="country" options={countries} placeholder="Select country" + value={values['country']} /> )} diff --git a/client/src/components/Profile/components/ResumeInfo/ResumeInfo.jsx b/client/src/components/Profile/components/ResumeInfo/ResumeInfo.jsx index 305a67beb..73911406b 100644 --- a/client/src/components/Profile/components/ResumeInfo/ResumeInfo.jsx +++ b/client/src/components/Profile/components/ResumeInfo/ResumeInfo.jsx @@ -7,9 +7,9 @@ import { ProfileSection } from '../../Profile.styles' import EditingComponentAvatar from './EditingComponents/EditingComponentAvatar' import EditingComponentDefault from './EditingComponents/EditingComponentDefault' import EditingComponentDescription from './EditingComponents/EditingComponentDescription' -import EditingComponentEducation from './EditingComponents/EditingComponentEducation' +import EditingComponentEducation from './EditingComponents/EditingComponentEducation/EditingComponentEducation' import EditingComponentFrameworks from './EditingComponents/EditingComponentFrameworks' -import EditingComponentJob from './EditingComponents/EditingComponentJob' +import EditingComponentJob from './EditingComponents/EditingComponentJob/EditingComponentJob' import EditingComponentLanguages from './EditingComponents/EditingComponentLanguages' import EditingComponentProfile from './EditingComponents/EditingComponentProfile' import EditingComponentProjects from './EditingComponents/EditingComponentProjects' diff --git a/client/src/components/RegistrationPipeline/components/RegistrationForms/UserEducationForm/UserEducationForm.jsx b/client/src/components/RegistrationPipeline/components/RegistrationForms/UserEducationForm/UserEducationForm.jsx index f6bac0b78..4359c88ba 100644 --- a/client/src/components/RegistrationPipeline/components/RegistrationForms/UserEducationForm/UserEducationForm.jsx +++ b/client/src/components/RegistrationPipeline/components/RegistrationForms/UserEducationForm/UserEducationForm.jsx @@ -3,9 +3,9 @@ import { useFormikContext } from 'formik' import { degrees } from '../../../../../constants/degrees' import { majors } from '../../../../../constants/majors' +import CheckboxWithLabel from '../../../../../shared/components/CheckboxWithLabel/CheckboxWithLabel' import CustomInput from '../../../../../shared/components/Formik/CustomInput/CustomInput' import CustomSelectAutocomplete from '../../../../../shared/components/Formik/CustomSelectAutocomplete/CustomSelectAutocomplete' -import CheckboxWithLabel from '../../CheckboxWithLabel/CheckboxWithLabel' import { ContentContainer } from '../../MultiStepRegistration/MultiStepRegistration.styles' import { GroupItems, InputWrapper } from './UserEducationForm.styles' diff --git a/client/src/components/RegistrationPipeline/components/RegistrationForms/UserJobForm/UserJobForm.jsx b/client/src/components/RegistrationPipeline/components/RegistrationForms/UserJobForm/UserJobForm.jsx index 839baaf64..02381dbc9 100644 --- a/client/src/components/RegistrationPipeline/components/RegistrationForms/UserJobForm/UserJobForm.jsx +++ b/client/src/components/RegistrationPipeline/components/RegistrationForms/UserJobForm/UserJobForm.jsx @@ -1,8 +1,8 @@ import React, { useEffect, useState } from 'react' import { useFormikContext } from 'formik' +import CheckboxWithLabel from '../../../../../shared/components/CheckboxWithLabel/CheckboxWithLabel' import CustomInput from '../../../../../shared/components/Formik/CustomInput/CustomInput' -import CheckboxWithLabel from '../../CheckboxWithLabel/CheckboxWithLabel' import { ContentContainer } from '../../MultiStepRegistration/MultiStepRegistration.styles' import { InputWrapper } from '../UserEducationForm/UserEducationForm.styles' @@ -10,10 +10,6 @@ const UserJobForm = () => { const [checkbox, setCheckbox] = useState(false) const { setFieldValue, values } = useFormikContext() - useEffect(() => { - values && values.jobData[0].endDate === '0' ? setCheckbox(true) : setCheckbox(false) - }, []) - const handleClick = () => { if (checkbox) { setCheckbox(false) diff --git a/client/src/constants/concentrations.js b/client/src/constants/concentrations.js index cd118f12a..fa7beddba 100644 --- a/client/src/constants/concentrations.js +++ b/client/src/constants/concentrations.js @@ -1,51 +1,75 @@ export const concentrationOptions = [ { label: 'Mobile Developer', - value: 'Mobile', + value: 'mobile', }, { label: 'Frontend/UI Developer', - value: 'Frontend', + value: 'frontend', }, { label: 'Backend Developer', - value: 'Backend', + value: 'backend', }, { label: 'Full-Stack Developer', - value: 'Fullstack', + value: 'fullstack', }, { label: 'Desktop Applications Developer', - value: 'Desktop', + value: 'desktop', }, { label: 'Embedded Systems Developer', - value: 'Embedded', + value: 'embedded', }, { label: 'Machine Learning Engineer', - value: 'ML', + value: 'ml', }, { label: 'Data Scientist', - value: 'DataScience', + value: 'datascience', }, { label: 'DevOps Engineer', - value: 'DevOps', + value: 'devops', }, { label: 'Data Engineer', - value: 'DataEngineer', + value: 'dataengineer', }, { label: 'QA/Test Engineer', - value: 'QA', + value: 'qa', + }, + { + label: 'Designer', + value: 'designer', + }, + { + label: 'Project Manager', + value: 'projectmanager', + }, + { + label: 'Cyber Security', + value: 'cybersecurity', + }, + { + label: 'Web Developer', + value: 'webdeveloper', + }, + { + label: 'Database Developer', + value: 'databasedeveloper', + }, + { + label: 'No-code Developer', + value: 'nocodedeveloper', }, { label: 'Other', - value: 'Other', + value: 'other', }, ] diff --git a/client/src/constants/frameworkColors.js b/client/src/constants/frameworkColors.js index 15a1e428b..957d1b11f 100644 --- a/client/src/constants/frameworkColors.js +++ b/client/src/constants/frameworkColors.js @@ -1,28 +1,40 @@ const frameworkColors = Object.freeze({ - NodeJS: '#5A9E54', - Ruby: '#900E04', - Angular: '#C3002F', - AndroidSDK: '#06B75D', - IOS: '#F2F2F2', - Hadoop: '#E4E017', - Ember: '#E24B31', - Django: '#194838', - Redux: '#6333AA', - React: '#00A4D3', - Spring: '#66C229', - Spark: '#E67609', - Backbone: '#00619C', - Figma: '#8D3DF5', - Photoshop: '#2C97E6', + NodeJS: '#73B212', + Ruby: '#BF2626', + Angular: '#BF2626', + Hadoop: '#0AA6BF', + Ember: '#BF2626', + Django: '#08A642', + Redux: '#6836D9', + React: '#0AA6BF', + Spring: '#73B212', + Spark: '#D9790B', + Backbone: '#225FE5', + Figma: '#6836D9', + Photoshop: '#225FE5', + jQuery: '#225FE5', + MUI: '#225FE5', + 'ASP.NET': '#225FE5', + NumPy: '#225FE5', + Flutter: '#0AA6BF', + 'React N.': '#0AA6BF', + Flask: '#0AA6BF', + VueJS: '#08A642', + Bootstrap: '#6836D9', + KMM: '#CC1FC3', + GraphQL: '#CC1FC3', + Laravel: '#BF2626', + PyTorch: '#D9790B', + 'Tensor F.': '#D9790B', + Express: '#FAFAFA', + Illustrator: '#D9790B', }) const frameworkTextColors = Object.freeze({ NodeJS: 'white', Ruby: 'white', Angular: 'white', - AndroidSDK: 'white', - IOS: '#1E1E1E', - Hadoop: '#1E1E1E', + Hadoop: 'white', Ember: 'white', Django: 'white', Redux: 'white', @@ -32,6 +44,22 @@ const frameworkTextColors = Object.freeze({ Backbone: 'white', Figma: 'white', Photoshop: 'white', + jQuery: 'white', + MUI: 'white', + 'ASP.NET': 'white', + NumPy: 'white', + Flutter: 'white', + 'React N.': 'white', + Flask: 'white', + VueJS: 'white', + Bootstrap: 'white', + KMM: 'white', + GraphQL: 'white', + Laravel: 'white', + PyTorch: 'white', + 'Tensor F.': 'white', + Express: '#1E1E1E', + Illustrator: 'white', }) export { frameworkColors, frameworkTextColors } diff --git a/client/src/constants/frameworks.js b/client/src/constants/frameworks.js index e13854b6a..8bd07a992 100644 --- a/client/src/constants/frameworks.js +++ b/client/src/constants/frameworks.js @@ -2,8 +2,6 @@ const frameworkOptions = [ { label: 'NodeJS', value: 'nodejs' }, { label: 'Ruby', value: 'ruby' }, { label: 'Angular', value: 'angular' }, - { label: 'AndroidSDK', value: 'android' }, - { label: 'IOS', value: 'ios' }, { label: 'Hadoop', value: 'hadoop' }, { label: 'Ember', value: 'ember' }, { label: 'Django', value: 'django' }, @@ -14,6 +12,22 @@ const frameworkOptions = [ { label: 'Backbone', value: 'backbone' }, { label: 'Figma', value: 'figma' }, { label: 'Photoshop', value: 'photoshop' }, + { label: 'jQuery', value: 'jquery' }, + { label: 'MUI', value: 'mui' }, + { label: 'ASP.NET', value: 'aspnet' }, + { label: 'NumPy', value: 'numpy' }, + { label: 'Flutter', value: 'flutter' }, + { label: 'React N.', value: 'reactnative' }, + { label: 'Flask', value: 'flask' }, + { label: 'VueJS', value: 'vuejs' }, + { label: 'Bootstrap', value: 'bootstrap' }, + { label: 'KMM', value: 'kotlinmultiplatformmobile' }, + { label: 'GraphQL', value: 'graphql' }, + { label: 'Laravel', value: 'laravel' }, + { label: 'PyTorch', value: 'pytorch' }, + { label: 'Tensor F.', value: 'tensorflow' }, + { label: 'Express', value: 'express' }, + { label: 'Illustrator', value: 'illustrator' }, ] export default frameworkOptions diff --git a/client/src/constants/programmingLanguages.js b/client/src/constants/programmingLanguages.js index e609534b8..e09609a72 100644 --- a/client/src/constants/programmingLanguages.js +++ b/client/src/constants/programmingLanguages.js @@ -2,18 +2,24 @@ import C from '../assets/LanguageLogo/C' import Cplusplus from '../assets/LanguageLogo/Cplusplus' import Csharp from '../assets/LanguageLogo/Csharp' +import CSS from '../assets/LanguageLogo/CSS' import Dart from '../assets/LanguageLogo/Dart' import GO from '../assets/LanguageLogo/GO' import Html from '../assets/LanguageLogo/Html' import Java from '../assets/LanguageLogo/Java' import JS from '../assets/LanguageLogo/JS' +import Kotlin from '../assets/LanguageLogo/Kotlin' +import Lua from '../assets/LanguageLogo/Lua' import Perl from '../assets/LanguageLogo/Perl' import Php from '../assets/LanguageLogo/Php' import Python from '../assets/LanguageLogo/Python' +import R from '../assets/LanguageLogo/R' import Ruby from '../assets/LanguageLogo/Ruby' +import Rust from '../assets/LanguageLogo/Rust' import Scala from '../assets/LanguageLogo/Scala' import SQL from '../assets/LanguageLogo/SQL' import Swift from '../assets/LanguageLogo/Swift' +import TS from '../assets/LanguageLogo/TS' const programmingLanguageOptions = [ { label: 'JS', value: 'js' }, @@ -27,10 +33,16 @@ const programmingLanguageOptions = [ { label: 'Go', value: 'go' }, { label: 'C#', value: 'c#' }, { label: 'Java', value: 'java' }, - { label: 'HTML/CSS', value: 'html/css' }, + { label: 'HTML', value: 'html' }, + { label: 'CSS', value: 'css' }, { label: 'Dart', value: 'dart' }, { label: 'Perl', value: 'perl' }, { label: 'SQL', value: 'sql' }, + { label: 'TS', value: 'ts' }, + { label: 'Kotlin', value: 'kotlin' }, + { label: 'Rust', value: 'rust' }, + { label: 'R', value: 'r' }, + { label: 'Lua', value: 'lua' }, ] const languageOptions = Object.freeze({ @@ -45,10 +57,16 @@ const languageOptions = Object.freeze({ Go: , 'C#': , Java: , - 'HTML/CSS': , + HTML: , + CSS: , Dart: , Perl: , SQL: , + TS: , + Kotlin: , + Rust: , + R: , + Lua: , }) export { languageOptions, programmingLanguageOptions } diff --git a/client/src/http/index.js b/client/src/http/index.js index 1108344ce..0285f5d00 100644 --- a/client/src/http/index.js +++ b/client/src/http/index.js @@ -31,8 +31,6 @@ api.interceptors.response.use( try { const response = await axios.get(`${API_URL}/auth/refresh`, { withCredentials: true }) - console.log(response) - localStorage.setItem('token', response.data.accessToken) return api.request(originalRequest) diff --git a/client/src/schemas/index.js b/client/src/schemas/index.js index 9c6291181..4e124e587 100644 --- a/client/src/schemas/index.js +++ b/client/src/schemas/index.js @@ -6,6 +6,14 @@ const regMatch = const SUPPORTED_FORMATS = ['image/jpg', 'image/jpeg', 'image/gif', 'image/png'] +const isDate = (_date) => { + const _regExp = new RegExp( + '^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$', + ) + + return _regExp.test(_date) +} + const userFileValidation = yup.object().shape({ file: yup .mixed() @@ -382,6 +390,22 @@ export const editProfileValidation = yup.object().shape( return yup.string().notRequired() } }), + dateOfBirth: yup + .string() + .matches( + /^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[0-2])\/(19\d\d|20[01][0-9]|202[0-3])$/, + 'Invalid date format. Please enter a date in the format dd/mm/yyyy', + ) + .test('valid-year', 'Year must be between 1901 and current year', function (value) { + if (value) { + const year = parseInt(value.split('/')[2]) + + return year >= 1901 && year <= new Date().getFullYear() + } + + return true + }) + .required('Please input your birthday'), experience: yup.string().required('Please choose your experience'), programmingLanguages: yup .array() @@ -438,6 +462,119 @@ export const editProfileValidation = yup.object().shape( ) .min(0) .max(5), + jobData: yup + .array() + .of( + yup.object().shape({ + title: yup + .string() + .matches(/^[a-zA-Z0-9\s]+$/, { + message: 'Only alphabets and numbers are allowed for this field', + excludeEmptyString: true, + }) + .required('Input your title!'), + company: yup + .string() + .matches(/^[a-zA-Z0-9\s]+$/, { + message: 'Only alphabets and numbers are allowed for this field', + excludeEmptyString: true, + }) + .required('Input your company!'), + startDate: yup + .string() + .test('valid-year', 'Year must be between 1901 and current year', function (value) { + /* Check if value is Date type that we got from backend */ + if (typeof value === 'string' && isDate(value)) { + return true + } + + /* Check if number is between specific range */ + return value >= 1901 && value <= new Date().getFullYear() + }) + .required('Date is required'), + endDate: yup + .string() + .test( + 'valid-year', + 'Year must be from 1901 and bigger than start date', + function (value) { + let { startDate } = this.parent + + /* Check if value is Date type that we got from backend */ + if (typeof value === 'string' && isDate(value)) { + return true + } + + /* If startDate is of type string (and not number), convert it to number */ + if (isDate(startDate)) { + startDate = Number(startDate.slice(0, 4)) + } + + /* If startDate is of type null, return true as input might be empty */ + if (value === null) { + return true + } + + /* Finally check if number is between specific range */ + return value >= 1901 && startDate <= value + }, + ) + .nullable(), + }), + ) + .min(0) + .max(2), + universityData: yup + .array() + .of( + yup.object().shape({ + major: yup.string().required('Input your major!'), + degree: yup.string().required('Input your degree!'), + university: yup.string().required('Input your university!'), + addmissionDate: yup + .string() + .test('valid-year', 'Year must be between 1901 and current year', function (value) { + /* Check if value is Date type that we got from backend */ + if (typeof value === 'string' && isDate(value)) { + return true + } + + /* Check if number is between specific range */ + return value >= 1901 && value <= new Date().getFullYear() + }) + .required('Date is required'), + graduationDate: yup + .string() + .test( + 'valid-year', + 'Year must be from 1901 and bigger than start date', + function (value) { + let { addmissionDate } = this.parent + + /* Check if value is Date type that we got from backend */ + if (typeof value === 'string' && isDate(value)) { + return true + } + + /* If startDate is of type string (and not number), convert it to number */ + if (isDate(addmissionDate)) { + addmissionDate = Number(addmissionDate.slice(0, 4)) + } + + /* If startDate is of type null, return true as input might be empty */ + if (value === null) { + return true + } + + /* Finally check if number is between specific range */ + return value >= 1901 && addmissionDate <= value + }, + ) + .nullable(), + }), + ) + .min(0) + .max(2), }, [ ['description', 'description'], diff --git a/client/src/components/RegistrationPipeline/components/CheckboxWithLabel/CheckboxWithLabel.js b/client/src/shared/components/CheckboxWithLabel/CheckboxWithLabel.js similarity index 100% rename from client/src/components/RegistrationPipeline/components/CheckboxWithLabel/CheckboxWithLabel.js rename to client/src/shared/components/CheckboxWithLabel/CheckboxWithLabel.js diff --git a/client/src/components/RegistrationPipeline/components/CheckboxWithLabel/CheckboxWithLabel.styles.js b/client/src/shared/components/CheckboxWithLabel/CheckboxWithLabel.styles.js similarity index 100% rename from client/src/components/RegistrationPipeline/components/CheckboxWithLabel/CheckboxWithLabel.styles.js rename to client/src/shared/components/CheckboxWithLabel/CheckboxWithLabel.styles.js diff --git a/client/src/shared/components/Formik/CustomInput/CustomInput.jsx b/client/src/shared/components/Formik/CustomInput/CustomInput.jsx index 4efe10cda..15f4379d9 100644 --- a/client/src/shared/components/Formik/CustomInput/CustomInput.jsx +++ b/client/src/shared/components/Formik/CustomInput/CustomInput.jsx @@ -18,6 +18,7 @@ const CustomInput = ({ shouldFormatDate = false, shouldFormatYear = false, isOptional = false, + maxLength = 524288, ...props }) => { const [field, meta, helpers] = useField(props) @@ -25,6 +26,7 @@ const CustomInput = ({ const handleChange = (event) => { const { value } = event.target + let formattedValue = value if (shouldFormatDate) { @@ -34,6 +36,7 @@ const CustomInput = ({ } helpers.setValue(formattedValue) // Manually set the value of the field + console.log(field, props) if (onInputChange) { onInputChange(formattedValue) // Pass the input value to the callback @@ -47,6 +50,7 @@ const CustomInput = ({ { - const [day, month, year] = inputValue.split('/') + const pattern = /^\d{2}\/\d{2}\/\d{4}$/ - // Create a new Date object in JavaScript (months start from 0) - const birthday = new Date(year, month - 1, day, 0, 0, 0).toISOString() + if (pattern.test(inputValue)) { + const [day, month, year] = inputValue.split('/') - return birthday + // Create a new Date object in JavaScript (months start from 0) + const birthday = new Date(year, month - 1, day, 0, 0, 0).toISOString() + + return birthday + } + + return inputValue } diff --git a/server/src/maintenance/maintenance.service.ts b/server/src/maintenance/maintenance.service.ts index 9282635a0..f7d24431a 100644 --- a/server/src/maintenance/maintenance.service.ts +++ b/server/src/maintenance/maintenance.service.ts @@ -57,14 +57,17 @@ export class MaintenanceService { 'Dart', 'Perl', 'SQL', + 'TS', + 'Kotlin', + 'Rust', + 'R', + 'Lua', ]; frameworks: string[] = [ 'NodeJS', 'Ruby', 'Angular', - 'AndroidSDK', - 'IOS', 'Hadoop', 'Ember', 'Django', @@ -75,6 +78,22 @@ export class MaintenanceService { 'Backbone', 'Figma', 'Photoshop', + 'jQuery', + 'MUI', + 'ASP.NET', + 'NumPy', + 'Flutter', + 'React N.', + 'Flask', + 'VueJS', + 'Bootstrap', + 'KMM', + 'GraphQL', + 'Laravel', + 'PyTorch', + 'Tensor F.', + 'Express', + 'Illustrator', ]; concentrations: string[] = [ @@ -89,6 +108,13 @@ export class MaintenanceService { 'DevOps Engineer', 'Data Engineer', 'QA/Test Engineer', + 'Designer', + 'Project Manager', + 'Cyber Security', + 'Web Developer', + 'Database Developer', + 'No-code Developer', + 'Other', ]; images: string[] = [ From 87350ef438e6668223a9458347b0d22cda19bb96 Mon Sep 17 00:00:00 2001 From: Nikita Mashchenko Date: Wed, 5 Jul 2023 14:46:24 -0500 Subject: [PATCH 2/2] final fixes --- client/src/components/Profile/Profile.jsx | 2 +- .../EditingComponentEducation.jsx | 9 ++-- .../EditingComponentJob.jsx | 9 ++-- .../EditingComponentJob/States/Main.jsx | 1 - .../ProjectsSkills/ProjectsSkills.jsx | 10 +++- client/src/schemas/index.js | 53 +++++++++++++------ .../Formik/CustomInput/CustomInput.jsx | 1 - 7 files changed, 57 insertions(+), 28 deletions(-) diff --git a/client/src/components/Profile/Profile.jsx b/client/src/components/Profile/Profile.jsx index 68bcca2e2..3b74c82be 100644 --- a/client/src/components/Profile/Profile.jsx +++ b/client/src/components/Profile/Profile.jsx @@ -119,7 +119,7 @@ const Profile = () => { onSubmit={handleSubmit} > {({ values, errors, dirty }) => { - console.log(values, dirty) + console.log(errors) usePrompt('You have unsaved changes. Do you want to discard them?', dirty) return ( diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/EditingComponentEducation.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/EditingComponentEducation.jsx index b5f0a556a..8b64b7df3 100644 --- a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/EditingComponentEducation.jsx +++ b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentEducation/EditingComponentEducation.jsx @@ -23,11 +23,12 @@ function EditingComponentEducation({ handleBack, showingUser }) { values && values?.universityData[index]?.graduationDate === null ? setCheckbox(true) : setCheckbox(false) - }, [index]) + }, [index, values?.universityData[index]?.graduationDate]) const handleReset = () => { setModalActive('') setCurrentAction('main') + setIndex(0) } const { mutate: editUserDetails, isLoading } = useEditUserDetails(handleReset) @@ -38,9 +39,9 @@ function EditingComponentEducation({ handleBack, showingUser }) { const updatedUniversityData = universities.filter((_, i) => i !== index) setFieldValue('universityData', updatedUniversityData) - setIndex((prev) => prev - 1) editUserDetails({ email: showingUser.email, universityData: updatedUniversityData }) + setIndex(0) } const handleEditUniversity = (index) => { @@ -51,8 +52,6 @@ function EditingComponentEducation({ handleBack, showingUser }) { const submitEditUniversity = async () => { const validation = await validateForm() - console.log(validation) - if (!validation?.universityData) { const initialObject = cloneDeep(values.universityData) @@ -67,6 +66,7 @@ function EditingComponentEducation({ handleBack, showingUser }) { setFieldValue(`universityData[${index}].graduationDate`, initialObject[index].graduationDate) editUserDetails({ email: showingUser.email, universityData: initialObject }) + setIndex(0) } else { setErrors({ universityData: validation.universityData[index] }) errorToaster('Fill in data before submission!') @@ -80,6 +80,7 @@ function EditingComponentEducation({ handleBack, showingUser }) { const handleClose = () => { setModalActive('') + setIndex(0) } const handleCancel = () => { diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/EditingComponentJob.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/EditingComponentJob.jsx index 97030261c..71ab75a24 100644 --- a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/EditingComponentJob.jsx +++ b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/EditingComponentJob.jsx @@ -20,13 +20,13 @@ function EditingComponentJob({ handleBack, showingUser }) { const [checkbox, setCheckbox] = useState(false) useEffect(() => { - console.log(index) values && values?.jobData[index]?.endDate === null ? setCheckbox(true) : setCheckbox(false) - }, [index]) + }, [index, values?.jobData[index]?.endDate]) const handleReset = () => { setModalActive('') setCurrentAction('main') + setIndex(0) } const { mutate: editUserDetails, isLoading } = useEditUserDetails(handleReset) @@ -37,9 +37,9 @@ function EditingComponentJob({ handleBack, showingUser }) { const updatedJobData = jobs.filter((_, i) => i !== index) setFieldValue('jobData', updatedJobData) - setIndex((prev) => prev - 1) editUserDetails({ email: showingUser.email, jobData: updatedJobData }) + setIndex(0) } const handleEditJob = (index) => { @@ -64,6 +64,7 @@ function EditingComponentJob({ handleBack, showingUser }) { setFieldValue(`jobData[${index}].endDate`, initialObject[index].endDate) editUserDetails({ email: showingUser.email, jobData: initialObject }) + setIndex(0) } else { setErrors({ jobData: validation.jobData[index] }) errorToaster('Fill in data before submission!') @@ -77,6 +78,7 @@ function EditingComponentJob({ handleBack, showingUser }) { const handleClose = () => { setModalActive('') + setIndex(0) } const handleCancel = () => { @@ -116,7 +118,6 @@ function EditingComponentJob({ handleBack, showingUser }) { handleBack={handleBack} setCurrentAction={setCurrentAction} setIndex={setIndex} - index={index} /> )} diff --git a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Main.jsx b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Main.jsx index b8c950d9d..f1832352b 100644 --- a/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Main.jsx +++ b/client/src/components/Profile/components/ResumeInfo/EditingComponents/EditingComponentJob/States/Main.jsx @@ -14,7 +14,6 @@ const Main = ({ handleBack, setCurrentAction, setIndex, - index, }) => { return ( diff --git a/client/src/components/Profile/components/ResumeInfo/ProjectsSkills/ProjectsSkills.jsx b/client/src/components/Profile/components/ResumeInfo/ProjectsSkills/ProjectsSkills.jsx index 6718f895f..ca7135b9d 100644 --- a/client/src/components/Profile/components/ResumeInfo/ProjectsSkills/ProjectsSkills.jsx +++ b/client/src/components/Profile/components/ResumeInfo/ProjectsSkills/ProjectsSkills.jsx @@ -31,7 +31,13 @@ const ProjectsSkills = ({ showingUser, setIsEditing, userStatus }) => { )} {showingUser?.description ? ( -