Skip to content

Commit

Permalink
Merge pull request #18 from frontend-park-mail-ru/feature-cv-page
Browse files Browse the repository at this point in the history
Релиз приложения к рк2
  • Loading branch information
let-robots-reign authored Nov 8, 2024
2 parents 250afa6 + 4efaf91 commit 140cf26
Show file tree
Hide file tree
Showing 121 changed files with 2,931 additions and 388 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ComponentController } from '../../modules/Components/Component.js';
import eventBus from '../../modules/Events/EventBus.js';
import { USER_UPDATED } from '../../modules/Events/Events.js';
import { NOTIFICATION_OK, USER_UPDATED } from '../../modules/Events/Events.js';
import { NOTIFICATION_TIMEOUT } from '../NotificationBox/NotificationBox.js';

export class ApplicantProfileFormController extends ComponentController {
constructor(model, view, controller) {
Expand Down Expand Up @@ -40,6 +41,10 @@ export class ApplicantProfileFormController extends ComponentController {
return false;
}
eventBus.emit(USER_UPDATED);
eventBus.emit(NOTIFICATION_OK, {
message: 'Успешно сохранено',
timeout: NOTIFICATION_TIMEOUT.MEDIUM,
});
return true;
}

Expand Down
10 changes: 8 additions & 2 deletions src/Components/ApplicantProfileForm/ApplicantProfileFormModel.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ComponentModel } from '../../modules/Components/Component.js';
import { Api } from '../../modules/Api/Api.js';
import { Applicant } from '../../modules/models/Applicant.js';
import { catchStandardResponseError } from '../../modules/Api/Errors.js';

export class ApplicantProfileFormModel extends ComponentModel {
#lastValidData;
Expand All @@ -23,9 +24,14 @@ export class ApplicantProfileFormModel extends ComponentModel {
async submit(formData) {
formData.birthDate = new Date(formData.birthDate);
formData.id = this.#userId;
if (await Api.updateApplicantProfile(formData)) {
this.#lastValidData = new Applicant(formData);
try {
await Api.updateApplicantProfile(formData);
const app = new Applicant(formData);
app.birthDate = app.birthDate.toISOString().split('T')[0];
this.#lastValidData = app;
return true;
} catch (err) {
catchStandardResponseError(err);
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { ComponentController } from '../../modules/Components/Component.js';
import { REGISTER_APPLICANT } from '../../modules/Events/Events.js';
import router from '/src/modules/Router/Router.js';
import { resolveUrl } from '../../modules/UrlUtils/UrlUtils.js';
import eventBus from '../../modules/Events/EventBus.js';
import { NOTIFICATION_OK } from '../../modules/Events/Events.js';
import { NOTIFICATION_TIMEOUT } from '../NotificationBox/NotificationBox.js';

export class ApplicantRegistrationFormController extends ComponentController {
constructor(model, view, controller) {
Expand All @@ -20,14 +23,19 @@ export class ApplicantRegistrationFormController extends ComponentController {
}
this._model
.register(formData)
.then(() => router.navigate(new URL(resolveUrl('vacancies')), true, true))
.then(() => {
eventBus.emit(NOTIFICATION_OK, {
message: 'Успешно сохранено',
timeout: NOTIFICATION_TIMEOUT.MEDIUM,
});
router.navigate(new URL(resolveUrl('vacancies')), true, true);
})
.catch((errorMsg) => {
this._view.declineValidation(errorMsg);
});
}

_validate(formData) {
this._view.hideError();
const formValidationError = this._model.validate(formData);
if (formValidationError) {
this._view.declineValidation(formValidationError);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import state from '../../modules/AppState/AppState.js';
import { ComponentModel } from '../../modules/Components/Component.js';
import USER_TYPE from '../../modules/UserSession/UserType.js';
import { USER_ALREADY_EXISTS_ERROR } from '../../modules/Api/Errors.js';
import { TransportError, ResponseError } from '../../modules/Api/Api.js';

export class ApplicantRegistrationFormModel extends ComponentModel {
validate(formData) {
Expand All @@ -18,6 +20,15 @@ export class ApplicantRegistrationFormModel extends ComponentModel {
}

async register(formData) {
return state.userSession.register(USER_TYPE.APPLICANT, formData);
return state.userSession.register(USER_TYPE.APPLICANT, formData).catch((err) => {
if (err.toString() === USER_ALREADY_EXISTS_ERROR) {
return Promise.reject('Соискатель с таким адресом электронной почты уже зарегистрирован');
}
if (err instanceof TransportError) {
return Promise.reject('Произошла сетевая ошибка, повторите позднее');
}
if (err instanceof ResponseError)
return Promise.reject('Произошла непредвиденная ошибка, повторите позднее');
});
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { ComponentView } from '../../modules/Components/Component.js';
import { NOTIFICATION_ERROR } from '../../modules/Events/Events.js';
import { addEventListeners } from '../../modules/Events/EventUtils.js';
import { getFormData } from '../../modules/FormUtils/FormUtils.js';
import eventBus from '/src/modules/Events/EventBus.js';
import { REGISTER_APPLICANT } from '/src/modules/Events/Events.js';
import { NOTIFICATION_TIMEOUT } from '../NotificationBox/NotificationBox.js';

export class ApplicantRegistrationFormView extends ComponentView {
constructor({ elementClass }, existingElement) {
Expand All @@ -28,19 +30,16 @@ export class ApplicantRegistrationFormView extends ComponentView {
this.repeatPasswordField = this._html.querySelector(
'.applicant-registration-form__repeat-password',
);
this.error = this._html.querySelector('.applicant-registration-form__error');
}

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

hideError() {
this.error.hidden = true;
}

declineValidation(errorMessage) {
this.error.innerText = errorMessage;
this.error.hidden = false;
eventBus.emit(NOTIFICATION_ERROR, {
message: errorMessage,
timeout: NOTIFICATION_TIMEOUT.MEDIUM,
});
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<form class="{{elementClass}} applicant-registration-form" id="applicant-registration-form" method='POST' novalidate>
<div class="applicant-registration-form__error text_roboto-regular" hidden></div>
{{> validated-input formName="applicant-registration-form" elementName="first-name" inputName="firstName" inputCaption="Имя" inputType="text"}}
{{> validated-input formName="applicant-registration-form" elementName="second-name" inputName="secondName" inputCaption="Фамилия" inputType="text"}}
{{> validated-input formName="applicant-registration-form" elementName="birthdate" inputName="birthDate" inputCaption="Дата Рождения" inputType="date"}}
Expand Down
21 changes: 21 additions & 0 deletions src/Components/AppliersList/AppliersList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Component } from '../../modules/Components/Component.js';
import { AppliersListModel } from './AppliersListModel.js';
import { AppliersListController } from './AppliersListController.js';
import { AppliersListView } from './AppliersListView.js';
import { ListMixin } from '../Lists/List/ListMixin.js';

export class AppliersList extends Component {
constructor({ vacancyId, elementClass, existingElement }) {
super({
modelClass: AppliersListModel,
controllerClass: AppliersListController,
viewClass: AppliersListView,
viewParams: { elementClass },
modelParams: { vacancyId },
existingElement,
});
this._controller.fillList();
}
}

Object.assign(AppliersList.prototype, ListMixin);
12 changes: 12 additions & 0 deletions src/Components/AppliersList/AppliersListController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ComponentController } from '../../modules/Components/Component.js';

export class AppliersListController extends ComponentController {
constructor(model, view, component) {
super(model, view, component);
}

async fillList() {
const appliers = await this._model.getItems();
appliers.forEach((applier) => this._view.addListItem(applier));
}
}
31 changes: 31 additions & 0 deletions src/Components/AppliersList/AppliersListModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { ComponentModel } from '../../modules/Components/Component.js';
import { Api } from '../../modules/Api/Api.js';
import { Applicant } from '../../modules/models/Applicant.js';
import { catchStandardResponseError } from '../../modules/Api/Errors.js';

export class AppliersListModel extends ComponentModel {
#vacancyId;
constructor({ vacancyId }) {
super();
this.#vacancyId = vacancyId;
}

async getItems() {
try {
const peopleJson = (await Api.getAppliersByVacancyId({ id: this.#vacancyId })).subscribers;
const applicantObjects = peopleJson.reduce((applicantObjects, applicantJsonItem) => {
try {
const applicant = new Applicant(applicantJsonItem);
applicant.name = `${applicant.firstName} ${applicant.secondName}`;
applicantObjects.push(applicant);
return applicantObjects;
} catch {
return applicantObjects;
}
}, []);
return applicantObjects;
} catch (err) {
catchStandardResponseError(err);
}
}
}
34 changes: 34 additions & 0 deletions src/Components/AppliersList/AppliersListView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ComponentView } from '../../modules/Components/Component.js';
import { resolveUrl } from '../../modules/UrlUtils/UrlUtils.js';
import USER_TYPE from '../../modules/UserSession/UserType.js';

export class AppliersListView extends ComponentView {
#listItems;
#isEmpty;
constructor(renderParams, existingElement) {
super({
renderParams,
existingElement,
templateName: 'appliers-list.hbs',
});
this.list = this._html.querySelector('.appliers-list__list');
this.#listItems = [];
this.#isEmpty = true;
this.list.innerText = 'Пока никто не откликнулся';
}

addListItem({ id, name }) {
if (this.#isEmpty) {
this.#isEmpty = false;
this.list.innerHTML = '';
}
const listItem = document.createElement('li');
const anchorElement = document.createElement('a');
anchorElement.href = `${resolveUrl('profile')}?id=${id}&userType=${USER_TYPE.APPLICANT}`;
anchorElement.innerText = name;
anchorElement.classList.add('appliers-list__list-item');
listItem.appendChild(anchorElement);
this.list.appendChild(listItem);
this.#listItems.push(listItem);
}
}
6 changes: 6 additions & 0 deletions src/Components/AppliersList/appliers-list.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div class="{{elementClass}} appliers-list">
<h1 class="appliers-list__header">Откликнулись</h1>
<div class="appliers-list__divider"></div>
<ol class="appliers-list__list">
</ol>
</div>
5 changes: 3 additions & 2 deletions src/Components/CrudFormBox/CrudFormBoxController.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ export class CrudFormBoxController extends ComponentController {
this._component.disableForm();
}

submitForm({ caller }) {
async submitForm({ caller }) {
if (!Object.is(this._view.formView, caller)) {
return;
}
if (!this._component.form.submit()) {
const submitResult = await this._component.form.submit();
if (!submitResult) {
return;
}
this._view.readState();
Expand Down
18 changes: 18 additions & 0 deletions src/Components/CvArticle/ButtonContainer/ButtonContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {
Component,
ComponentController,
ComponentModel,
} from '../../../modules/Components/Component.js';
import { ButtonContainerView } from './ButtonContainerView.js';

export class ButtonContainer extends Component {
constructor({ isOwner, isEmployer, ownerId, cvId, existingElement }) {
super({
modelClass: ComponentModel,
viewClass: ButtonContainerView,
controllerClass: ComponentController,
viewParams: { isOwner, isEmployer, ownerId, cvId },
existingElement,
});
}
}
41 changes: 41 additions & 0 deletions src/Components/CvArticle/ButtonContainer/ButtonContainerView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { CV_DELETE, CV_EDIT } 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';

export class ButtonContainerView extends ComponentView {
#editButton;
#deleteButton;
#cvId;
constructor({ isOwner, isEmployer, ownerId, cvId }, existingElement) {
super({
renderParams: { isOwner, isEmployer, ownerId },
existingElement,
templateName: 'cv-article__button-container.hbs',
});
this.#cvId = cvId;
if (isOwner) {
this.#editButton = this._html.querySelector('.cv-article__edit-button');
this.#deleteButton = this._html.querySelector('.cv-article__delete-button');
this._eventListeners.push(
{
event: 'click',
object: this.#editButton,
listener: function (ev) {
ev.preventDefault();
eventBus.emit(CV_EDIT, { vacancyId: this.#cvId });
}.bind(this),
},
{
event: 'click',
object: this.#deleteButton,
listener: function (ev) {
ev.preventDefault();
eventBus.emit(CV_DELETE, { vacancyId: this.#cvId });
}.bind(this),
},
);
}
addEventListeners(this._eventListeners);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class="cv-article__button-container">
{{#if isEmployer}}
<a class="cv-article__another-button button button_main-secondary" href="{{url "profile" ""}}?id={{ownerId}}&userType=applicant&from=cvList">Все резюме</a>
{{else}}
{{#if isOwner}}
<button type="button" class="cv-article__edit-button button button_main-primary">Изменить</button>
<button type="button" class="cv-article__delete-button button button_danger-secondary {
">Удалить</button>
{{/if}}
{{/if}}
<div class='cv-article__created-at'></div>
</div>
38 changes: 38 additions & 0 deletions src/Components/CvArticle/CvArticle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Component } from '../../modules/Components/Component.js';
import { CvArticleController } from './CvArticleController.js';
import { CvArticleModel } from './CvArticleModel.js';
import { CvArticleView } from './CvArticleView.js';
import { ButtonContainer } from './ButtonContainer/ButtonContainer.js';
import USER_TYPE from '../../modules/UserSession/UserType.js';

export class CvArticle extends Component {
constructor({ elementClass, cvId, userId, userType }) {
super({
modelClass: CvArticleModel,
modelParams: { cvId },
viewClass: CvArticleView,
controllerClass: CvArticleController,
viewParams: { elementClass },
});
this._userId = userId;
this._userType = userType;
this._cvId = cvId;
}

async makeButtons() {
const modelData = await this._controller.fetchData();
this._buttonContainer = new ButtonContainer({
isOwner: modelData.applicantId === this._userId && this._userType === USER_TYPE.APPLICANT,
isEmployer: this._userType === USER_TYPE.EMPLOYER,
ownerId: modelData.applicantId,
cvId: this._cvId,
});
this._children.push(this._buttonContainer);
this._controller.addButtonContainer(this._buttonContainer);
this._controller.renderData();
}

async getApplicantId() {
return this._model.getApplicantId();
}
}
Loading

0 comments on commit 140cf26

Please sign in to comment.