diff --git a/src/api/points-api.js b/src/api/points-api.js index dd73674..c501bd6 100644 --- a/src/api/points-api.js +++ b/src/api/points-api.js @@ -3,6 +3,8 @@ import ApiService from '../framework/api-service.js'; const Method = { GET: 'GET', PUT: 'PUT', + POST: 'POST', + DELETE: 'DELETE', }; export default class PointsApi extends ApiService { @@ -24,19 +26,41 @@ export default class PointsApi extends ApiService { return parsedResponse; } + async addPoint(point) { + const response = await this._load({ + url: 'points', + method: Method.POST, + body: JSON.stringify(this.#adaptToServer(point)), + headers: new Headers({'Content-Type': 'application/json'}), + }); + + const parsedResponse = await ApiService.parseResponse(response); + + return parsedResponse; + } + + async deletePoint(point) { + const response = await this._load({ + url: `points/${point.id}`, + method: Method.DELETE, + }); + + return response; + } + #adaptToServer(point) { - const adaptedTask = {...point, + const adaptedPoint = {...point, 'base_price': point.basePrice, 'date_from': point.dateFrom.toISOString(), 'date_to': point.dateTo.toISOString(), - 'is-favorite': point.isFavorite, + 'is_favorite': point.isFavorite, }; - delete adaptedTask.basePrice; - delete adaptedTask.dateFrom; - delete adaptedTask.dateTo; - delete adaptedTask.isFavorite; + delete adaptedPoint.basePrice; + delete adaptedPoint.dateFrom; + delete adaptedPoint.dateTo; + delete adaptedPoint.isFavorite; - return adaptedTask; + return adaptedPoint; } } diff --git a/src/model/points-model.js b/src/model/points-model.js index 988843a..37340ca 100644 --- a/src/model/points-model.js +++ b/src/model/points-model.js @@ -41,47 +41,53 @@ export default class PointsModel extends Observable { ]; this._notify(updateType, updatedPoint); } catch(err) { - throw new Error('Can\'t update task'); + throw new Error('Can\'t update point'); } } - addPoint(updateType, update) { - this.#points = [ - update, - ...this.#points, - ]; - - this._notify(updateType, update); + async addPoint(updateType, update) { + try { + const response = await this.#pointsApi.addPoint(update); + const newPoint = this.#adaptToClient(response); + this.#points = [newPoint, ...this.#points]; + this._notify(updateType, newPoint); + } catch(err) { + throw new Error('Can\'t add point'); + } } - deletePoint(updateType, update) { + async deletePoint(updateType, update) { const index = this.#points.findIndex((point) => point.id === update.id); if (index === -1) { throw new Error('Can\'t delete unexisting point'); } - this.#points = [ - ...this.#points.slice(0, index), - ...this.#points.slice(index + 1), - ]; - - this._notify(updateType); + try { + await this.#pointsApi.deletePoint(update); + this.#points = [ + ...this.#points.slice(0, index), + ...this.#points.slice(index + 1), + ]; + this._notify(updateType); + } catch(err) { + throw new Error('Can\'t delete point'); + } } #adaptToClient(point) { - const adaptedTask = {...point, + const adaptedPoint = {...point, basePrice: point['base_price'], dateFrom: new Date(point['date_from']), dateTo: new Date(point['date_to']), isFavorite: point['is-favorite'] }; - delete adaptedTask['base_price']; - delete adaptedTask['date_from']; - delete adaptedTask['date_to']; - delete adaptedTask['is-favorite']; + delete adaptedPoint['base_price']; + delete adaptedPoint['date_from']; + delete adaptedPoint['date_to']; + delete adaptedPoint['is-favorite']; - return adaptedTask; + return adaptedPoint; } } diff --git a/src/presenter/new-point-presenter.js b/src/presenter/new-point-presenter.js index f974c99..c633729 100644 --- a/src/presenter/new-point-presenter.js +++ b/src/presenter/new-point-presenter.js @@ -1,6 +1,5 @@ import {remove, render, RenderPosition} from '../framework/render.js'; import EditablePointView from '../view/editable-point-view.js'; -import {nanoid} from 'nanoid'; import {UserAction, UpdateType} from '../const.js'; import { isEscapeKey } from '../util.js'; @@ -53,15 +52,31 @@ export default class NewPointPresenter { document.removeEventListener('keydown', this.#escKeyDownHandler); } + setSaving() { + this.#pointEditComponent.updateElement({ + isDisabled: true, + isSaving: true, + }); + } + + setAborting() { + const resetFormState = () => { + this.#pointEditComponent.updateElement({ + isDisabled: false, + isSaving: false, + isDeleting: false, + }); + }; + + this.#pointEditComponent.shake(resetFormState); + } + #handleFormSubmit = (point) => { this.#handleDataChange( UserAction.ADD_POINT, UpdateType.MINOR, - // Пока у нас нет сервера, который бы после сохранения - // выдывал честный id задачи, нам нужно позаботиться об этом самим - {id: nanoid(), ...point}, + point, ); - this.destroy(); }; #handleDeleteClick = () => { diff --git a/src/presenter/trip-events-presenter.js b/src/presenter/trip-events-presenter.js index e31bafa..2f4f69c 100644 --- a/src/presenter/trip-events-presenter.js +++ b/src/presenter/trip-events-presenter.js @@ -26,22 +26,22 @@ export default class TripEventsPresenter { #currentSortType = SortType.PRICE; #pointPresenters = new Map(); - #newTaskPresenter = null; + #newPointPresenter = null; #filterType = null; #isLoading = true; - constructor({tpipEventsContainer, pointsModel, offersModel, destinationsModel, filterModel, onNewTaskDestroy}) { + constructor({tpipEventsContainer, pointsModel, offersModel, destinationsModel, filterModel, onNewPointDestroy}) { this.#tpipEventsContainer = tpipEventsContainer; this.#pointsModel = pointsModel; this.#offersModel = offersModel; this.#destinationsModel = destinationsModel; this.#filterModel = filterModel; - this.#newTaskPresenter = new NewPointPresenter({ + this.#newPointPresenter = new NewPointPresenter({ pointListContainer: this.#listComponent.element, onDataChange: this.#handleViewAction, - onDestroy: onNewTaskDestroy, + onDestroy: onNewPointDestroy, offersModel: this.#offersModel, destinationsModel: this.#destinationsModel }); @@ -75,19 +75,36 @@ export default class TripEventsPresenter { createPoint() { this.#currentSortType = SortType.DAY; this.#filterModel.setFilter(UpdateType.MAJOR, FilterType.EVERYTHING); - this.#newTaskPresenter.init(); + this.#newPointPresenter.init(); } - #handleViewAction = (actionType, updateType, update) => { + #handleViewAction = async (actionType, updateType, update) => { switch (actionType) { case UserAction.UPDATE_POINT: - this.#pointsModel.updatePoint(updateType, update); + this.#pointPresenters.get(update.id).setSaving(); + try { + await this.#pointsModel.updatePoint(updateType, update); + } catch(err) { + this.#pointPresenters.get(update.id).setAborting(); + } break; + case UserAction.ADD_POINT: - this.#pointsModel.addPoint(updateType, update); + this.#newPointPresenter.setSaving(); + try { + await this.#pointsModel.addPoint(updateType, update); + } catch(err) { + this.#pointPresenters.get(update.id).setAborting(); + } break; + case UserAction.DELETE_POINT: - this.#pointsModel.deletePoint(updateType, update); + this.#pointPresenters.get(update.id).setDeleting(); + try { + await this.#pointsModel.deletePoint(updateType, update); + } catch(err) { + this.#pointPresenters.get(update.id).setAborting(); + } break; } }; @@ -121,7 +138,7 @@ export default class TripEventsPresenter { }; #changeViewHandler = () => { - this.#newTaskPresenter.destroy(); + this.#newPointPresenter.destroy(); this.#pointPresenters.forEach((presenter) => presenter.resetView()); }; @@ -178,7 +195,7 @@ export default class TripEventsPresenter { } #clearBoard(resetSortType = false) { - this.#newTaskPresenter.destroy(); + this.#newPointPresenter.destroy(); this.#pointPresenters.forEach((presenter) => presenter.remove()); this.#pointPresenters.clear(); diff --git a/src/presenter/trip-point-presenter.js b/src/presenter/trip-point-presenter.js index a3fa822..208c55e 100644 --- a/src/presenter/trip-point-presenter.js +++ b/src/presenter/trip-point-presenter.js @@ -101,6 +101,41 @@ export default class TripPointPresenter { remove(this.#editPointComponent); }; + setSaving() { + if (this.#view === VIEW.EDIT) { + this.#editPointComponent.updateElement({ + isDisabled: true, + isSaving: true, + }); + } + } + + setDeleting() { + if (this.#view === VIEW.EDIT) { + this.#editPointComponent.updateElement({ + isDisabled: true, + isDeleting: true, + }); + } + } + + setAborting() { + if (this.#view === VIEW.DEFAULT) { + this.#pointComponent.shake(); + return; + } + + const resetFormState = () => { + this.#editPointComponent.updateElement({ + isDisabled: false, + isSaving: false, + isDeleting: false, + }); + }; + + this.#editPointComponent.shake(resetFormState); + } + #pointRollupButtonClikHandler = () => { this.#replacePointToForm(); document.addEventListener('keydown', this.#onFormKeydown); @@ -118,7 +153,6 @@ export default class TripPointPresenter { UpdateType.MINOR, point ); - this.#replaceFormToPoint(); document.removeEventListener('keydown', this.#onFormKeydown); }; diff --git a/src/view/editable-point-view.js b/src/view/editable-point-view.js index 1bf3ba6..e2ce927 100644 --- a/src/view/editable-point-view.js +++ b/src/view/editable-point-view.js @@ -7,7 +7,6 @@ import 'flatpickr/dist/flatpickr.min.css'; const DATE_FORMAT = 'DD/MM/YY HH:mm'; const newPoint = { - id: 11111, basePrice: 0, dateFrom: new Date(), dateTo: new Date(), @@ -19,6 +18,7 @@ const newPoint = { function createEditablePointTemplate(state, isNew, CITIES) { const type = state.type; + const isDisabled = state.isDisabled; return( `
  • @@ -33,7 +33,7 @@ function createEditablePointTemplate(state, isNew, CITIES) {
    Event type - ${createEventTypeList(type)} + ${createEventTypeList(type, isDisabled)}
    @@ -42,7 +42,7 @@ function createEditablePointTemplate(state, isNew, CITIES) { - + ${createDestinationsList(CITIES)} @@ -50,10 +50,10 @@ function createEditablePointTemplate(state, isNew, CITIES) {
    - + - +
    @@ -61,11 +61,11 @@ function createEditablePointTemplate(state, isNew, CITIES) { Price € - +
    - - + + ${isNew ? '' : `