From 15ae8fae07e2d04eab862bb82860af574ac0fe8c Mon Sep 17 00:00:00 2001 From: Rudovsky Egor Date: Wed, 22 May 2024 00:26:35 +0500 Subject: [PATCH] =?UTF-8?q?8.6.=20=D0=9F=D1=80=D0=B8=D1=88=D1=91=D0=BB,=20?= =?UTF-8?q?=D1=83=D0=B2=D0=B8=D0=B4=D0=B5=D0=BB,=20=D0=B7=D0=B0=D0=B3?= =?UTF-8?q?=D1=80=D1=83=D0=B7=D0=B8=D0=BB=20(=D1=87=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D1=8C=201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/destinations-api.js | 8 ++++ src/api/offers-api.js | 8 ++++ src/api/points-api.js | 42 ++++++++++++++++++ src/const.js | 1 + src/main.js | 23 ++++++++-- src/model/destinations-model.js | 25 ++++++++--- src/model/offers-model.js | 17 ++++++-- src/model/points-model.js | 59 +++++++++++++++++++++----- src/presenter/trip-events-presenter.js | 21 +++++++++ src/view/editable-point-view.js | 33 ++++++++++---- src/view/loading-view.js | 11 +++++ 11 files changed, 217 insertions(+), 31 deletions(-) create mode 100644 src/api/destinations-api.js create mode 100644 src/api/offers-api.js create mode 100644 src/api/points-api.js create mode 100644 src/view/loading-view.js diff --git a/src/api/destinations-api.js b/src/api/destinations-api.js new file mode 100644 index 0000000..a75595d --- /dev/null +++ b/src/api/destinations-api.js @@ -0,0 +1,8 @@ +import ApiService from '../framework/api-service.js'; + +export default class DestinationsApi extends ApiService { + get destinations() { + return this._load({url: 'destinations'}) + .then(ApiService.parseResponse); + } +} diff --git a/src/api/offers-api.js b/src/api/offers-api.js new file mode 100644 index 0000000..1d47424 --- /dev/null +++ b/src/api/offers-api.js @@ -0,0 +1,8 @@ +import ApiService from '../framework/api-service.js'; + +export default class OffersApi extends ApiService { + get offers() { + return this._load({url: 'offers'}) + .then(ApiService.parseResponse); + } +} diff --git a/src/api/points-api.js b/src/api/points-api.js new file mode 100644 index 0000000..dd73674 --- /dev/null +++ b/src/api/points-api.js @@ -0,0 +1,42 @@ +import ApiService from '../framework/api-service.js'; + +const Method = { + GET: 'GET', + PUT: 'PUT', +}; + +export default class PointsApi extends ApiService { + get points() { + return this._load({url: 'points'}) + .then(ApiService.parseResponse); + } + + async updatePoint(point) { + const response = await this._load({ + url: `points/${point.id}`, + method: Method.PUT, + body: JSON.stringify(this.#adaptToServer(point)), + headers: new Headers({'Content-Type': 'application/json'}), + }); + + const parsedResponse = await ApiService.parseResponse(response); + + return parsedResponse; + } + + #adaptToServer(point) { + const adaptedTask = {...point, + 'base_price': point.basePrice, + 'date_from': point.dateFrom.toISOString(), + 'date_to': point.dateTo.toISOString(), + 'is-favorite': point.isFavorite, + }; + + delete adaptedTask.basePrice; + delete adaptedTask.dateFrom; + delete adaptedTask.dateTo; + delete adaptedTask.isFavorite; + + return adaptedTask; + } +} diff --git a/src/const.js b/src/const.js index 70d8aa7..017ec37 100644 --- a/src/const.js +++ b/src/const.js @@ -33,6 +33,7 @@ const UpdateType = { PATCH: 'PATCH', MINOR: 'MINOR', MAJOR: 'MAJOR', + INIT: 'INIT' }; const SortType = { diff --git a/src/main.js b/src/main.js index fb2f048..1841746 100644 --- a/src/main.js +++ b/src/main.js @@ -7,14 +7,27 @@ import FilterPresenter from './presenter/filter-presenter.js'; import NewPointButtonView from './view/new-point-button-view.js'; import { render } from './framework/render.js'; +import PointsApi from './api/points-api.js'; +import OffersApi from './api/offers-api.js'; +import DestinationsApi from './api/destinations-api.js'; + +const AUTHORIZATION = 'Basic h111222sfasfafsf44ffx'; +const END_POINT = 'https://21.objects.htmlacademy.pro/big-trip'; + const mainContainer = document.querySelector('.trip-main'); const tripControlsFilters = mainContainer.querySelector('.trip-controls__filters'); const tripEvents = document.querySelector('.trip-events'); -const pointsModel = new PointsModel(); -const offersModel = new OffersModel(); +const pointsModel = new PointsModel({ + pointsApi: new PointsApi(END_POINT, AUTHORIZATION) +}); +const offersModel = new OffersModel({ + offersApi: new OffersApi(END_POINT, AUTHORIZATION) +}); const filterModel = new FilterModel(); -const destinationsModel = new DestinationsModel(); +const destinationsModel = new DestinationsModel({ + destinationsApi: new DestinationsApi(END_POINT, AUTHORIZATION) +}); const tripEventsPresenter = new TripEventsPresenter({ tpipEventsContainer : tripEvents, @@ -45,6 +58,8 @@ function handleNewPointButtonClick() { } render(newPointButtonComponent, mainContainer); +offersModel.init(); +destinationsModel.init(); filterPresenter.init(); tripEventsPresenter.init(); - +pointsModel.init(); diff --git a/src/model/destinations-model.js b/src/model/destinations-model.js index 5ec50fb..f01530e 100644 --- a/src/model/destinations-model.js +++ b/src/model/destinations-model.js @@ -1,17 +1,32 @@ -import { destinations } from '../mock/destination.js'; - export default class DestinationsModel { - #destinations = destinations; + #destinations = []; + #destinationsApi = null; + + constructor({destinationsApi}) { + this.#destinationsApi = destinationsApi; + } get destinations(){ return this.#destinations; } + async init() { + try { + this.#destinations = await this.#destinationsApi.destinations; + } catch(err) { + this.#destinations = []; + } + } + + getCityNames() { + return this.#destinations.map((item) => item.name); + } + getDestinationById(id) { - return this.destinations.find((destination) => destination.id === id); + return this.#destinations.find((destination) => destination.id === id); } getDestinationByName(name) { - return this.destinations.find((destination) => destination.name === name); + return this.#destinations.find((destination) => destination.name === name); } } diff --git a/src/model/offers-model.js b/src/model/offers-model.js index 5242272..2b6ee40 100644 --- a/src/model/offers-model.js +++ b/src/model/offers-model.js @@ -1,12 +1,23 @@ -import { offers } from '../mock/offers.js'; - export default class OffersModel { - #offers = offers; + #offers = []; + #offersApi = null; + + constructor({offersApi}) { + this.#offersApi = offersApi; + } get offers(){ return this.#offers; } + async init() { + try { + this.#offers = await this.#offersApi.offers; + } catch(err) { + this.#offers = []; + } + } + getOfferByType(type){ return this.#offers.find((offer) => offer.type === type); } diff --git a/src/model/points-model.js b/src/model/points-model.js index b839a28..988843a 100644 --- a/src/model/points-model.js +++ b/src/model/points-model.js @@ -1,27 +1,48 @@ -import { points } from '../mock/points.js'; import Observable from '../framework/observable.js'; - +import {UpdateType} from '../const.js'; export default class PointsModel extends Observable { - #points = points; + #pointsApi = null; + #points = []; + + constructor({pointsApi}) { + super(); + this.#pointsApi = pointsApi; + } get points(){ return this.#points; } - updatePoint(updateType, update) { + async init() { + try { + const points = await this.#pointsApi.points; + this.#points = points.map(this.#adaptToClient); + } catch(err) { + this.#points = []; + } + + this._notify(UpdateType.INIT); + } + + async updatePoint(updateType, update) { const index = this.#points.findIndex((point) => point.id === update.id); if (index === -1) { throw new Error('Can\'t update unexisting point'); } - this.#points = [ - ...this.#points.slice(0, index), - update, - ...this.#points.slice(index + 1), - ]; - - this._notify(updateType, update); + try { + const response = await this.#pointsApi.updatePoint(update); + const updatedPoint = this.#adaptToClient(response); + this.#points = [ + ...this.#points.slice(0, index), + updatedPoint, + ...this.#points.slice(index + 1), + ]; + this._notify(updateType, updatedPoint); + } catch(err) { + throw new Error('Can\'t update task'); + } } addPoint(updateType, update) { @@ -47,4 +68,20 @@ export default class PointsModel extends Observable { this._notify(updateType); } + + #adaptToClient(point) { + const adaptedTask = {...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']; + + return adaptedTask; + } } diff --git a/src/presenter/trip-events-presenter.js b/src/presenter/trip-events-presenter.js index f6b9b6c..e31bafa 100644 --- a/src/presenter/trip-events-presenter.js +++ b/src/presenter/trip-events-presenter.js @@ -1,8 +1,11 @@ import SortView from '../view/sort-view.js'; import ListView from '../view/list-view.js'; +import LoadingView from '../view/loading-view.js'; import ListEmptyView from '../view/list-empty-view.js'; + import TripPointPresenter from './trip-point-presenter.js'; import NewPointPresenter from './new-point-presenter.js'; + import {render, remove} from '../framework/render.js'; import { UserAction, UpdateType, SortType, FilterType} from '../const.js'; import { calculateDateDifference, filter } from '../util.js'; @@ -12,6 +15,7 @@ export default class TripEventsPresenter { #listComponent = new ListView(); #listEmptyComponent = null; #sortComponent = null; + #loadingComponent = new LoadingView(); #tpipEventsContainer = null; #pointsModel = null; @@ -25,6 +29,7 @@ export default class TripEventsPresenter { #newTaskPresenter = null; #filterType = null; + #isLoading = true; constructor({tpipEventsContainer, pointsModel, offersModel, destinationsModel, filterModel, onNewTaskDestroy}) { this.#tpipEventsContainer = tpipEventsContainer; @@ -100,6 +105,12 @@ export default class TripEventsPresenter { this.#clearBoard(true); this.#renderBoard(); break; + case UpdateType.INIT: + this.#isLoading = false; + remove(this.#loadingComponent); + this.#clearBoard(true); + this.#renderBoard(); + break; } }; @@ -123,9 +134,14 @@ export default class TripEventsPresenter { render(this.#listComponent, this.#tpipEventsContainer); }; + #renderLoading() { + render(this.#loadingComponent, this.#tpipEventsContainer); + } + #renderListEmpty = () => { this.#listEmptyComponent = new ListEmptyView(this.#filterType); render(this.#listEmptyComponent, this.#tpipEventsContainer); + remove(this.#loadingComponent); }; #renderPoints = (points) => { @@ -151,6 +167,11 @@ export default class TripEventsPresenter { }; #renderBoard() { + if (this.#isLoading) { + this.#renderLoading(); + return; + } + this.#renderSort(); this.#renderList(); this.#renderPoints(this.points); diff --git a/src/view/editable-point-view.js b/src/view/editable-point-view.js index bec4731..1bf3ba6 100644 --- a/src/view/editable-point-view.js +++ b/src/view/editable-point-view.js @@ -1,4 +1,4 @@ -import { POINT_TYPE, CITIES } from '../const.js'; +import { POINT_TYPE } from '../const.js'; import { formatEventDate, isElementHas } from '../util.js'; import AbstractStatefulView from '../framework/view/abstract-stateful-view.js'; import flatpickr from 'flatpickr'; @@ -17,7 +17,7 @@ const newPoint = { type: 'flight' }; -function createEditablePointTemplate(state, isNew) { +function createEditablePointTemplate(state, isNew, CITIES) { const type = state.type; return( `
  • @@ -44,7 +44,7 @@ function createEditablePointTemplate(state, isNew) { - ${createDestinationsList()} + ${createDestinationsList(CITIES)} @@ -61,7 +61,7 @@ function createEditablePointTemplate(state, isNew) { Price € - + @@ -106,7 +106,7 @@ function createEventTypeList(type) { return eventTypeList.join(''); } -function createDestinationsList() { +function createDestinationsList(CITIES) { const destinationsList = CITIES.map((city) => ``); return destinationsList.join(''); } @@ -129,6 +129,7 @@ function createOffersList(offer, checkedOffers) { } export default class EditablePointView extends AbstractStatefulView { + #CITIES = null; #onSubmitForm = null; #onDeleteButtonClick = null; #onRollupButtonClick = null; @@ -149,12 +150,13 @@ export default class EditablePointView extends AbstractStatefulView { this.#destinationsModel = destinationsModel; this.#offersModel = offersModel; this.#isNew = isNew; + this.#CITIES = this.#destinationsModel.getCityNames(); this._restoreHandlers(); } get template() { - return createEditablePointTemplate(this._state, this.#isNew); + return createEditablePointTemplate(this._state, this.#isNew, this.#CITIES); } removeElement() { @@ -196,6 +198,9 @@ export default class EditablePointView extends AbstractStatefulView { this.element.querySelectorAll('.event__offer-checkbox') ?.forEach((checkbox) => checkbox.addEventListener('change', this.#offerInputHandler)); + this.element.querySelector('.event__input--price') + ?.addEventListener('change', this.#priceInputHandler); + this.#setDatepicker(); } @@ -205,6 +210,7 @@ export default class EditablePointView extends AbstractStatefulView { offer, destination, type: point.type, + basePrice: point.basePrice, name: destination?.name, dateFrom: point.dateFrom, dateTo: point.dateTo, @@ -220,6 +226,9 @@ export default class EditablePointView extends AbstractStatefulView { point.destination = state.destination.id; point.type = state.type; point.offers = state.checkedOffers; + point.dateFrom = state.dateFrom; + point.dateTo = state.dateTo; + point.basePrice = Number(state.basePrice); return point; } @@ -251,6 +260,14 @@ export default class EditablePointView extends AbstractStatefulView { this.#onRollupButtonClick(); }; + #priceInputHandler = (evt) => { + evt.preventDefault(); + const newPrice = evt.target.value; + this.updateElement({ + basePrice: newPrice + }); + }; + #typeInputHandler = (evt) => { evt.preventDefault(); const newOffers = this.#offersModel.getOfferByType(evt.target.value); @@ -292,13 +309,13 @@ export default class EditablePointView extends AbstractStatefulView { #dateFromChangeHandler = (userDate) => { this.updateElement({ - dateFrom: userDate, + dateFrom: new Date(userDate), }); }; #dateToChangeHandler = (userDate) => { this.updateElement({ - dateTo: userDate, + dateTo: new Date(userDate), }); }; diff --git a/src/view/loading-view.js b/src/view/loading-view.js new file mode 100644 index 0000000..837a756 --- /dev/null +++ b/src/view/loading-view.js @@ -0,0 +1,11 @@ +import AbstractView from '../framework/view/abstract-view.js'; + +function createLoadingTemplate() { + return('

    Loading...

    '); +} + +export default class ListView extends AbstractView { + get template() { + return createLoadingTemplate(); + } +}