diff --git a/src/application/action_creators/cv_action_creators.ts b/src/application/action_creators/cv_action_creators.ts index fcac003..4fc1fd2 100644 --- a/src/application/action_creators/cv_action_creators.ts +++ b/src/application/action_creators/cv_action_creators.ts @@ -75,7 +75,7 @@ export interface CvFormFields { const cvFormValidators = new Map( Object.entries({ positionRu: validateRequired, - positionEn: validatorTrain([validateRequired, validateEnglish]), + positionEn: validatorTrain(validateRequired, validateEnglish), jobSearchStatus: validateRequired, description: validateRequired, workingExperience: validateRequired, @@ -111,13 +111,13 @@ function validateCvForm(formFields: CvFormFields): boolean { isValid, } as CvFormData, }); - return true; + return isValid; } async function createCv(body: CvFormFields) { const backendOrigin = backendStore.getData().backendOrigin; if (!validateCvForm(body)) { - return; + throw new TypeError('form is not valid'); } try { const newCv = makeCvFromApi( @@ -146,7 +146,7 @@ async function createCv(body: CvFormFields) { async function updateCv(id: number, body: CvFormFields) { const backendOrigin = backendStore.getData().backendOrigin; if (!validateCvForm(body)) { - return; + throw new TypeError('form is not valid'); } try { const updatedCv = makeCvFromApi( diff --git a/src/application/action_creators/vacancy_action_creators.ts b/src/application/action_creators/vacancy_action_creators.ts index 2d02545..29d8fcc 100644 --- a/src/application/action_creators/vacancy_action_creators.ts +++ b/src/application/action_creators/vacancy_action_creators.ts @@ -5,6 +5,7 @@ import { UpdateAction, UpdateActionPayload, VacancyActions, + VacancyFormSubmitAction, } from '../stores/vacancy_store/vacancy_actions'; import { makeVacancyFromApi } from '../models/vacancy'; import { @@ -17,6 +18,8 @@ import { getApplicantFavoriteVacancies, removeVacancyFromFavorites, resetApplyToVacancy, + createVacancy as apiCreateVacancy, + updateVacancy as apiUpdateVacancy, } from '@/modules/api/api'; import { assertIfError } from '@/modules/common_utils/asserts/asserts'; import { Application } from '@/modules/api/src/responses/application'; @@ -24,6 +27,127 @@ import { makeApplicantFromApi } from '../models/applicant'; import { userStore } from '../stores/user_store/user_store'; import { UserType } from '../models/user-type'; import { Applicant } from '../models/applicant'; +import { + validateNumeric, + validateOk, + validateRequired, + validatorTrain, +} from '../validators/validators'; +import { FormValue } from '../models/form_value'; +import { VacancyFormData, vacancyStore } from '../stores/vacancy_store/vacancy_store'; + +export interface VacancyFormFields { + position?: string; + salary?: string; + workType?: string; + location?: string; + description?: string; + positionGroup?: string; +} + +const vacancyFormValidators = new Map( + Object.entries({ + position: validateRequired, + salary: validatorTrain(validateRequired, validateNumeric), + workType: validateRequired, + location: validateRequired, + description: validateRequired, + positionGroup: validateOk, + }), +); + +function submitVacancyFields(data: VacancyFormFields) { + const validatedData: VacancyFormData = {}; + Object.entries(data).forEach(([key, value]) => { + (validatedData as { [key: string]: FormValue })[key] = vacancyFormValidators.get(key)(value); + }); + storeManager.dispatch({ + type: VacancyActions.FormSubmit, + payload: validatedData, + } as VacancyFormSubmitAction); +} + +function validateVacancyForm(data: VacancyFormFields) { + submitVacancyFields(data); + let isValid = false; + const userData = userStore.getData(); + if (userData.userType !== UserType.Employer) { + return false; + } + const { position, salary, workType, location, description } = + vacancyStore.getData().vacancyFormData; + isValid = [position, salary, workType, location, description].every((field) => field.isValid); + storeManager.dispatch({ + type: VacancyActions.FormSubmit, + payload: { + isValid, + } as VacancyFormData, + }); + return isValid; +} + +async function createVacancy(body: VacancyFormFields) { + const backendOrigin = backendStore.getData().backendOrigin; + if (!validateVacancyForm(body)) { + throw new TypeError('Form is not valid'); + } + try { + const newVacancy = makeVacancyFromApi( + await apiCreateVacancy(backendOrigin, userStore.getData().csrfToken, { + position: body.position, + salary: Number(body.salary), + location: body.location, + workType: body.workType, + description: body.description, + positionGroup: body.positionGroup, + }), + ); + storeManager.dispatch({ + type: VacancyActions.Update, + payload: { + vacancy: newVacancy, + loaded: true, + appliers: [], + } as UpdateActionPayload, + } as UpdateAction); + } catch (err) { + assertIfError(err); + console.log(err); + vacancyActionCreators.clearVacancy(); + } +} + +async function updateVacancy(id: number, body: VacancyFormFields) { + // const backendOrigin = backendStore.getData().backendOrigin; + if (!validateVacancyForm(body)) { + throw new TypeError('Form is not valid'); + } + try { + const updatedVacancy = makeVacancyFromApi( + await apiUpdateVacancy(backendStore.getData().backendOrigin, userStore.getData().csrfToken, { + id, + position: body.position, + salary: Number(body.salary), + location: body.location, + workType: body.workType, + description: body.description, + positionGroup: body.positionGroup, + }), + ); + const oldVacancy = vacancyStore.getData(); + storeManager.dispatch({ + type: VacancyActions.Update, + payload: { + vacancy: updatedVacancy, + loaded: true, + appliers: oldVacancy.appliers, + } as UpdateActionPayload, + }); + } catch (err) { + assertIfError(err); + console.log(err); + } +} async function loadVacancy(id: number) { const backendOrigin = backendStore.getData().backendOrigin; @@ -33,7 +157,7 @@ async function loadVacancy(id: number) { const userData = userStore.getData(); let loadedAppliers: Applicant[]; if (userData.userType === UserType.Employer && userData.id === loadedVacancy.employerId) { - const rawAppliers = await getVacancyAppliers(backendOrigin, id); + const rawAppliers = await getVacancyAppliers(backendOrigin, userData.csrfToken, id); loadedAppliers = rawAppliers.subscribers.map((apiApplicant) => makeApplicantFromApi(apiApplicant), ); @@ -181,13 +305,20 @@ function clearVacancy() { } export const vacancyActionCreators = { - loadVacancy, loadApplyStatus, - loadFavoriteStatus, applyVacancy, removeApplyVacancy, + clearVacancy, - removeVacancy, + + loadFavoriteStatus, addVacancyToFavorite, removeVacancyFromFavorite, + + submitVacancyFields, + + createVacancy, + loadVacancy, + updateVacancy, + removeVacancy, }; diff --git a/src/application/components/header/header.tsx b/src/application/components/header/header.tsx index 7ab0c1c..605e6b5 100644 --- a/src/application/components/header/header.tsx +++ b/src/application/components/header/header.tsx @@ -59,6 +59,7 @@ export class Header extends Component { setIsOpen={this.setIsNotifyDropdownOpen} > diff --git a/src/application/components/input/input.tsx b/src/application/components/input/input.tsx index 3c1d1c0..aa072d1 100644 --- a/src/application/components/input/input.tsx +++ b/src/application/components/input/input.tsx @@ -72,6 +72,7 @@ export class Input extends Component { name={this.props.name} id={this.props.id} maxlength={this.props.maxlength} + onFocusOut={this.props.onFocusOut} > {this.props.value ?? ''} @@ -94,6 +95,7 @@ export class Input extends Component { name={this.props.name} id={this.props.id} value={this.props.value} + onFocusOut={this.props.onFocusOut} > {(this.props.options as Option[]).map((option) => (