Skip to content

Commit

Permalink
feature: fully working cvs
Browse files Browse the repository at this point in the history
  • Loading branch information
Regikon committed Nov 8, 2024
1 parent f9fe397 commit 2bfed4a
Show file tree
Hide file tree
Showing 34 changed files with 648 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ export class ApplicantProfileFormModel extends ComponentModel {
formData.birthDate = new Date(formData.birthDate);
formData.id = this.#userId;
if (await Api.updateApplicantProfile(formData)) {
this.#lastValidData = new Applicant(formData);
const app = new Applicant(formData);
app.birthDate = app.birthDate.toISOString().split('T')[0];
this.#lastValidData = app;
return true;
}
return false;
Expand Down
5 changes: 3 additions & 2 deletions src/Components/CvArticle/CvArticleController.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ComponentController } from '../../modules/Components/Component.js';
import { GO_TO, REDIRECT_TO, CV_DELETE, CV_EDIT } from '../../modules/Events/Events.js';
import { resolveUrl } from '../../modules/UrlUtils/UrlUtils.js';
import { CvPage } from '../../Pages/CvPage/CvPage.js';
import eventBus from '../../modules/Events/EventBus.js';

export class CvArticleController extends ComponentController {
Expand Down Expand Up @@ -39,8 +40,8 @@ export class CvArticleController extends ComponentController {

async cvEdit() {
const query = {};
// const vacancy = await this._model.getCvData();
// query[CvArticlePage.CV_ID] = vacancy.id;
const cv = await this._model.getCvData();
query[CvPage.CV_ID] = cv.id;
eventBus.emit(GO_TO, { redirectUrl: resolveUrl('editCv', query) });
throw Error('Not implemented');
}
Expand Down
2 changes: 1 addition & 1 deletion src/Components/CvArticle/CvArticleView.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class CvArticleView extends ComponentView {
workingExperience,
updatedAt,
}) {
this._position.innerText = `${positionRu} / ${positionEn}`;
this._position.innerText = positionEn ? `${positionRu} / ${positionEn}` : positionRu;
this._jobSearchStatus.innerText = jobSearchStatus;
this._description.innerText = description || 'Не указано';
this._workingExperience.innerText = workingExperience || 'Не указан';
Expand Down
59 changes: 59 additions & 0 deletions src/Components/CvForm/CvForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Component } from '../../modules/Components/Component.js';
import { LiteralInput } from '/src/Components/FormInputs/LiteralInput/LiteralInput.js';
import { ValidatedTextArea } from '../FormInputs/ValidatedTextArea/ValidatedTextArea.js';
import { CvFormModel } from './CvFormModel.js';
import { CvFormView } from './CvFormView.js';
import { CvFormController } from './CvFormController.js';

export class CvForm extends Component {
#isNew;
constructor({ cvId = null, elementClass, existingElement }) {
super({
modelClass: CvFormModel,
viewClass: CvFormView,
controllerClass: CvFormController,
modelParams: { cvId },
existingElement,
viewParams: { elementClass, isNew: !cvId },
});
this.#isNew = !cvId;
this._positionRuField = new LiteralInput({
existingElement: this._view.positionRuField,
selfValidate: true,
});
this._positionEnField = new LiteralInput({
existingElement: this._view.positionEnField,
selfValidate: true,
});
this._jobSearchStatusField = new LiteralInput({
existingElement: this._view.jobSearchStatusField,
selfValidate: true,
});
this._descriptionField = new ValidatedTextArea({
existingElement: this._view.descriptionField,
selfValidate: true,
});
this._workingExperienceField = new ValidatedTextArea({
existingElement: this._view.workingExperienceField,
selfValidate: true,
});
this._children.push(
this._positionEnField,
this._positionRuField,
this._jobSearchStatusField,
this._descriptionField,
this._workingExperienceField,
);
if (!this.#isNew) {
this.reset();
}
}

get view() {
return this._view;
}

reset() {
return this._controller.reset();
}
}
64 changes: 64 additions & 0 deletions src/Components/CvForm/CvFormController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { ComponentController } from '../../modules/Components/Component.js';
import { REDIRECT_TO, SUBMIT_FORM } from '../../modules/Events/Events.js';
import { CvPage } from '../../Pages/CvPage/CvPage.js';
import { Cv } from '../../modules/models/Cv.js';
import { resolveUrl } from '../../modules/UrlUtils/UrlUtils.js';
import eventBus from '../../modules/Events/EventBus.js';

export class CvFormController extends ComponentController {
constructor(model, view, controller) {
super(model, view, controller);
this.setHandlers([
{
event: SUBMIT_FORM,
handler: this.submit.bind(this),
},
]);
}

_validate() {
const errorMessage = this._model.validate(this._view.getData());
if (errorMessage) {
return false;
}
return [
this._component._positionRuField.controller.validateInput({
callerView: this._component._positionRuField._view,
}),
this._component._positionEnField.controller.validateInput({
callerView: this._component._positionEnField._view,
}),
this._component._jobSearchStatusField.controller.validateInput({
callerView: this._component._jobSearchStatusField._view,
}),
this._component._workingExperienceField.controller.validateInput({
callerView: this._component._workingExperienceField._view,
}),
this._component._descriptionField.controller.validateInput({
callerView: this._component._descriptionField._view,
}),
].every((val) => val);
}

async submit({ caller }) {
if (!Object.is(caller, this._view)) {
return;
}
if (!this._validate()) {
return;
}
const cv = await this._model.submit(new Cv(this._view.getData()));
if (!cv) {
return;
}
const query = {};
query[CvPage.CV_ID_PARAM] = cv.id;
eventBus.emit(REDIRECT_TO, { redirectUrl: resolveUrl('cv', query) });
}

async reset() {
const oldData = await this._model.getLastValidData();
this._view.renderData(oldData);
return true;
}
}
53 changes: 53 additions & 0 deletions src/Components/CvForm/CvFormModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Api } from '../../modules/Api/Api.js';
import { ComponentModel } from '../../modules/Components/Component.js';
import { Cv } from '../../modules/models/Cv.js';
import { resolveUrl } from '../../modules/UrlUtils/UrlUtils.js';
import { zip } from '../../modules/ObjectUtils/Zip.js';
import eventBus from '../../modules/Events/EventBus.js';
import { REDIRECT_TO } from '../../modules/Events/Events.js';

export class CvFormModel extends ComponentModel {
#lastValidData;
#cvId;
#isNew;

constructor({ cvId = null }) {
super();
this.#cvId = cvId;
this.#isNew = !this.#cvId;
this.#lastValidData = this.#cvId
? Api.getCvById({ id: this.#cvId }).then(
(cv) => new Cv(cv),
() => {
eventBus.emit(REDIRECT_TO, { redirectUrl: resolveUrl('createCv') });
},
)
: null;
}

async getLastValidData() {
return this.#lastValidData;
}

async submit(formData) {
const cv = this.#isNew
? await Api.createCv(formData)
: await Api.updateCvById(zip({ id: this.#cvId }, formData));
if (cv) {
this.#lastValidData = formData;
return cv;
}
return null;
}

validate(formData) {
const hasEmptyRequiredFields = [formData.positionRu, formData.jobSearchStatus].some(
(fieldValue) => {
return !fieldValue.trim();
},
);
if (hasEmptyRequiredFields) {
return 'Заполните обязательные поля';
}
}
}
47 changes: 47 additions & 0 deletions src/Components/CvForm/CvFormView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ComponentView } from '../../modules/Components/Component.js';
import eventBus from '../../modules/Events/EventBus.js';
import { SUBMIT_FORM } from '../../modules/Events/Events.js';
import { addEventListeners } from '../../modules/Events/EventUtils.js';
import { getFormData } from '../../modules/FormUtils/FormUtils.js';

export class CvFormView extends ComponentView {
constructor({ elementClass, isNew }, existingElement) {
super({
renderParams: { elementClass, isNew },
existingElement,
templateName: 'cv-form.hbs',
});
this.positionRuField = this._html.querySelector('.cv-form__position-ru');
this.positionEnField = this._html.querySelector('.cv-form__position-en');
this.jobSearchStatusField = this._html.querySelector('.cv-form__job-search-status');
this.descriptionField = this._html.querySelector('.cv-form__description');
this.workingExperienceField = this._html.querySelector('.cv-form__working-experience');

this._eventListeners.push({
event: 'submit',
object: this._html,
listener: function (ev) {
ev.preventDefault();
eventBus.emit(SUBMIT_FORM, { caller: this });
}.bind(this),
});
addEventListeners(this._eventListeners);
}

getData() {
return getFormData(this._html);
}

getId() {
return 'cv-form';
}

renderData({ positionRu, positionEn, jobSearchStatus, description, workingExperience }) {
this.positionRuField.querySelector('.validated-input__input').value = positionRu;
this.positionEnField.querySelector('.validated-input__input').value = positionEn;
this.jobSearchStatusField.querySelector('.validated-input__input').value = jobSearchStatus;
this.descriptionField.querySelector('.validated-textarea__textarea').value = description;
this.workingExperienceField.querySelector('.validated-textarea__textarea').value =
workingExperience;
}
}
19 changes: 19 additions & 0 deletions src/Components/CvForm/cv-form.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<form class="{{elementClass}} cv-form" id="cv-form" method="POST" novalidate>
{{> validated-input formName="cv-form" elementName="position-ru" inputName="positionRu" inputCaption="Должность" inputType="text"}}
{{> validated-input formName="cv-form" elementName="position-en" inputName="positionEn" inputCaption="Перевод должности на английский" inputType="text"}}
{{> validated-input formName="cv-form" elementName="job-search-status" inputName="jobSearchStatus" inputCaption="Статус поиска работы" inputType="text"}}
{{> validated-textarea formName="cv-form" elementName="description" inputName="description" inputCaption="Описание и достижения"}}
{{> validated-textarea formName="cv-form" elementName="working-experience" inputName="workingExperience" inputCaption="Опыт работы"}}
<div class="cv-form__button-container">
{{#if isNew}}
<button type="submit" class="cv-form__submit-button button button_main-primary">
Создать
</button>
{{else}}
<button type="submit" class="cv-form__submit-button button button_main-primary">
Сохранить
</button>
{{/if}}
<a class="cv-form__go-back-button button button_danger-tertiary" href="{{url "myProfile" ""}}">В профиль</a>
</div>
</form>
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export class EmployerProfileFormModel extends ComponentModel {
formData.birthDate = new Date(formData.birthDate);
formData.id = this.#userId;
if (await Api.updateEmployerProfile(formData)) {
this.#lastValidData = new Employer(formData);
return true;
}
return false;
Expand Down
14 changes: 14 additions & 0 deletions src/Components/Lists/ApplicantCvList/ApplicantCvListModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,18 @@ export class ApplicantCvListModel extends ComponentModel {
}, []);
return cvsObjects;
}

async removeChild(cvArrId) {
if (cvArrId >= this.#items.length || cvArrId < 0) {
return false;
}
const cv = this.#items[cvArrId];
try {
await Api.deleteCvById({ id: cv.id });
return true;
} catch (err) {
console.log(err);
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import {
import { ButtonContainerView } from './ButtonContainerView.js';

export class ButtonContainer extends Component {
constructor({ isOwner, isApplicant, ownerId, vacancyId, existingElement }) {
constructor({ isOwner, isApplied, isApplicant, ownerId, vacancyId, existingElement }) {
super({
modelClass: ComponentModel,
viewClass: ButtonContainerView,
controllerClass: ComponentController,
viewParams: { isOwner, isApplicant, ownerId, vacancyId },
viewParams: { isOwner, isApplicant, isApplied, ownerId, vacancyId },
existingElement,
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { VACANCY_APPLY, VACANCY_DELETE, VACANCY_EDIT } from '../../../modules/Events/Events.js';
import {
VACANCY_APPLY,
VACANCY_DELETE,
VACANCY_EDIT,
VACANCY_RESET_APPLY,
} from '../../../modules/Events/Events.js';
import { addEventListeners } from '../../../modules/Events/EventUtils.js';
import { ComponentView } from '/src/modules/Components/Component.js';
import eventBus from '/src/modules/Events/EventBus.js';
Expand All @@ -8,23 +13,38 @@ export class ButtonContainerView extends ComponentView {
#editButton;
#deleteButton;
#vacancyId;
constructor({ isOwner, isApplicant, ownerId, vacancyId }, existingElement) {
#isApplied;
#resetApplyButton;
#activeApplyButton;
constructor({ isOwner, isApplicant, ownerId, vacancyId, isApplied }, existingElement) {
super({
renderParams: { isOwner, isApplicant, ownerId },
existingElement,
templateName: 'vacancy-article__button-container.hbs',
});
this.#isApplied = isApplied;
this.#vacancyId = vacancyId;
if (isApplicant) {
this.#applyButton = this._html.querySelector('.vacancy-article__apply-button');
this._eventListeners.push({
event: 'click',
object: this.#applyButton,
listener: (ev) => {
listener: function (ev) {
ev.preventDefault();
eventBus.emit(VACANCY_APPLY, { vacancyId: this.#vacancyId });
},
eventBus.emit(VACANCY_APPLY, { caller: this, vacancyId: this.#vacancyId });
}.bind(this),
});
this.#resetApplyButton = this._html.querySelector('.vacancy-article__reset-apply-button');
this._eventListeners.push({
event: 'click',
object: this.#resetApplyButton,
listener: function (ev) {
ev.preventDefault();
eventBus.emit(VACANCY_RESET_APPLY, { caller: this, vacancyId: this.#vacancyId });
}.bind(this),
});
this.#activeApplyButton = this.#isApplied ? this.#resetApplyButton : this.#applyButton;
this.#activeApplyButton.classList.remove('hidden');
} else if (isOwner) {
this.#editButton = this._html.querySelector('.vacancy-article__edit-button');
this.#deleteButton = this._html.querySelector('.vacancy-article__delete-button');
Expand All @@ -49,4 +69,15 @@ export class ButtonContainerView extends ComponentView {
}
addEventListeners(this._eventListeners);
}

toggleApplyButton() {
this.#activeApplyButton.classList.add('hidden');
if (Object.is(this.#activeApplyButton, this.#applyButton)) {
this.#activeApplyButton = this.#resetApplyButton;
this.#resetApplyButton.classList.remove('hidden');
} else {
this.#activeApplyButton = this.#applyButton;
this.#applyButton.classList.remove('hidden');
}
}
}
Loading

0 comments on commit 2bfed4a

Please sign in to comment.