From e88fc7d53638cfaf6b7929efcc624fbdc3328bd0 Mon Sep 17 00:00:00 2001 From: ssftvyn Date: Sun, 19 May 2024 18:25:21 +0500 Subject: [PATCH] 8.7 --- src/api-service/points-api.js | 19 ++++++++++ src/const.js | 7 ++++ src/model/point-model.js | 39 ++++++++++--------- src/presenter/new-point-presenter.js | 22 ++++++++++- src/presenter/point-presenter.js | 37 +++++++++++++++++- src/presenter/trip-presenter.js | 33 +++++++++++++--- src/view/edit-point-view.js | 56 ++++++++++++++++++---------- 7 files changed, 167 insertions(+), 46 deletions(-) diff --git a/src/api-service/points-api.js b/src/api-service/points-api.js index 794f4fe..b51d62a 100644 --- a/src/api-service/points-api.js +++ b/src/api-service/points-api.js @@ -18,6 +18,25 @@ export default class PointsApiService extends ApiService{ return parsedResponse; } + async addPoint(point){ + const response = await this._load({ + url:'points', + method: ApiServiceMethod.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: ApiServiceMethod.DELETE, + }); + return response; + } + #adaptToServer(point) { const adaptedPoint = {...point, 'base_price': point.price, diff --git a/src/const.js b/src/const.js index 3113c89..08a0f93 100644 --- a/src/const.js +++ b/src/const.js @@ -64,4 +64,11 @@ export const POINT_COUNT_PER_STEP = 7; export const ApiServiceMethod = { GET: 'GET', PUT: 'PUT', + POST: 'POST', + DELETE: 'DELETE' +}; + +export const TimeLimit = { + LOWER_LIMIT: 350, + UPPER_LIMIT: 1000, }; diff --git a/src/model/point-model.js b/src/model/point-model.js index 84e8694..0b3da9e 100644 --- a/src/model/point-model.js +++ b/src/model/point-model.js @@ -26,7 +26,7 @@ export default class PointModel extends Observable{ async updatePoint(updateType, update) { const index = this.#points.findIndex((point) => point.id === update.id); if (index === -1) { - throw new Error('Can\'t update unexisting task'); + throw new Error('Can\'t update unexisting point'); } try { const response = await this.#pointsApiService.updatePoint(update); @@ -34,7 +34,7 @@ export default class PointModel extends Observable{ this.#points = [ ...this.#points.slice(0, index), updatedPoint, - ...this.#points.slice(index + 1), + ...this.#points.slice(index + 1) ]; this._notify(updateType, updatedPoint); }catch(error) { @@ -42,28 +42,33 @@ export default class PointModel extends Observable{ } } - addPoint(updateType, update) { - this.#points = [ - update, - ...this.#points, - ]; - - this._notify(updateType, update); + async addPoint(updateType, update) { + try { + const response = await this.#pointsApiService.addPoint(update); + const newPoint = this.#adaptToClient(response); + this.#points = [newPoint, ...this.#points]; + this._notify(updateType, newPoint); + } catch (error){ + 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.#pointsApiService.deletePoint(update); + this.#points = [ + ...this.#points.slice(0, index), + ...this.#points.slice(index + 1), + ]; + this._notify(updateType); + } catch (error){ + throw new Error('Can\'t delete point'); + } } #adaptToClient(point){ diff --git a/src/presenter/new-point-presenter.js b/src/presenter/new-point-presenter.js index a1199f5..d0d1d9a 100644 --- a/src/presenter/new-point-presenter.js +++ b/src/presenter/new-point-presenter.js @@ -1,5 +1,4 @@ import {remove, render, RenderPosition} from '../framework/render.js'; -import {nanoid} from 'nanoid'; import {UpdateType, UserAction} from '../const'; import EditingPointView from '../view/edit-point-view.js'; @@ -41,6 +40,25 @@ export default class NewPointPresenter { document.addEventListener('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); + } + + destroy() { if (this.#pointEditComponent === null) { return; @@ -58,7 +76,7 @@ export default class NewPointPresenter { this.#handleFavoriteChange( UserAction.ADD_POINT, UpdateType.MINOR, - {id: nanoid(), ...point}, + point ); this.destroy(); }; diff --git a/src/presenter/point-presenter.js b/src/presenter/point-presenter.js index 6018189..c4cefa0 100644 --- a/src/presenter/point-presenter.js +++ b/src/presenter/point-presenter.js @@ -58,7 +58,8 @@ export default class PointPresenter{ } if (this.#mode === Mode.EDITING){ - replace(this.#editPointComponent, prevEditPointComponent); + replace(this.#pointComponent, prevEditPointComponent); + this.#mode = Mode.DEFAULT; } remove(prevPointComponent); @@ -77,11 +78,43 @@ export default class PointPresenter{ } } + setSaving() { + if (this.#mode === Mode.EDITING) { + this.#editPointComponent.updateElement({ + isDisabled: true, + isSaving: true + }); + } + } + + setDeleting() { + if (this.#mode === Mode.EDITING) { + this.#editPointComponent.updateElement({ + isDisabled: true, + isDeleting: true + }); + } + } + + setAborting() { + if (this.#mode === Mode.DEFAULT) { + this.#pointComponent.shake(); + return; + } + const resetFormState = () => { + this.#editPointComponent.updateElement({ + isDisabled: false, + isSaving: false, + isDeleting: false, + }); + }; + this.#editPointComponent.shake(resetFormState); + } + #onEscKeyDown = (event) => { if(event.key === 'Escape' || event.key === 'Esc'){ event.preventDefault(); this.#editPointComponent.reset(this.#point); - this.#replaceEditPointToPoint(); } }; diff --git a/src/presenter/trip-presenter.js b/src/presenter/trip-presenter.js index 7be0e7c..e47bf85 100644 --- a/src/presenter/trip-presenter.js +++ b/src/presenter/trip-presenter.js @@ -6,9 +6,10 @@ import NoPointView from '../view/no-point-view'; import PointPresenter from './point-presenter'; import {sortPointsByType, SortType} from '../utils/common'; import {filterByType, FILTERTYPE} from '../utils/filter'; -import {UpdateType, UserAction} from '../const'; +import {TimeLimit, UpdateType, UserAction} from '../const'; import NewPointPresenter from './new-point-presenter'; import LoadingView from '../view/loading-view'; +import UiBlocker from '../framework/ui-blocker/ui-blocker'; class TripPresenter{ #container = null; @@ -25,6 +26,11 @@ class TripPresenter{ #currentSortType = SortType.DEFAULT; #filterType = FILTERTYPE.EVERYTHING; #isLoading = true; + #uiBlocker = new UiBlocker({ + lowerLimit: TimeLimit.LOWER_LIMIT, + upperLimit: TimeLimit.UPPER_LIMIT + }); + constructor({container, pointsModel, destinationsModel, offersModel, filtersModel, onNewPointDestroy}) { this.#container = container; this.#pointsModel = pointsModel; @@ -74,18 +80,35 @@ class TripPresenter{ this.#pointPresenter.forEach((presenter) => presenter.resetView()); }; - #handleViewAction = (actionType, updateType, update) => { + #handleViewAction = async (actionType, updateType, update) => { + this.#uiBlocker.block(); switch (actionType) { case UserAction.UPDATE_POINT: - this.#pointsModel.updatePoint(updateType, update); + this.#pointPresenter.get(update.id).setSaving(); + try { + await this.#pointsModel.updatePoint(updateType, update); + }catch (error){ + this.#pointPresenter.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 (error){ + this.#newPointPresenter.setAborting(); + } break; case UserAction.DELETE_POINT: - this.#pointsModel.deletePoint(updateType, update); + this.#pointPresenter.get(update.id).setDeleting(); + try { + await this.#pointsModel.deletePoint(updateType, update); + }catch (error){ + this.#pointPresenter.get(update.id).setAborting(); + } break; } + this.#uiBlocker.unblock(); }; #handleModelEvent = (updateType, data) => { diff --git a/src/view/edit-point-view.js b/src/view/edit-point-view.js index 34add86..c3e2cb2 100644 --- a/src/view/edit-point-view.js +++ b/src/view/edit-point-view.js @@ -9,23 +9,23 @@ import he from 'he'; const BLANK_POINT = { type: TYPES[0], - destination: 0, + destination: 1, startDate: dayjs(), endDate: dayjs(), - price: 0, + price: 100, isFavorite: false, offers: [] }; -const generateDestinations = (destinations) => { +const generateDestinations = (destinations, isDisabled) => { let destinationsTemplate = ''; destinations.forEach((destination) => { - destinationsTemplate += ``; + destinationsTemplate += ``; }); return destinationsTemplate; }; -const createOffersTemplates = (allOffers, checkedOffers) => { +const createOffersTemplates = (allOffers, checkedOffers, isDisabled) => { if (allOffers.length === 0) { return ''; } else { @@ -33,7 +33,8 @@ const createOffersTemplates = (allOffers, checkedOffers) => { allOffers.forEach((offer) => { const checked = checkedOffers.includes(offer.id) ? 'checked' : ''; offersTemplates += `
- +
- + - +
@@ -112,12 +118,13 @@ export const editingPoint = (point, destinations, arrayOffersIds, isNewPoint) => Price € - +
- + ${isNewPoint ? '' : - ` + ` @@ -127,7 +134,7 @@ export const editingPoint = (point, destinations, arrayOffersIds, isNewPoint) =>

Offers

- ${createOffersTemplates(allTypeOffers.offers, offers)} + ${createOffersTemplates(allTypeOffers.offers, offers, isDisabled)}
@@ -277,10 +284,19 @@ export default class EditingPointView extends AbstractStatefulView{ static parsePointToState = (point) => ({ ...point, startDate: dayjs(point.startDate).toDate(), - endDate: dayjs(point.endDate).toDate() + endDate: dayjs(point.endDate).toDate(), + isDisabled: false, + isSaving: false, + isDeleting: false }); - static parseStateToPoint = (state) => ({...state}); + static parseStateToPoint = (state) => { + const point = {...state}; + delete point.isDisabled; + delete point.isSaving; + delete point.isDeleting; + return point; + }; reset(point) { this.updateElement(