From c8914a72eac9fd8dacd9bee47476e02ed328bf7a Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Fri, 4 Oct 2024 20:12:54 +0300 Subject: [PATCH 01/17] =?UTF-8?q?=D0=B2=D1=8B=D0=B4=D0=B5=D0=BB=D0=B8?= =?UTF-8?q?=D0=BB=20event=20presenter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/presenter/board-presenter.js | 45 +++++-------------------- src/presenter/event-presenter.js | 58 ++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 36 deletions(-) create mode 100644 src/presenter/event-presenter.js diff --git a/src/presenter/board-presenter.js b/src/presenter/board-presenter.js index 78ff5dc..853a09f 100644 --- a/src/presenter/board-presenter.js +++ b/src/presenter/board-presenter.js @@ -1,9 +1,7 @@ -import { render, replace } from '../framework/render.js'; -import { isEscapeKey } from '../utils/common.js'; +import { render } from '../framework/render.js'; import NewTripSortView from '../view/new-sort-container-view.js'; import NewEventsListView from '../view/new-events-list-view.js'; -import NewEventsItemView from '../view/new-events-item-view.js'; -import NewEventEditElementView from '../view/new-event-edit-element-view.js'; +import EventPresenter from './event-presenter.js'; import NoEventsView from '../view/no-events-view.js'; @@ -28,46 +26,21 @@ export default class BoardPresenter { } #renderEvent(inputUserEvent) { - const escKeyDownHandler = (evt) => { - if (isEscapeKey(evt)) { - evt.preventDefault(); - replaceEditFormToEventCard(); - document.removeEventListener('keydown', escKeyDownHandler); - } - }; - - const eventCardComponent = new NewEventsItemView({ - userEvent: inputUserEvent, - onClick: () => { - replaceEventCardToEditForm(); - document.addEventListener('keydown', escKeyDownHandler); - } - }); - - const editFormComponent = new NewEventEditElementView({ - userEvent: inputUserEvent, - onClick: () => { - replaceEditFormToEventCard(); - document.removeEventListener('keydown', escKeyDownHandler); - } + const eventPresenter = new EventPresenter({ + container: this.#eventsListComponent.element, }); + eventPresenter.init(inputUserEvent); + } - function replaceEventCardToEditForm () { - replace(editFormComponent, eventCardComponent); - } - - function replaceEditFormToEventCard () { - replace(eventCardComponent, editFormComponent); - } - - render(eventCardComponent, this.#eventsListComponent.element); + #renderNoEvents() { + render (new NoEventsView(), this.#container); } #renderBoard () { render(this.#sortComponent, this.#container); if (this.#eventsList.length === 0) { - render (new NoEventsView(), this.#container); + this.#renderNoEvents(); return; } diff --git a/src/presenter/event-presenter.js b/src/presenter/event-presenter.js new file mode 100644 index 0000000..dbefe42 --- /dev/null +++ b/src/presenter/event-presenter.js @@ -0,0 +1,58 @@ +import { render, replace } from '../framework/render.js'; +import { isEscapeKey } from '../utils/common.js'; +import NewEventsItemView from '../view/new-events-item-view.js'; +import NewEventEditElementView from '../view/new-event-edit-element-view.js'; + +export default class EventPresenter { + #container = null; + + #eventComponent = null; + #eventEditComponent = null; + + #eventItem = null; + + constructor ({container}) { + this.#container = container; + } + + init (eventItem) { + this.#eventItem = eventItem; + + this.#eventComponent = new NewEventsItemView({ + userEvent: this.#eventItem, + onClick: this.#handleEditClick, + }); + this.#eventEditComponent = new NewEventEditElementView({ + userEvent: this.#eventItem, + onClick: this.#handleSaveClick, + }); + + render(this.#eventComponent, this.#container); + } + + #replaceEventCardToEditForm() { + replace(this.#eventEditComponent, this.#eventComponent); + document.addEventListener('keydown', this.#escKeyDownHandler); + } + + #replaceEditFormToEventCard() { + replace(this.#eventComponent, this.#eventEditComponent); + document.removeEventListener('keydown', this.#escKeyDownHandler); + } + + #escKeyDownHandler = (evt) => { + if (isEscapeKey(evt)) { + evt.preventDefault(); + this.#replaceEditFormToEventCard(); + } + }; + + #handleEditClick = () => { + this.#replaceEventCardToEditForm(); + }; + + #handleSaveClick = () => { + this.#replaceEditFormToEventCard(); + }; + +} From 8c271e781b76b61ef3ef637912860d5ff953645f Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Wed, 9 Oct 2024 17:08:45 +0300 Subject: [PATCH 02/17] =?UTF-8?q?=D0=BD=D0=B0=D1=88=D0=B5=D0=BB=20=D0=BE?= =?UTF-8?q?=D1=88=D0=B8=D0=B1=D0=BA=D1=83=20=D0=B2=20event=20presenter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 33 ++++++++++++++++++----- package.json | 3 ++- src/mock/events.js | 7 ++--- src/presenter/board-presenter.js | 15 +++++++++++ src/presenter/event-presenter.js | 36 ++++++++++++++++++++++--- src/utils/common.js | 3 +++ src/view/new-event-edit-element-view.js | 2 +- src/view/new-events-item-view.js | 11 +++++++- 8 files changed, 93 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index e6136d8..bb745d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ "dependencies": { "dayjs": "1.11.7", "flatpickr": "4.6.13", - "he": "1.2.0" + "he": "1.2.0", + "nanoid": "^5.0.7" }, "devDependencies": { "@babel/core": "7.21.4", @@ -5883,10 +5884,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz", + "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==", "funding": [ { "type": "github", @@ -5895,10 +5895,10 @@ ], "license": "MIT", "bin": { - "nanoid": "bin/nanoid.cjs" + "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^18 || >=20" } }, "node_modules/natural-compare": { @@ -6534,6 +6534,25 @@ "dev": true, "license": "MIT" }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/package.json b/package.json index 18a13e1..9df0ae8 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "dependencies": { "dayjs": "1.11.7", "flatpickr": "4.6.13", - "he": "1.2.0" + "he": "1.2.0", + "nanoid": "4.0.0" }, "browserslist": [ "last 2 versions", diff --git a/src/mock/events.js b/src/mock/events.js index dd3bb0e..1585b6b 100644 --- a/src/mock/events.js +++ b/src/mock/events.js @@ -1,15 +1,16 @@ import { getRandomArrayElement, getRandomNumber, getRandomBoolean, getRandomDate } from '../utils/common'; import { EVENTS_TYPES } from '../const'; +import {nanoid} from 'nanoid'; const TIME_SKIP = 125; -const getRandomEvent = (id, date, destinationsList) => { +const getRandomEvent = (date, destinationsList) => { const firstDate = new Date(date); const secondDate = new Date(firstDate); const destinationsIds = destinationsList.map((destination) => destination.id); secondDate.setMinutes(firstDate.getMinutes() + TIME_SKIP); const randomEvent = { - 'id': `${id}4b62099-293f-4c3d-a702-94eec4a2808c`, + 'id': nanoid(), 'base_price': getRandomNumber(499, 4999), 'date_from': firstDate.toISOString(), 'date_to': secondDate.toISOString(), @@ -30,7 +31,7 @@ const getRandomEvents = (count, destinationsList) => { const date = getRandomDate(); const events = []; for (let i = 0; i < count; i++) { - events.push(getRandomEvent(i, date, destinationsList)); + events.push(getRandomEvent(date, destinationsList)); date.setMinutes(date.getMinutes() + TIME_SKIP); } return events; diff --git a/src/presenter/board-presenter.js b/src/presenter/board-presenter.js index 853a09f..23e6e06 100644 --- a/src/presenter/board-presenter.js +++ b/src/presenter/board-presenter.js @@ -1,4 +1,5 @@ import { render } from '../framework/render.js'; +import { updateItem } from '../utils/common.js'; import NewTripSortView from '../view/new-sort-container-view.js'; import NewEventsListView from '../view/new-events-list-view.js'; import EventPresenter from './event-presenter.js'; @@ -13,6 +14,7 @@ export default class BoardPresenter { #eventsListComponent = new NewEventsListView(); #eventsList = []; + #eventPresenters = new Map(); constructor ({container, eventsModel}) { this.#container = container; @@ -28,8 +30,10 @@ export default class BoardPresenter { #renderEvent(inputUserEvent) { const eventPresenter = new EventPresenter({ container: this.#eventsListComponent.element, + onDataChange: this.#handleEventChange, }); eventPresenter.init(inputUserEvent); + this.#eventPresenters.set(inputUserEvent.id, eventPresenter); } #renderNoEvents() { @@ -50,4 +54,15 @@ export default class BoardPresenter { this.#renderEvent(this.#eventsList[i]); } } + + #handleEventChange = (updatedEvent) => { + this.#eventsList = updateItem(this.#eventsList, updatedEvent); + this.#eventPresenters.get(updatedEvent.id).init(updatedEvent); + }; + + #clearEventList() { + this.#eventPresenters.forEach((presenter) => presenter.destroy()); + this.#eventPresenters.clear(); + } + } diff --git a/src/presenter/event-presenter.js b/src/presenter/event-presenter.js index dbefe42..f3d67fa 100644 --- a/src/presenter/event-presenter.js +++ b/src/presenter/event-presenter.js @@ -1,33 +1,51 @@ -import { render, replace } from '../framework/render.js'; +import { render, replace, remove } from '../framework/render.js'; import { isEscapeKey } from '../utils/common.js'; import NewEventsItemView from '../view/new-events-item-view.js'; import NewEventEditElementView from '../view/new-event-edit-element-view.js'; export default class EventPresenter { #container = null; + #handleDataChange = null; #eventComponent = null; #eventEditComponent = null; #eventItem = null; - constructor ({container}) { + constructor ({container, onDataChange}) { this.#container = container; + this.#handleDataChange = onDataChange; } init (eventItem) { this.#eventItem = eventItem; + const prevEventComponent = this.#eventComponent; + const prevEventEditComponent = this.#eventEditComponent; + this.#eventComponent = new NewEventsItemView({ userEvent: this.#eventItem, onClick: this.#handleEditClick, + onFavoriteClick: this.#handleFavoriteClick, }); this.#eventEditComponent = new NewEventEditElementView({ userEvent: this.#eventItem, onClick: this.#handleSaveClick, }); - render(this.#eventComponent, this.#container); + if (prevEventComponent === null || prevEventEditComponent === null) { + render(this.#eventComponent, this.#container); + return; + } + + if (this.#container.contains(prevEventComponent.element)) { + replace (this.#eventComponent, prevEventComponent); + } + if (this.#container.contains(prevEventEditComponent.element)) { + replace (this.#eventEditComponent, prevEventEditComponent); + } + remove(prevEventComponent); + remove(prevEventEditComponent); } #replaceEventCardToEditForm() { @@ -40,6 +58,11 @@ export default class EventPresenter { document.removeEventListener('keydown', this.#escKeyDownHandler); } + destroy() { + remove(this.#eventComponent); + remove(this.#eventEditComponent); + } + #escKeyDownHandler = (evt) => { if (isEscapeKey(evt)) { evt.preventDefault(); @@ -51,7 +74,12 @@ export default class EventPresenter { this.#replaceEventCardToEditForm(); }; - #handleSaveClick = () => { + #handleFavoriteClick = () => { + this.#handleDataChange({...this.#eventItem, isFavorite: !this.#eventItem.isFavorite}); + }; + + #handleSaveClick = (eventItem) => { + this.#handleDataChange(eventItem); this.#replaceEditFormToEventCard(); }; diff --git a/src/utils/common.js b/src/utils/common.js index 61e6976..ac2612d 100644 --- a/src/utils/common.js +++ b/src/utils/common.js @@ -47,7 +47,10 @@ const convertKeysToCamelCase = (items) => { return items; }; +const updateItem = (items, update) => items.map((item) => item.id === update.id ? update : item); + export { + updateItem, getRandomNumber, getRandomString, getRandomBoolean, diff --git a/src/view/new-event-edit-element-view.js b/src/view/new-event-edit-element-view.js index 251d56a..4460bc8 100644 --- a/src/view/new-event-edit-element-view.js +++ b/src/view/new-event-edit-element-view.js @@ -169,7 +169,7 @@ export default class NewEventEditElementView extends AbstractView { #clickHandler = (evt) => { evt.preventDefault(); - this.#handleClick(); + this.#handleClick(this.#eventData); }; #submitHandler = (evt) => { diff --git a/src/view/new-events-item-view.js b/src/view/new-events-item-view.js index 7b33249..9d49da3 100644 --- a/src/view/new-events-item-view.js +++ b/src/view/new-events-item-view.js @@ -90,13 +90,17 @@ export default class NewEventsItemView extends AbstractView { #eventData = null; #handleClick = null; #rollupButton = null; + #handleFavoriteClick = null; - constructor ({userEvent, onClick}) { + constructor ({userEvent, onClick, onFavoriteClick}) { super(); this.#eventData = userEvent; this.#handleClick = onClick; + this.#handleFavoriteClick = onFavoriteClick; this.#rollupButton = this.element.querySelector('.event__rollup-btn'); this.#rollupButton.addEventListener('click', this.#clickHandler); + this.element.querySelector('.event__favorite-icon') + .addEventListener('click', this.#favoriteClickHandler); } get template () { @@ -112,4 +116,9 @@ export default class NewEventsItemView extends AbstractView { this.#rollupButton.removeEventListener('click', this.#clickHandler); } + #favoriteClickHandler = (evt) => { + evt.preventDefault(); + this.#handleFavoriteClick(); + }; + } From 6a69b9a2c97995cf5481cb658d429ac0d0fb1355 Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Wed, 9 Oct 2024 17:39:07 +0300 Subject: [PATCH 03/17] =?UTF-8?q?module5-task1=20-=20=D0=B3=D0=BE=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/presenter/board-presenter.js | 5 +++++ src/presenter/event-presenter.js | 23 ++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/presenter/board-presenter.js b/src/presenter/board-presenter.js index 23e6e06..a4d6973 100644 --- a/src/presenter/board-presenter.js +++ b/src/presenter/board-presenter.js @@ -31,6 +31,7 @@ export default class BoardPresenter { const eventPresenter = new EventPresenter({ container: this.#eventsListComponent.element, onDataChange: this.#handleEventChange, + onModeChange: this.#handleModeChange, }); eventPresenter.init(inputUserEvent); this.#eventPresenters.set(inputUserEvent.id, eventPresenter); @@ -60,6 +61,10 @@ export default class BoardPresenter { this.#eventPresenters.get(updatedEvent.id).init(updatedEvent); }; + #handleModeChange = () => { + this.#eventPresenters.forEach((presenter) => presenter.resetView()); + }; + #clearEventList() { this.#eventPresenters.forEach((presenter) => presenter.destroy()); this.#eventPresenters.clear(); diff --git a/src/presenter/event-presenter.js b/src/presenter/event-presenter.js index f3d67fa..482dc4a 100644 --- a/src/presenter/event-presenter.js +++ b/src/presenter/event-presenter.js @@ -3,18 +3,26 @@ import { isEscapeKey } from '../utils/common.js'; import NewEventsItemView from '../view/new-events-item-view.js'; import NewEventEditElementView from '../view/new-event-edit-element-view.js'; +const Mode = { + DEFAULT: 'DEFAULT', + EDITING: 'EDITING', +}; + export default class EventPresenter { #container = null; #handleDataChange = null; + #handleModeChange = null; #eventComponent = null; #eventEditComponent = null; #eventItem = null; + #mode = Mode.DEFAULT; - constructor ({container, onDataChange}) { + constructor ({container, onDataChange, onModeChange}) { this.#container = container; this.#handleDataChange = onDataChange; + this.#handleModeChange = onModeChange; } init (eventItem) { @@ -38,10 +46,10 @@ export default class EventPresenter { return; } - if (this.#container.contains(prevEventComponent.element)) { + if (this.#mode === Mode.DEFAULT) { replace (this.#eventComponent, prevEventComponent); } - if (this.#container.contains(prevEventEditComponent.element)) { + if (this.#mode === Mode.EDITING) { replace (this.#eventEditComponent, prevEventEditComponent); } remove(prevEventComponent); @@ -51,11 +59,14 @@ export default class EventPresenter { #replaceEventCardToEditForm() { replace(this.#eventEditComponent, this.#eventComponent); document.addEventListener('keydown', this.#escKeyDownHandler); + this.#handleModeChange(); + this.#mode = Mode.EDITING; } #replaceEditFormToEventCard() { replace(this.#eventComponent, this.#eventEditComponent); document.removeEventListener('keydown', this.#escKeyDownHandler); + this.#mode = Mode.DEFAULT; } destroy() { @@ -63,6 +74,12 @@ export default class EventPresenter { remove(this.#eventEditComponent); } + resetView() { + if (this.#mode !== Mode.DEFAULT) { + this.#replaceEditFormToEventCard(); + } + } + #escKeyDownHandler = (evt) => { if (isEscapeKey(evt)) { evt.preventDefault(); From fa6d23f3a42a6096721f398b7ed4bfe89dcb9c70 Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Wed, 9 Oct 2024 17:54:44 +0300 Subject: [PATCH 04/17] =?UTF-8?q?SortType=20-=20data=20=D0=B0=D1=82=D1=80?= =?UTF-8?q?=D0=B8=D0=B1=D1=83=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/const.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/const.js b/src/const.js index db07df0..a54e4c7 100644 --- a/src/const.js +++ b/src/const.js @@ -44,9 +44,16 @@ const FilterType = { PAST: 'past', }; +const SortType = { + DEFAULT: 'default', + PRICE: 'price', + TIME: 'time', +}; + export { EVENTS_TYPES, CITIES, SENTENCES, - FilterType + FilterType, + SortType }; From a6d283713b5054bd365d5537c5f50ecabc749810 Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Wed, 9 Oct 2024 18:49:49 +0300 Subject: [PATCH 05/17] =?UTF-8?q?=D0=B7=D0=B0=D0=B3=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D0=B0=20=D0=BF=D0=BE=D0=B4=20=D1=81=D0=BE=D1=80?= =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/presenter/board-presenter.js | 22 ++++++++++++++++++++-- src/view/new-sort-container-view.js | 25 ++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/presenter/board-presenter.js b/src/presenter/board-presenter.js index a4d6973..f6f21f0 100644 --- a/src/presenter/board-presenter.js +++ b/src/presenter/board-presenter.js @@ -10,7 +10,7 @@ export default class BoardPresenter { #container = null; #eventsModel = null; - #sortComponent = new NewTripSortView(); + #sortComponent = null; #eventsListComponent = new NewEventsListView(); #eventsList = []; @@ -41,8 +41,26 @@ export default class BoardPresenter { render (new NoEventsView(), this.#container); } - #renderBoard () { + #handleSortTypeChange = (sortType) => { + if (sortType === undefined) { + return; + } + console.log(sortType); + // - Сортируем задачи + // - Очищаем список + // - Рендерим список заново + }; + + #renderSort() { + this.#sortComponent = new NewTripSortView({ + onSortTypeChange: this.#handleSortTypeChange + }); + render(this.#sortComponent, this.#container); + } + + #renderBoard () { + this.#renderSort(); if (this.#eventsList.length === 0) { this.#renderNoEvents(); diff --git a/src/view/new-sort-container-view.js b/src/view/new-sort-container-view.js index 20bbcf9..d5fdcc8 100644 --- a/src/view/new-sort-container-view.js +++ b/src/view/new-sort-container-view.js @@ -1,10 +1,11 @@ import AbstractView from '../framework/view/abstract-view'; +import { SortType } from '../const'; function createNewTripSortTemplate() { return `
- +
@@ -14,12 +15,12 @@ function createNewTripSortTemplate() {
- +
- +
@@ -30,7 +31,25 @@ function createNewTripSortTemplate() { } export default class NewTripSortView extends AbstractView { + #handleSortTypeChange = null; + + constructor({onSortTypeChange}) { + super(); + this.#handleSortTypeChange = onSortTypeChange; + + this.element.addEventListener('click', this.#sortTypeChangeHandler); + } + get template () { return createNewTripSortTemplate(); } + + #sortTypeChangeHandler = (evt) => { + if (evt.target.tagName !== 'LABEL') { + return; + } + + evt.preventDefault(); + this.#handleSortTypeChange(evt.target.dataset.sortType); + }; } From 0e957e4baee8d65f8f9c29f761d51880bcff4807 Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Wed, 9 Oct 2024 21:10:16 +0300 Subject: [PATCH 06/17] =?UTF-8?q?=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20=D1=81?= =?UTF-8?q?=D0=BE=D1=80=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/events-connector.js | 2 +- src/presenter/board-presenter.js | 27 ++++++++++++++++++++++++--- src/utils/event.js | 18 +++++++++++++++++- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/model/events-connector.js b/src/model/events-connector.js index dbf5ee8..0cf3274 100644 --- a/src/model/events-connector.js +++ b/src/model/events-connector.js @@ -3,7 +3,7 @@ import { getRandomOffers } from '../mock/offers'; import { getRandomDestinations } from '../mock/destinations'; import { convertKeysToCamelCase } from '../utils/common'; -const EVENTS_COUNT = 3; +const EVENTS_COUNT = 5; const createUserEvents = () => { const offersList = convertKeysToCamelCase(getRandomOffers()); diff --git a/src/presenter/board-presenter.js b/src/presenter/board-presenter.js index f6f21f0..ef49e53 100644 --- a/src/presenter/board-presenter.js +++ b/src/presenter/board-presenter.js @@ -4,6 +4,8 @@ import NewTripSortView from '../view/new-sort-container-view.js'; import NewEventsListView from '../view/new-events-list-view.js'; import EventPresenter from './event-presenter.js'; import NoEventsView from '../view/no-events-view.js'; +import { SortType } from '../const.js'; +import { sortEventsPrice, sortEventsTime } from '../utils/event.js'; export default class BoardPresenter { @@ -15,6 +17,8 @@ export default class BoardPresenter { #eventsList = []; #eventPresenters = new Map(); + #currentSortType = SortType.DEFAULT; + #sourcedBoardEvents = []; constructor ({container, eventsModel}) { this.#container = container; @@ -23,7 +27,7 @@ export default class BoardPresenter { init () { this.#eventsList = [...this.#eventsModel.userEvents]; - + this.#sourcedBoardEvents = [...this.#eventsModel.userEvents]; this.#renderBoard(); } @@ -41,12 +45,28 @@ export default class BoardPresenter { render (new NoEventsView(), this.#container); } + #sortEvents(sortType) { + switch (sortType) { + case SortType.PRICE: + this.#eventsList.sort(sortEventsPrice); + break; + case SortType.TIME: + this.#eventsList.sort(sortEventsTime); + break; + default: + this.#eventsList = [...this.#sourcedBoardEvents]; + } + + this.#currentSortType = sortType; + } + #handleSortTypeChange = (sortType) => { - if (sortType === undefined) { + if (sortType === undefined || this.#currentSortType === sortType) { return; } - console.log(sortType); + // - Сортируем задачи + this.#sortEvents(sortType); // - Очищаем список // - Рендерим список заново }; @@ -76,6 +96,7 @@ export default class BoardPresenter { #handleEventChange = (updatedEvent) => { this.#eventsList = updateItem(this.#eventsList, updatedEvent); + this.#sourcedBoardEvents = updateItem(this.#sourcedBoardEvents, updatedEvent); this.#eventPresenters.get(updatedEvent.id).init(updatedEvent); }; diff --git a/src/utils/event.js b/src/utils/event.js index 781222b..50ed9f5 100644 --- a/src/utils/event.js +++ b/src/utils/event.js @@ -14,9 +14,25 @@ const isEventPresent = (dateFrom, dateTo) => { const isEventPast = (dateTo) => dateTo && dayjs().isAfter(dateTo, 'D'); +const sortEventsPrice = (eventA, eventB) => eventB.basePrice - eventA.basePrice; + +const sortEventsTime = (eventA, eventB) => { + const dateFromA = dayjs(eventA.dateFrom); + const dateToA = dayjs(eventA.dateTo); + const dateFromB = dayjs(eventB.dateFrom); + const dateToB = dayjs(eventB.dateTo); + + const durationA = dateToA.diff(dateFromA); + const durationB = dateToB.diff(dateFromB); + + return durationB - durationA; +}; + export { humanizeDueDate, isEventFuture, isEventPresent, - isEventPast + isEventPast, + sortEventsPrice, + sortEventsTime }; From 858a1c0b69ff1dceee7591fee7662015eb9542a3 Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Wed, 9 Oct 2024 23:25:16 +0300 Subject: [PATCH 07/17] =?UTF-8?q?=D1=81=D0=B2=D1=8F=D0=B7=D0=B0=D0=BB=20?= =?UTF-8?q?=D1=81=D0=BE=D1=80=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D1=83=20?= =?UTF-8?q?=D0=B8=20=D1=80=D0=B5=D0=BD=D0=B4=D0=B5=D1=80=D0=B8=D0=BD=D0=B3?= =?UTF-8?q?=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/presenter/board-presenter.js | 11 +++++------ src/view/new-event-edit-element-view.js | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/presenter/board-presenter.js b/src/presenter/board-presenter.js index ef49e53..b31f2b6 100644 --- a/src/presenter/board-presenter.js +++ b/src/presenter/board-presenter.js @@ -29,6 +29,7 @@ export default class BoardPresenter { this.#eventsList = [...this.#eventsModel.userEvents]; this.#sourcedBoardEvents = [...this.#eventsModel.userEvents]; this.#renderBoard(); + this.#renderSort(); } #renderEvent(inputUserEvent) { @@ -65,10 +66,10 @@ export default class BoardPresenter { return; } - // - Сортируем задачи this.#sortEvents(sortType); - // - Очищаем список - // - Рендерим список заново + + this.#clearEventList(); + this.#renderBoard(); }; #renderSort() { @@ -76,12 +77,10 @@ export default class BoardPresenter { onSortTypeChange: this.#handleSortTypeChange }); - render(this.#sortComponent, this.#container); + render(this.#sortComponent, this.#container, 'AFTERBEGIN'); } #renderBoard () { - this.#renderSort(); - if (this.#eventsList.length === 0) { this.#renderNoEvents(); return; diff --git a/src/view/new-event-edit-element-view.js b/src/view/new-event-edit-element-view.js index 4460bc8..1ae4a5e 100644 --- a/src/view/new-event-edit-element-view.js +++ b/src/view/new-event-edit-element-view.js @@ -174,7 +174,7 @@ export default class NewEventEditElementView extends AbstractView { #submitHandler = (evt) => { evt.preventDefault(); - this.#handleClick(); + this.#handleClick(this.#eventData); }; removeEventListeners() { From 47197b695342f5784ac71e9aeac6236de6c9b2b6 Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Fri, 11 Oct 2024 15:06:31 +0300 Subject: [PATCH 08/17] 6.1.1 --- src/view/new-event-edit-element-view.js | 30 +++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/view/new-event-edit-element-view.js b/src/view/new-event-edit-element-view.js index 1ae4a5e..4f615d1 100644 --- a/src/view/new-event-edit-element-view.js +++ b/src/view/new-event-edit-element-view.js @@ -1,4 +1,4 @@ -import AbstractView from '../framework/view/abstract-view'; +import AbstractStatefulView from '../framework/view/abstract-stateful-view'; import { humanizeDueDate } from '../utils/event'; const TIME_PATTERN = 'DD/MM/YY hh:mm'; @@ -24,9 +24,8 @@ const createOffers = (offers) => { }; const createNewEventEditElementTemplate = (eventData) => { - const {basePrice, dateFrom, dateTo, type, offers, destination} = eventData; - const dateStart = humanizeDueDate(dateFrom, TIME_PATTERN); - const dateEnd = humanizeDueDate(dateTo, TIME_PATTERN); + const {basePrice, type, offers, destination, dateStart, dateEnd} = eventData; + return `
  • @@ -141,7 +140,7 @@ const createNewEventEditElementTemplate = (eventData) => {
  • `; }; -export default class NewEventEditElementView extends AbstractView { +export default class NewEventEditElementView extends AbstractStatefulView { #eventData = null; #handleClick = null; #rollupButton = null; @@ -152,6 +151,7 @@ export default class NewEventEditElementView extends AbstractView { constructor ({userEvent, onClick, onSubmit}) { super(); this.#eventData = userEvent; + this._setState(NewEventEditElementView.parseEventDataToState(userEvent)); this.#handleClick = onClick; this.#handleSubmit = onSubmit; @@ -164,7 +164,7 @@ export default class NewEventEditElementView extends AbstractView { } get template () { - return createNewEventEditElementTemplate(this.#eventData); + return createNewEventEditElementTemplate(this._state); } #clickHandler = (evt) => { @@ -174,11 +174,27 @@ export default class NewEventEditElementView extends AbstractView { #submitHandler = (evt) => { evt.preventDefault(); - this.#handleClick(this.#eventData); + this.#handleClick(NewEventEditElementView.parseStateToEventData(this._state)); }; removeEventListeners() { this.#rollupButton.removeEventListener('click', this.#clickHandler); this.#formElement.removeEventListener('submit', this.#submitHandler); } + + static parseEventDataToState(eventData) { + return {...eventData, + dateStart: humanizeDueDate(eventData.dateFrom, TIME_PATTERN), + dateEnd: humanizeDueDate(eventData.dateTo, TIME_PATTERN), + }; + } + + static parseStateToEventData(state) { + const eventData = {...state}; + + delete eventData.dateStart; + delete eventData.dateEnd; + + return eventData; + } } From 577fb013212bbcce6de00bf5ffd200c3bf5f5023 Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Fri, 11 Oct 2024 19:33:58 +0300 Subject: [PATCH 09/17] =?UTF-8?q?=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D1=81=D0=BA=D0=B8=D1=85=20?= =?UTF-8?q?=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/view/new-event-edit-element-view.js | 129 +++++++++++++++--------- 1 file changed, 79 insertions(+), 50 deletions(-) diff --git a/src/view/new-event-edit-element-view.js b/src/view/new-event-edit-element-view.js index 4f615d1..7520347 100644 --- a/src/view/new-event-edit-element-view.js +++ b/src/view/new-event-edit-element-view.js @@ -1,5 +1,6 @@ import AbstractStatefulView from '../framework/view/abstract-stateful-view'; import { humanizeDueDate } from '../utils/event'; +import { EVENTS_TYPES } from '../const'; const TIME_PATTERN = 'DD/MM/YY hh:mm'; @@ -23,6 +24,22 @@ const createOffers = (offers) => { return offersHTML; }; +const createType = (type) => { + const typeToLowerCase = type.toLowerCase(); + return `
    + + +
    `; +}; + +const createTypes = (types) => { + let typesHTML = ''; + types.forEach((type) => { + typesHTML += createType(type); + }); + return typesHTML; +}; + const createNewEventEditElementTemplate = (eventData) => { const {basePrice, type, offers, destination, dateStart, dateEnd} = eventData; @@ -39,51 +56,7 @@ const createNewEventEditElementTemplate = (eventData) => {
    Event type - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    + ${createTypes(EVENTS_TYPES)}
    @@ -92,7 +65,7 @@ const createNewEventEditElementTemplate = (eventData) => { - + @@ -141,6 +114,7 @@ const createNewEventEditElementTemplate = (eventData) => { }; export default class NewEventEditElementView extends AbstractStatefulView { + #form = null; #eventData = null; #handleClick = null; #rollupButton = null; @@ -156,17 +130,36 @@ export default class NewEventEditElementView extends AbstractStatefulView { this.#handleClick = onClick; this.#handleSubmit = onSubmit; - this.#rollupButton = this.element.querySelector('.event__rollup-btn'); - this.#rollupButton.addEventListener('click', this.#clickHandler); + this._restoreHandlers(); - this.#formElement = this.element.querySelector('.event--edit'); - this.#formElement.addEventListener('submit', this.#submitHandler); } get template () { return createNewEventEditElementTemplate(this._state); } + _restoreHandlers() { + this.#rollupButton = this.element.querySelector('.event__rollup-btn'); + this.#formElement = this.element.querySelector('.event--edit'); + this.#formElement.addEventListener('submit', this.#submitHandler); + this.#rollupButton.addEventListener('click', this.#clickHandler); + + this.element.querySelector('#event-price-1') + .addEventListener('input', this.#eventPriceToggleHandler); + this.element.querySelector('.event__input--destination') + .addEventListener('input', this.#eventDestinationToggleHandler); + + this.element.querySelector('#event-start-time-1') + .addEventListener('input', this.#eventStartTimeToggleHandler); + this.element.querySelector('#event-end-time-1') + .addEventListener('input', this.#eventEndTimeToggleHandler); + this.element.querySelectorAll('.event__type-label') + .forEach((label) => { + label.addEventListener('click', this.#eventEventTypeToggleHandler); + }); + + } + #clickHandler = (evt) => { evt.preventDefault(); this.#handleClick(this.#eventData); @@ -197,4 +190,40 @@ export default class NewEventEditElementView extends AbstractStatefulView { return eventData; } + + #eventPriceToggleHandler = (evt) => { + evt.preventDefault(); + this._setState({ + basePrice: evt.target.value, + }); + }; + + #eventDestinationToggleHandler = (evt) => { + evt.preventDefault(); + this._setState({ + destination: evt.target.value, + }); + }; + + #eventStartTimeToggleHandler = (evt) => { + evt.preventDefault(); + this._setState({ + dateStart: evt.target.value, + }); + }; + + #eventEndTimeToggleHandler = (evt) => { + evt.preventDefault(); + this._setState({ + dateEnd: evt.target.value, + }); + }; + + #eventEventTypeToggleHandler = (evt) => { + evt.preventDefault(); + this.updateElement({ + type: evt.target.dataset.eventType, + }); + }; + } From f7bd16a3709e35ae87369f81982a5a3876c5d99b Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Sat, 12 Oct 2024 19:39:16 +0300 Subject: [PATCH 10/17] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=20=D0=B3=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/events-connector.js | 18 +++++---- src/model/events-model.js | 5 +++ src/presenter/event-presenter.js | 1 + src/view/new-event-edit-element-view.js | 53 ++++++++++++++++++++----- 4 files changed, 60 insertions(+), 17 deletions(-) diff --git a/src/model/events-connector.js b/src/model/events-connector.js index 0cf3274..5b09cf1 100644 --- a/src/model/events-connector.js +++ b/src/model/events-connector.js @@ -3,13 +3,9 @@ import { getRandomOffers } from '../mock/offers'; import { getRandomDestinations } from '../mock/destinations'; import { convertKeysToCamelCase } from '../utils/common'; -const EVENTS_COUNT = 5; - -const createUserEvents = () => { - const offersList = convertKeysToCamelCase(getRandomOffers()); - const destinationsList = convertKeysToCamelCase(getRandomDestinations()); - const eventsList = convertKeysToCamelCase(getRandomEvents(EVENTS_COUNT, destinationsList)); +const EVENTS_COUNT = 6; +const createUserEvents = (offersList, destinationsList, eventsList) => { const destinationsMap = new Map(destinationsList.map((destination) => [destination.id, destination])); const offers = offersList[0].offers; @@ -36,7 +32,15 @@ const createUserEvents = () => { }; export default class EventsConnector { + #offersList = convertKeysToCamelCase(getRandomOffers()); + #destinationsList = convertKeysToCamelCase(getRandomDestinations()); + #eventsList = convertKeysToCamelCase(getRandomEvents(EVENTS_COUNT, this.#destinationsList)); + get userEvents () { - return structuredClone(createUserEvents()); + return structuredClone(createUserEvents(this.#offersList, this.#destinationsList, this.#eventsList)); + } + + get destinationsList () { + return this.#destinationsList; } } diff --git a/src/model/events-model.js b/src/model/events-model.js index 37389d1..c98a0b7 100644 --- a/src/model/events-model.js +++ b/src/model/events-model.js @@ -3,8 +3,13 @@ import EventsConnector from './events-connector'; export default class EventsModel { #EventsConnector = new EventsConnector; #eventsList = this.#EventsConnector.userEvents; + #destinationsList = this.#EventsConnector.destinationsList; get userEvents () { return this.#eventsList; } + + get destinationsList () { + return this.#destinationsList; + } } diff --git a/src/presenter/event-presenter.js b/src/presenter/event-presenter.js index 482dc4a..baae92f 100644 --- a/src/presenter/event-presenter.js +++ b/src/presenter/event-presenter.js @@ -76,6 +76,7 @@ export default class EventPresenter { resetView() { if (this.#mode !== Mode.DEFAULT) { + this.#eventEditComponent.reset(this.#eventItem); this.#replaceEditFormToEventCard(); } } diff --git a/src/view/new-event-edit-element-view.js b/src/view/new-event-edit-element-view.js index 7520347..6b6aaf4 100644 --- a/src/view/new-event-edit-element-view.js +++ b/src/view/new-event-edit-element-view.js @@ -1,6 +1,7 @@ import AbstractStatefulView from '../framework/view/abstract-stateful-view'; import { humanizeDueDate } from '../utils/event'; import { EVENTS_TYPES } from '../const'; +import EventsModel from '../model/events-model'; const TIME_PATTERN = 'DD/MM/YY hh:mm'; @@ -40,7 +41,21 @@ const createTypes = (types) => { return typesHTML; }; -const createNewEventEditElementTemplate = (eventData) => { +const createDestination = (destination) => ``; + +const createDestinations = (destinationsList) => { + if (!Array.isArray(destinationsList)) { + return; + } + + let destinationsHTML = ''; + destinationsList.forEach((destination) => { + destinationsHTML += createDestination(destination); + }); + return destinationsHTML; +}; + +const createNewEventEditElementTemplate = (eventData, destinationsList) => { const {basePrice, type, offers, destination, dateStart, dateEnd} = eventData; return `
  • @@ -67,9 +82,7 @@ const createNewEventEditElementTemplate = (eventData) => { - - - + ${createDestinations(destinationsList)}
  • @@ -114,10 +127,11 @@ const createNewEventEditElementTemplate = (eventData) => { }; export default class NewEventEditElementView extends AbstractStatefulView { - #form = null; + #EventsModel = new EventsModel; #eventData = null; #handleClick = null; #rollupButton = null; + #destinationsList = null; #handleSubmit = null; #formElement = null; @@ -125,6 +139,7 @@ export default class NewEventEditElementView extends AbstractStatefulView { constructor ({userEvent, onClick, onSubmit}) { super(); this.#eventData = userEvent; + this.#destinationsList = this.#EventsModel.destinationsList; this._setState(NewEventEditElementView.parseEventDataToState(userEvent)); this.#handleClick = onClick; @@ -135,7 +150,13 @@ export default class NewEventEditElementView extends AbstractStatefulView { } get template () { - return createNewEventEditElementTemplate(this._state); + return createNewEventEditElementTemplate(this._state, this.#destinationsList); + } + + reset (eventData) { + this.updateElement( + NewEventEditElementView.parseEventDataToState(eventData), + ); } _restoreHandlers() { @@ -147,7 +168,7 @@ export default class NewEventEditElementView extends AbstractStatefulView { this.element.querySelector('#event-price-1') .addEventListener('input', this.#eventPriceToggleHandler); this.element.querySelector('.event__input--destination') - .addEventListener('input', this.#eventDestinationToggleHandler); + .addEventListener('change', this.#eventDestinationToggleHandler); this.element.querySelector('#event-start-time-1') .addEventListener('input', this.#eventStartTimeToggleHandler); @@ -199,9 +220,21 @@ export default class NewEventEditElementView extends AbstractStatefulView { }; #eventDestinationToggleHandler = (evt) => { - evt.preventDefault(); - this._setState({ - destination: evt.target.value, + const inputValue = evt.target.value; + const options = document.querySelectorAll('#destination-list-1 option'); + + options.forEach((option) => { + if (option.value === inputValue) { + const cityId = option.getAttribute('data-destination-id'); + + const selectedDestination = this.#destinationsList.find((destination) => destination.id === cityId); + + if (selectedDestination) { + this.updateElement({ + destination: selectedDestination, + }); + } + } }); }; From f73bca48a9911f0e18f233e1bde01e7faf37ee72 Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Sun, 13 Oct 2024 18:49:20 +0300 Subject: [PATCH 11/17] =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=82=D1=8F=D0=BD?= =?UTF-8?q?=D1=83=D0=BB=20=D0=BF=D0=BE=D0=B8=D1=81=D0=BA=20=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BE=20=D0=B3=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=20=D0=B8=D0=B7=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=B2=D0=BE=20=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/events-model.js | 6 +++--- src/presenter/board-presenter.js | 6 ++++++ src/presenter/event-presenter.js | 8 +++++++- src/view/new-event-edit-element-view.js | 15 +++++++-------- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/model/events-model.js b/src/model/events-model.js index c98a0b7..8a10d0d 100644 --- a/src/model/events-model.js +++ b/src/model/events-model.js @@ -9,7 +9,7 @@ export default class EventsModel { return this.#eventsList; } - get destinationsList () { - return this.#destinationsList; - } + getDestinationsData = () => this.#destinationsList; + findDestinationData = (destinationId) => this.#destinationsList.find((destination) => destination.id === destinationId); + } diff --git a/src/presenter/board-presenter.js b/src/presenter/board-presenter.js index b31f2b6..3e5da9a 100644 --- a/src/presenter/board-presenter.js +++ b/src/presenter/board-presenter.js @@ -19,10 +19,14 @@ export default class BoardPresenter { #eventPresenters = new Map(); #currentSortType = SortType.DEFAULT; #sourcedBoardEvents = []; + #findDestinationData = null; + #getDestinationsData = null; constructor ({container, eventsModel}) { this.#container = container; this.#eventsModel = eventsModel; + this.#findDestinationData = this.#eventsModel.findDestinationData; + this.#getDestinationsData = this.#eventsModel.getDestinationsData; } init () { @@ -37,6 +41,8 @@ export default class BoardPresenter { container: this.#eventsListComponent.element, onDataChange: this.#handleEventChange, onModeChange: this.#handleModeChange, + findDestinationData: this.#findDestinationData, + getDestinationsList: this.#getDestinationsData, }); eventPresenter.init(inputUserEvent); this.#eventPresenters.set(inputUserEvent.id, eventPresenter); diff --git a/src/presenter/event-presenter.js b/src/presenter/event-presenter.js index baae92f..6918ec3 100644 --- a/src/presenter/event-presenter.js +++ b/src/presenter/event-presenter.js @@ -12,6 +12,8 @@ export default class EventPresenter { #container = null; #handleDataChange = null; #handleModeChange = null; + #findDestinationData = null; + #getDestinationsList = null; #eventComponent = null; #eventEditComponent = null; @@ -19,10 +21,12 @@ export default class EventPresenter { #eventItem = null; #mode = Mode.DEFAULT; - constructor ({container, onDataChange, onModeChange}) { + constructor ({container, onDataChange, onModeChange, findDestinationData, getDestinationsList}) { this.#container = container; this.#handleDataChange = onDataChange; this.#handleModeChange = onModeChange; + this.#findDestinationData = findDestinationData; + this.#getDestinationsList = getDestinationsList; } init (eventItem) { @@ -39,6 +43,8 @@ export default class EventPresenter { this.#eventEditComponent = new NewEventEditElementView({ userEvent: this.#eventItem, onClick: this.#handleSaveClick, + findDestination: this.#findDestinationData, + getDestinationsData: this.#getDestinationsList, }); if (prevEventComponent === null || prevEventEditComponent === null) { diff --git a/src/view/new-event-edit-element-view.js b/src/view/new-event-edit-element-view.js index 6b6aaf4..ee96144 100644 --- a/src/view/new-event-edit-element-view.js +++ b/src/view/new-event-edit-element-view.js @@ -1,7 +1,6 @@ import AbstractStatefulView from '../framework/view/abstract-stateful-view'; import { humanizeDueDate } from '../utils/event'; import { EVENTS_TYPES } from '../const'; -import EventsModel from '../model/events-model'; const TIME_PATTERN = 'DD/MM/YY hh:mm'; @@ -127,19 +126,19 @@ const createNewEventEditElementTemplate = (eventData, destinationsList) => { }; export default class NewEventEditElementView extends AbstractStatefulView { - #EventsModel = new EventsModel; #eventData = null; #handleClick = null; #rollupButton = null; - #destinationsList = null; + #getDestinationsData = null; #handleSubmit = null; #formElement = null; - constructor ({userEvent, onClick, onSubmit}) { + constructor ({userEvent, onClick, findDestination, getDestinationsData, onSubmit}) { super(); this.#eventData = userEvent; - this.#destinationsList = this.#EventsModel.destinationsList; + this.findDestinationData = findDestination; + this.#getDestinationsData = getDestinationsData; this._setState(NewEventEditElementView.parseEventDataToState(userEvent)); this.#handleClick = onClick; @@ -150,7 +149,7 @@ export default class NewEventEditElementView extends AbstractStatefulView { } get template () { - return createNewEventEditElementTemplate(this._state, this.#destinationsList); + return createNewEventEditElementTemplate(this._state, this.#getDestinationsData()); } reset (eventData) { @@ -168,7 +167,7 @@ export default class NewEventEditElementView extends AbstractStatefulView { this.element.querySelector('#event-price-1') .addEventListener('input', this.#eventPriceToggleHandler); this.element.querySelector('.event__input--destination') - .addEventListener('change', this.#eventDestinationToggleHandler); + .addEventListener('input', this.#eventDestinationToggleHandler); this.element.querySelector('#event-start-time-1') .addEventListener('input', this.#eventStartTimeToggleHandler); @@ -227,7 +226,7 @@ export default class NewEventEditElementView extends AbstractStatefulView { if (option.value === inputValue) { const cityId = option.getAttribute('data-destination-id'); - const selectedDestination = this.#destinationsList.find((destination) => destination.id === cityId); + const selectedDestination = this.findDestinationData(cityId); if (selectedDestination) { this.updateElement({ From ad12ad8a16bb709ba7233cb5f7604c3cd1994f38 Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Sun, 13 Oct 2024 20:45:24 +0300 Subject: [PATCH 12/17] =?UTF-8?q?=D1=83=D1=81=D1=82=D0=B0=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=B8=D0=BB=20flatpickr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/view/new-event-edit-element-view.js | 80 +++++++++++++++++-------- src/view/new-events-item-view.js | 2 +- 2 files changed, 56 insertions(+), 26 deletions(-) diff --git a/src/view/new-event-edit-element-view.js b/src/view/new-event-edit-element-view.js index ee96144..9c8ffcb 100644 --- a/src/view/new-event-edit-element-view.js +++ b/src/view/new-event-edit-element-view.js @@ -1,8 +1,8 @@ import AbstractStatefulView from '../framework/view/abstract-stateful-view'; -import { humanizeDueDate } from '../utils/event'; import { EVENTS_TYPES } from '../const'; +import flatpickr from 'flatpickr'; +import 'flatpickr/dist/flatpickr.min.css'; -const TIME_PATTERN = 'DD/MM/YY hh:mm'; const createNewOffer = (offer) => { const {title, price} = offer; @@ -55,7 +55,7 @@ const createDestinations = (destinationsList) => { }; const createNewEventEditElementTemplate = (eventData, destinationsList) => { - const {basePrice, type, offers, destination, dateStart, dateEnd} = eventData; + const {basePrice, type, offers, destination, dateFrom, dateTo} = eventData; return `
  • @@ -87,10 +87,10 @@ const createNewEventEditElementTemplate = (eventData, destinationsList) => {
    - + - +
    @@ -130,6 +130,7 @@ export default class NewEventEditElementView extends AbstractStatefulView { #handleClick = null; #rollupButton = null; #getDestinationsData = null; + #datepicker = null; #handleSubmit = null; #formElement = null; @@ -152,6 +153,15 @@ export default class NewEventEditElementView extends AbstractStatefulView { return createNewEventEditElementTemplate(this._state, this.#getDestinationsData()); } + removeElement() { + super.removeElement(); + + if (this.#datepicker) { + this.#datepicker.destroy(); + this.#datepicker = null; + } + } + reset (eventData) { this.updateElement( NewEventEditElementView.parseEventDataToState(eventData), @@ -169,8 +179,6 @@ export default class NewEventEditElementView extends AbstractStatefulView { this.element.querySelector('.event__input--destination') .addEventListener('input', this.#eventDestinationToggleHandler); - this.element.querySelector('#event-start-time-1') - .addEventListener('input', this.#eventStartTimeToggleHandler); this.element.querySelector('#event-end-time-1') .addEventListener('input', this.#eventEndTimeToggleHandler); this.element.querySelectorAll('.event__type-label') @@ -178,6 +186,7 @@ export default class NewEventEditElementView extends AbstractStatefulView { label.addEventListener('click', this.#eventEventTypeToggleHandler); }); + this.#setDatepicker(); } #clickHandler = (evt) => { @@ -195,22 +204,50 @@ export default class NewEventEditElementView extends AbstractStatefulView { this.#formElement.removeEventListener('submit', this.#submitHandler); } - static parseEventDataToState(eventData) { - return {...eventData, - dateStart: humanizeDueDate(eventData.dateFrom, TIME_PATTERN), - dateEnd: humanizeDueDate(eventData.dateTo, TIME_PATTERN), - }; - } + #setDatepicker() { + const timeInputs = this.element.querySelectorAll('.event__input--time'); - static parseStateToEventData(state) { - const eventData = {...state}; + timeInputs.forEach((input, index) => { + let date = ''; + if (index) { + date = this._state.dateTo; + } else { + date = this._state.dateFrom; + } + this.#datepicker = flatpickr(input, { + enableTime: true, + dateFormat: 'd/m/y H:i', + defaultDate: date, + onChange: (selectedDates) => this.#eventStartTimeToggleHandler(selectedDates, index), + }); + if (index) { + this.#datepicker.set('minDate', this._state.dateTo); + } else { + this.#datepicker.set('maxDate', this._state.dateFrom); + } + }); + } - delete eventData.dateStart; - delete eventData.dateEnd; + #eventStartTimeToggleHandler = ([userDate], index) => { + if (index) { + this.updateElement({ + dateTo: userDate, + }); + } else { + this.updateElement({ + dateFrom: userDate, + }); + } + }; + static parseEventDataToState(eventData) { return eventData; } + static parseStateToEventData(state) { + return state; + } + #eventPriceToggleHandler = (evt) => { evt.preventDefault(); this._setState({ @@ -237,16 +274,9 @@ export default class NewEventEditElementView extends AbstractStatefulView { }); }; - #eventStartTimeToggleHandler = (evt) => { - evt.preventDefault(); - this._setState({ - dateStart: evt.target.value, - }); - }; - #eventEndTimeToggleHandler = (evt) => { evt.preventDefault(); - this._setState({ + this.updateElement({ dateEnd: evt.target.value, }); }; diff --git a/src/view/new-events-item-view.js b/src/view/new-events-item-view.js index 9d49da3..a4ac1cc 100644 --- a/src/view/new-events-item-view.js +++ b/src/view/new-events-item-view.js @@ -2,7 +2,7 @@ import dayjs from 'dayjs'; import AbstractView from '../framework/view/abstract-view'; import { humanizeDueDate } from '../utils/event'; -const TIME_PATTERN = 'hh:mm'; +const TIME_PATTERN = 'HH:mm'; const HUMANIZED_EVENT_DATE_PATTERN = 'MMM DD'; From fec8a726f0cbe59063d085e7babe6d8c524b85a2 Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Sun, 13 Oct 2024 21:01:39 +0300 Subject: [PATCH 13/17] =?UTF-8?q?=D0=BE=D0=B3=D1=80=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=87=D0=B8=D0=BB=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=20=D0=B4?= =?UTF-8?q?=D0=B0=D1=82,=20=D1=87=D1=82=D0=BE=20=D0=B1=D1=8B=20=D0=BD?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=B7=D1=8F=20=D0=B1=D1=8B=D0=BB=D0=BE=20?= =?UTF-8?q?=D0=B2=D1=8B=D0=B1=D1=80=D0=B0=D1=82=D1=8C=20=D0=B4=D0=B0=D1=82?= =?UTF-8?q?=D1=83=20=D0=BD=D0=B0=D1=87=D0=B0=D0=BB=D0=B0=20=D0=B1=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D1=88=D0=B5=20=D1=87=D0=B5=D0=BC=20=D0=B4=D0=B0?= =?UTF-8?q?=D1=82=D0=B0=20=D0=BE=D0=BA=D0=BE=D0=BD=D1=87=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B8=20=D0=BD=D0=B0=D0=BE=D0=B1=D0=BE=D1=80=D0=BE?= =?UTF-8?q?=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/view/new-event-edit-element-view.js | 42 +++++++++++++------------ 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/view/new-event-edit-element-view.js b/src/view/new-event-edit-element-view.js index 9c8ffcb..2194b00 100644 --- a/src/view/new-event-edit-element-view.js +++ b/src/view/new-event-edit-element-view.js @@ -206,39 +206,41 @@ export default class NewEventEditElementView extends AbstractStatefulView { #setDatepicker() { const timeInputs = this.element.querySelectorAll('.event__input--time'); + this.datepickers = []; timeInputs.forEach((input, index) => { - let date = ''; - if (index) { - date = this._state.dateTo; - } else { - date = this._state.dateFrom; - } - this.#datepicker = flatpickr(input, { + const date = index ? this._state.dateTo : this._state.dateFrom; + const options = { enableTime: true, dateFormat: 'd/m/y H:i', defaultDate: date, onChange: (selectedDates) => this.#eventStartTimeToggleHandler(selectedDates, index), - }); - if (index) { - this.#datepicker.set('minDate', this._state.dateTo); - } else { - this.#datepicker.set('maxDate', this._state.dateFrom); - } + }; + + const datepickerInstance = flatpickr(input, options); + this.datepickers[index] = datepickerInstance; + + this.#updateDatePickerConstraints(index); }); } #eventStartTimeToggleHandler = ([userDate], index) => { + this.updateElement({ + [index ? 'dateTo' : 'dateFrom']: userDate, + }); + this.#updateDatePickerConstraints(index); + }; + + #updateDatePickerConstraints(index) { + const minDate = this._state.dateFrom; + const maxDate = this._state.dateTo; + if (index) { - this.updateElement({ - dateTo: userDate, - }); + this.datepickers[index].set('minDate', minDate); } else { - this.updateElement({ - dateFrom: userDate, - }); + this.datepickers[index].set('maxDate', maxDate); } - }; + } static parseEventDataToState(eventData) { return eventData; From f1c73597e79aff04a88656b376a00d0790a91bcc Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Sun, 13 Oct 2024 23:44:49 +0300 Subject: [PATCH 14/17] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B5=D1=89=D0=B5=20=D0=B1=D0=BE=D0=BB=D0=B5=D0=B5=20?= =?UTF-8?q?=D1=81=D0=BB=D1=83=D1=87=D0=B0=D0=B9=D0=BD=D1=83=D1=8E=20=D0=B3?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D1=8E=20=D0=BE=D1=84?= =?UTF-8?q?=D1=84=D0=B5=D1=80=D0=BE=D0=B2=20=D0=B8=20=D0=B8=D1=81=D0=BF?= =?UTF-8?q?=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=BA=D0=BE=D0=BD=D0=BD=D0=B5?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mock/events.js | 29 ++++++++---- src/mock/offers.js | 36 ++++++++------- src/model/events-connector.js | 87 ++++++++++++++++++++++------------- 3 files changed, 94 insertions(+), 58 deletions(-) diff --git a/src/mock/events.js b/src/mock/events.js index 1585b6b..725d88a 100644 --- a/src/mock/events.js +++ b/src/mock/events.js @@ -4,11 +4,25 @@ import {nanoid} from 'nanoid'; const TIME_SKIP = 125; -const getRandomEvent = (date, destinationsList) => { +const getOfferIdsByType = (offersMap, type) => { + const offerIds = []; + + if (offersMap.has(type)) { + const offersById = offersMap.get(type); + + offersById.forEach((_, id) => { + offerIds.push(id); + }); + } + return offerIds; +}; + +const getRandomEvent = (date, destinationsList, offersMap) => { const firstDate = new Date(date); const secondDate = new Date(firstDate); const destinationsIds = destinationsList.map((destination) => destination.id); secondDate.setMinutes(firstDate.getMinutes() + TIME_SKIP); + const randomType = getRandomArrayElement(EVENTS_TYPES).toLowerCase(); const randomEvent = { 'id': nanoid(), 'base_price': getRandomNumber(499, 4999), @@ -16,22 +30,17 @@ const getRandomEvent = (date, destinationsList) => { 'date_to': secondDate.toISOString(), 'destination': getRandomArrayElement(destinationsIds), 'is_favorite': getRandomBoolean(), - 'offers': [ - '04c3e4e6-9053-42ce-b747-e281314baa31', - '14c3e4e6-9053-42ce-b747-e281314baa31', - '24c3e4e6-9053-42ce-b747-e281314baa31', - '34c3e4e6-9053-42ce-b747-e281314baa31' - ], - 'type': getRandomArrayElement(EVENTS_TYPES) + 'offers': getOfferIdsByType(offersMap, randomType), + 'type': randomType, }; return randomEvent; }; -const getRandomEvents = (count, destinationsList) => { +const getRandomEvents = (count, destinationsList, offersMap) => { const date = getRandomDate(); const events = []; for (let i = 0; i < count; i++) { - events.push(getRandomEvent(date, destinationsList)); + events.push(getRandomEvent(date, destinationsList, offersMap)); date.setMinutes(date.getMinutes() + TIME_SKIP); } return events; diff --git a/src/mock/offers.js b/src/mock/offers.js index 8ae2f33..c7500da 100644 --- a/src/mock/offers.js +++ b/src/mock/offers.js @@ -1,27 +1,31 @@ -import { getRandomNumber} from '../utils/common'; +import { getRandomNumber } from '../utils/common'; +import { nanoid } from 'nanoid'; +import { EVENTS_TYPES } from '../const'; -const OFFERS_COUNT = 4; +const OFFERS_COUNT = 2; -const getRandomOffer = (id) => { +const getRandomOffer = (index) => { const offer = { - 'id': `${id }4c3e4e6-9053-42ce-b747-e281314baa31`, - 'title': `Upgrade ${ id}`, - 'price': getRandomNumber(19,499) + 'id': nanoid(), + 'title': `Upgrade ${index}`, + 'price': getRandomNumber(19, 499) }; return offer; }; -const getRandomOffers = (type) => { - const randomOffers = [ - { - 'type': type, - 'offers': [] - } - ]; - - for (let i = 0; i < OFFERS_COUNT; i ++) { - randomOffers[0].offers.push(getRandomOffer(i)); +const getRandomOffersOfSameType = (type) => { + const offers = []; + for (let i = 0; i < OFFERS_COUNT; i++) { + offers.push(getRandomOffer(i)); } + return { + 'type': type.toLowerCase(), + 'offers': offers + }; +}; + +const getRandomOffers = () => { + const randomOffers = EVENTS_TYPES.map((type) => getRandomOffersOfSameType(type)); return randomOffers; }; diff --git a/src/model/events-connector.js b/src/model/events-connector.js index 5b09cf1..f9db343 100644 --- a/src/model/events-connector.js +++ b/src/model/events-connector.js @@ -3,44 +3,67 @@ import { getRandomOffers } from '../mock/offers'; import { getRandomDestinations } from '../mock/destinations'; import { convertKeysToCamelCase } from '../utils/common'; -const EVENTS_COUNT = 6; - -const createUserEvents = (offersList, destinationsList, eventsList) => { - const destinationsMap = new Map(destinationsList.map((destination) => [destination.id, destination])); - const offers = offersList[0].offers; - - const mergedEvents = eventsList.map((event) => { - const destinationData = destinationsMap.get(event.destination); - - const offersData = event.offers - .map((offerId) => offers.find((offer) => offer.id === offerId)) - .filter(Boolean); - - return { - ...event, - destination: { - id: destinationData.id, - name: destinationData.name, - description: destinationData.description, - pictures: destinationData.pictures, - }, - offers: offersData - }; - }); - - return Array.from(new Map(mergedEvents.map((event) => [event.id, event])).values()); -}; +const EVENTS_COUNT = 5; export default class EventsConnector { - #offersList = convertKeysToCamelCase(getRandomOffers()); #destinationsList = convertKeysToCamelCase(getRandomDestinations()); - #eventsList = convertKeysToCamelCase(getRandomEvents(EVENTS_COUNT, this.#destinationsList)); + #offersMap = this.#createOffersMap(convertKeysToCamelCase(getRandomOffers())); + #eventsList = convertKeysToCamelCase(getRandomEvents(EVENTS_COUNT, this.#destinationsList, this.#offersMap)); - get userEvents () { - return structuredClone(createUserEvents(this.#offersList, this.#destinationsList, this.#eventsList)); + #createOffersMap(data) { + const offersMap = new Map(); + data.forEach((item) => { + const { type, offers } = item; + const offersById = new Map(); + + offers.forEach((offer) => { + const { id, title, price } = offer; + offersById.set(id, { title, price }); + }); + + offersMap.set(type, offersById); + }); + + return offersMap; + } + + #getOfferById(type, id) { + const offersById = this.#offersMap.get(type); + const offer = offersById.get(id); + const { title, price } = offer; + return { id, title, price }; + } + + #createUserEvents() { + const destinationsMap = new Map(this.#destinationsList.map((destination) => [destination.id, destination])); + + const mergedEvents = this.#eventsList.map((event) => { + const destinationData = destinationsMap.get(event.destination); + + const offersData = event.offers + .map((offerId) => this.#getOfferById(event.type, offerId)) + .filter(Boolean); + + return { + ...event, + destination: { + id: destinationData.id, + name: destinationData.name, + description: destinationData.description, + pictures: destinationData.pictures, + }, + offers: offersData + }; + }); + + return Array.from(new Map(mergedEvents.map((event) => [event.id, event])).values()); + } + + get userEvents() { + return structuredClone(this.#createUserEvents()); } - get destinationsList () { + get destinationsList() { return this.#destinationsList; } } From 9239f6802702ff2269ae56644c5bcc3d8f8b742c Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Tue, 15 Oct 2024 18:11:29 +0300 Subject: [PATCH 15/17] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=20=D0=BE=D1=84=D1=84?= =?UTF-8?q?=D0=B5=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mock/offers.js | 2 +- src/model/events-connector.js | 80 +++++++++++++++---------- src/model/events-model.js | 4 ++ src/presenter/board-presenter.js | 3 + src/presenter/event-presenter.js | 5 +- src/view/new-event-edit-element-view.js | 56 +++++++++++------ 6 files changed, 96 insertions(+), 54 deletions(-) diff --git a/src/mock/offers.js b/src/mock/offers.js index c7500da..bd90e9e 100644 --- a/src/mock/offers.js +++ b/src/mock/offers.js @@ -2,7 +2,7 @@ import { getRandomNumber } from '../utils/common'; import { nanoid } from 'nanoid'; import { EVENTS_TYPES } from '../const'; -const OFFERS_COUNT = 2; +const OFFERS_COUNT = 3; const getRandomOffer = (index) => { const offer = { diff --git a/src/model/events-connector.js b/src/model/events-connector.js index f9db343..f0da184 100644 --- a/src/model/events-connector.js +++ b/src/model/events-connector.js @@ -7,63 +7,77 @@ const EVENTS_COUNT = 5; export default class EventsConnector { #destinationsList = convertKeysToCamelCase(getRandomDestinations()); - #offersMap = this.#createOffersMap(convertKeysToCamelCase(getRandomOffers())); - #eventsList = convertKeysToCamelCase(getRandomEvents(EVENTS_COUNT, this.#destinationsList, this.#offersMap)); + #offersMap = this.#initializeOffersMap(convertKeysToCamelCase(getRandomOffers())); + #clonedOffersMap = structuredClone(this.#offersMap); + #eventsList = convertKeysToCamelCase(getRandomEvents(EVENTS_COUNT, this.#destinationsList, this.#clonedOffersMap)); - #createOffersMap(data) { - const offersMap = new Map(); - data.forEach((item) => { - const { type, offers } = item; - const offersById = new Map(); - - offers.forEach((offer) => { - const { id, title, price } = offer; - offersById.set(id, { title, price }); - }); + #initializeOffersMap(data) { + return data.reduce((offersMap, { type, offers }) => { + const offersById = offers.reduce((map, { id, title, price }) => { + map.set(id, { title, price, isActive: false }); + return map; + }, new Map()); offersMap.set(type, offersById); + return offersMap; + }, new Map()); + } + + #updateOffersActivity() { + this.#eventsList.forEach(({ offers: eventOffers, type }) => { + const offersById = this.#clonedOffersMap.get(type); + + eventOffers.forEach((offerId) => { + if (offersById.has(offerId)) { + offersById.get(offerId).isActive = true; + } + }); }); + } - return offersMap; + constructor() { + this.#updateOffersActivity(); } #getOfferById(type, id) { - const offersById = this.#offersMap.get(type); - const offer = offersById.get(id); - const { title, price } = offer; - return { id, title, price }; + const offer = this.#clonedOffersMap.get(type)?.get(id); + return offer ? { id, ...offer } : null; } #createUserEvents() { - const destinationsMap = new Map(this.#destinationsList.map((destination) => [destination.id, destination])); + const destinationsMap = new Map(this.#destinationsList.map(({ id, ...rest }) => [id, rest])); - const mergedEvents = this.#eventsList.map((event) => { + return Array.from(this.#eventsList.map((event) => { const destinationData = destinationsMap.get(event.destination); - - const offersData = event.offers - .map((offerId) => this.#getOfferById(event.type, offerId)) - .filter(Boolean); + const offersMap = new Map(event.offers.map((offerId) => { + const offer = this.#getOfferById(event.type, offerId); + return offer ? [offer.id, offer] : null; + }).filter(Boolean)); return { ...event, destination: { id: destinationData.id, - name: destinationData.name, - description: destinationData.description, - pictures: destinationData.pictures, + ...destinationData }, - offers: offersData + offers: offersMap }; - }); - - return Array.from(new Map(mergedEvents.map((event) => [event.id, event])).values()); + }).reduce((acc, event) => { + acc.set(event.id, event); + return acc; + }, new Map()).values()); } - get userEvents() { - return structuredClone(this.#createUserEvents()); + get userEvents () { + return this.#createUserEvents(); } - get destinationsList() { + get destinationsList () { return this.#destinationsList; } + + get offersMap () { + return this.#offersMap; + } + } diff --git a/src/model/events-model.js b/src/model/events-model.js index 8a10d0d..f8a0124 100644 --- a/src/model/events-model.js +++ b/src/model/events-model.js @@ -4,12 +4,16 @@ export default class EventsModel { #EventsConnector = new EventsConnector; #eventsList = this.#EventsConnector.userEvents; #destinationsList = this.#EventsConnector.destinationsList; + #offersMap = this.#EventsConnector.offersMap; get userEvents () { return this.#eventsList; } getDestinationsData = () => this.#destinationsList; + findDestinationData = (destinationId) => this.#destinationsList.find((destination) => destination.id === destinationId); + getOffersMapByType = (type) => this.#offersMap.get(type) || null; + } diff --git a/src/presenter/board-presenter.js b/src/presenter/board-presenter.js index 3e5da9a..0e2a006 100644 --- a/src/presenter/board-presenter.js +++ b/src/presenter/board-presenter.js @@ -21,12 +21,14 @@ export default class BoardPresenter { #sourcedBoardEvents = []; #findDestinationData = null; #getDestinationsData = null; + #getOffersMapByType = null; constructor ({container, eventsModel}) { this.#container = container; this.#eventsModel = eventsModel; this.#findDestinationData = this.#eventsModel.findDestinationData; this.#getDestinationsData = this.#eventsModel.getDestinationsData; + this.#getOffersMapByType = this.#eventsModel.getOffersMapByType; } init () { @@ -43,6 +45,7 @@ export default class BoardPresenter { onModeChange: this.#handleModeChange, findDestinationData: this.#findDestinationData, getDestinationsList: this.#getDestinationsData, + getOffersMapByType: this.#getOffersMapByType, }); eventPresenter.init(inputUserEvent); this.#eventPresenters.set(inputUserEvent.id, eventPresenter); diff --git a/src/presenter/event-presenter.js b/src/presenter/event-presenter.js index 6918ec3..ffaf20c 100644 --- a/src/presenter/event-presenter.js +++ b/src/presenter/event-presenter.js @@ -14,6 +14,7 @@ export default class EventPresenter { #handleModeChange = null; #findDestinationData = null; #getDestinationsList = null; + #getOffersMapByType = null; #eventComponent = null; #eventEditComponent = null; @@ -21,12 +22,13 @@ export default class EventPresenter { #eventItem = null; #mode = Mode.DEFAULT; - constructor ({container, onDataChange, onModeChange, findDestinationData, getDestinationsList}) { + constructor ({container, onDataChange, onModeChange, findDestinationData, getDestinationsList, getOffersMapByType}) { this.#container = container; this.#handleDataChange = onDataChange; this.#handleModeChange = onModeChange; this.#findDestinationData = findDestinationData; this.#getDestinationsList = getDestinationsList; + this.#getOffersMapByType = getOffersMapByType; } init (eventItem) { @@ -45,6 +47,7 @@ export default class EventPresenter { onClick: this.#handleSaveClick, findDestination: this.#findDestinationData, getDestinationsData: this.#getDestinationsList, + getOffersMapByType: this.#getOffersMapByType, }); if (prevEventComponent === null || prevEventEditComponent === null) { diff --git a/src/view/new-event-edit-element-view.js b/src/view/new-event-edit-element-view.js index 2194b00..738e2c0 100644 --- a/src/view/new-event-edit-element-view.js +++ b/src/view/new-event-edit-element-view.js @@ -4,11 +4,11 @@ import flatpickr from 'flatpickr'; import 'flatpickr/dist/flatpickr.min.css'; -const createNewOffer = (offer) => { - const {title, price} = offer; +const createNewOffer = (id, offer) => { + const { title, price, isActive } = offer; return `
    - -
    `; }; -const createOffers = (offers) => { +const createOffers = (offersMap) => { let offersHTML = ''; - offers.forEach((offer) => { - offersHTML += createNewOffer(offer); + offersMap.forEach((offer, id) => { + offersHTML += createNewOffer(id, offer); }); return offersHTML; }; @@ -130,16 +130,19 @@ export default class NewEventEditElementView extends AbstractStatefulView { #handleClick = null; #rollupButton = null; #getDestinationsData = null; + #findDestinationData = null; + #getOffersMapByType = null; #datepicker = null; #handleSubmit = null; #formElement = null; - constructor ({userEvent, onClick, findDestination, getDestinationsData, onSubmit}) { + constructor ({userEvent, onClick, findDestination, getDestinationsData, getOffersMapByType, onSubmit}) { super(); this.#eventData = userEvent; - this.findDestinationData = findDestination; + this.#findDestinationData = findDestination; this.#getDestinationsData = getDestinationsData; + this.#getOffersMapByType = getOffersMapByType; this._setState(NewEventEditElementView.parseEventDataToState(userEvent)); this.#handleClick = onClick; @@ -179,11 +182,13 @@ export default class NewEventEditElementView extends AbstractStatefulView { this.element.querySelector('.event__input--destination') .addEventListener('input', this.#eventDestinationToggleHandler); - this.element.querySelector('#event-end-time-1') - .addEventListener('input', this.#eventEndTimeToggleHandler); this.element.querySelectorAll('.event__type-label') .forEach((label) => { - label.addEventListener('click', this.#eventEventTypeToggleHandler); + label.addEventListener('click', this.#eventTypeToggleHandler); + }); + this.element.querySelectorAll('.event__offer-label') + .forEach((label) => { + label.addEventListener('click', this.#eventOfferToggleHandler); }); this.#setDatepicker(); @@ -265,7 +270,7 @@ export default class NewEventEditElementView extends AbstractStatefulView { if (option.value === inputValue) { const cityId = option.getAttribute('data-destination-id'); - const selectedDestination = this.findDestinationData(cityId); + const selectedDestination = this.#findDestinationData(cityId); if (selectedDestination) { this.updateElement({ @@ -276,18 +281,31 @@ export default class NewEventEditElementView extends AbstractStatefulView { }); }; - #eventEndTimeToggleHandler = (evt) => { + #eventTypeToggleHandler = (evt) => { evt.preventDefault(); + const newType = evt.target.dataset.eventType.toLowerCase(); + this.updateElement({ - dateEnd: evt.target.value, + type: evt.target.dataset.eventType, + offers: this.#getOffersMapByType(newType), }); }; - #eventEventTypeToggleHandler = (evt) => { + #eventOfferToggleHandler = (evt) => { evt.preventDefault(); - this.updateElement({ - type: evt.target.dataset.eventType, - }); + + const offerId = evt.currentTarget.dataset.offerId; + const offers = new Map (this._state.offers); + + if (offers.has(offerId)) { + const offer = offers.get(offerId); + offer.isActive = !offer.isActive; + offers.set(offerId, offer); + + this.updateElement({ + offers: offers, + }); + } }; } From be187d724eeba1678c9923a1df5dee498e361780 Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Tue, 15 Oct 2024 18:16:52 +0300 Subject: [PATCH 16/17] =?UTF-8?q?=D0=BE=D1=82=D0=B8=D0=BC=D0=B8=D0=B7?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=BA=D0=BE=D0=BD=D0=BD?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D0=BE=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/events-connector.js | 37 ++++++++++++++--------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/model/events-connector.js b/src/model/events-connector.js index f0da184..23ffe9c 100644 --- a/src/model/events-connector.js +++ b/src/model/events-connector.js @@ -11,55 +11,49 @@ export default class EventsConnector { #clonedOffersMap = structuredClone(this.#offersMap); #eventsList = convertKeysToCamelCase(getRandomEvents(EVENTS_COUNT, this.#destinationsList, this.#clonedOffersMap)); - #initializeOffersMap(data) { + #initializeOffersMap (data) { return data.reduce((offersMap, { type, offers }) => { - const offersById = offers.reduce((map, { id, title, price }) => { - map.set(id, { title, price, isActive: false }); - return map; - }, new Map()); - + const offersById = new Map( + offers.map(({ id, title, price }) => [id, { title, price, isActive: false }]) + ); offersMap.set(type, offersById); return offersMap; }, new Map()); } - #updateOffersActivity() { + #updateOffersActivity () { this.#eventsList.forEach(({ offers: eventOffers, type }) => { const offersById = this.#clonedOffersMap.get(type); - eventOffers.forEach((offerId) => { - if (offersById.has(offerId)) { - offersById.get(offerId).isActive = true; + const offer = offersById.get(offerId); + if (offer) { + offer.isActive = true; } }); }); } - constructor() { + constructor () { this.#updateOffersActivity(); } - #getOfferById(type, id) { + #getOfferById (type, id) { const offer = this.#clonedOffersMap.get(type)?.get(id); return offer ? { id, ...offer } : null; } - #createUserEvents() { + #createUserEvents () { const destinationsMap = new Map(this.#destinationsList.map(({ id, ...rest }) => [id, rest])); return Array.from(this.#eventsList.map((event) => { const destinationData = destinationsMap.get(event.destination); - const offersMap = new Map(event.offers.map((offerId) => { - const offer = this.#getOfferById(event.type, offerId); - return offer ? [offer.id, offer] : null; - }).filter(Boolean)); + const offersMap = new Map( + event.offers.map((offerId) => this.#getOfferById(event.type, offerId)).filter(Boolean).map((offer) => [offer.id, offer]) + ); return { ...event, - destination: { - id: destinationData.id, - ...destinationData - }, + destination: { id: destinationData.id, ...destinationData }, offers: offersMap }; }).reduce((acc, event) => { @@ -79,5 +73,4 @@ export default class EventsConnector { get offersMap () { return this.#offersMap; } - } From a986516b912cc280ca23024984ca825b9fec2c1b Mon Sep 17 00:00:00 2001 From: Anton Yushkov Date: Tue, 15 Oct 2024 22:59:09 +0300 Subject: [PATCH 17/17] =?UTF-8?q?=D0=BD=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/events-connector.js | 10 +++++----- src/model/events-model.js | 8 +++++--- src/presenter/board-presenter.js | 6 +++--- src/presenter/event-presenter.js | 10 +++++----- src/view/new-event-edit-element-view.js | 15 +++++++-------- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/model/events-connector.js b/src/model/events-connector.js index 23ffe9c..e7bc148 100644 --- a/src/model/events-connector.js +++ b/src/model/events-connector.js @@ -6,10 +6,10 @@ import { convertKeysToCamelCase } from '../utils/common'; const EVENTS_COUNT = 5; export default class EventsConnector { - #destinationsList = convertKeysToCamelCase(getRandomDestinations()); + #destinationsData = convertKeysToCamelCase(getRandomDestinations()); #offersMap = this.#initializeOffersMap(convertKeysToCamelCase(getRandomOffers())); #clonedOffersMap = structuredClone(this.#offersMap); - #eventsList = convertKeysToCamelCase(getRandomEvents(EVENTS_COUNT, this.#destinationsList, this.#clonedOffersMap)); + #eventsList = convertKeysToCamelCase(getRandomEvents(EVENTS_COUNT, this.#destinationsData, this.#clonedOffersMap)); #initializeOffersMap (data) { return data.reduce((offersMap, { type, offers }) => { @@ -43,7 +43,7 @@ export default class EventsConnector { } #createUserEvents () { - const destinationsMap = new Map(this.#destinationsList.map(({ id, ...rest }) => [id, rest])); + const destinationsMap = new Map(this.#destinationsData.map(({ id, ...rest }) => [id, rest])); return Array.from(this.#eventsList.map((event) => { const destinationData = destinationsMap.get(event.destination); @@ -66,8 +66,8 @@ export default class EventsConnector { return this.#createUserEvents(); } - get destinationsList () { - return this.#destinationsList; + get destinationsData () { + return this.#destinationsData; } get offersMap () { diff --git a/src/model/events-model.js b/src/model/events-model.js index f8a0124..0f41d62 100644 --- a/src/model/events-model.js +++ b/src/model/events-model.js @@ -3,16 +3,18 @@ import EventsConnector from './events-connector'; export default class EventsModel { #EventsConnector = new EventsConnector; #eventsList = this.#EventsConnector.userEvents; - #destinationsList = this.#EventsConnector.destinationsList; + #destinationsData = this.#EventsConnector.destinationsData; #offersMap = this.#EventsConnector.offersMap; get userEvents () { return this.#eventsList; } - getDestinationsData = () => this.#destinationsList; + get destinationsData() { + return this.#destinationsData; + } - findDestinationData = (destinationId) => this.#destinationsList.find((destination) => destination.id === destinationId); + findDestinationData = (destinationId) => this.#destinationsData.find((destination) => destination.id === destinationId); getOffersMapByType = (type) => this.#offersMap.get(type) || null; diff --git a/src/presenter/board-presenter.js b/src/presenter/board-presenter.js index 0e2a006..9719fbb 100644 --- a/src/presenter/board-presenter.js +++ b/src/presenter/board-presenter.js @@ -20,14 +20,14 @@ export default class BoardPresenter { #currentSortType = SortType.DEFAULT; #sourcedBoardEvents = []; #findDestinationData = null; - #getDestinationsData = null; + #destinationsData = null; #getOffersMapByType = null; constructor ({container, eventsModel}) { this.#container = container; this.#eventsModel = eventsModel; this.#findDestinationData = this.#eventsModel.findDestinationData; - this.#getDestinationsData = this.#eventsModel.getDestinationsData; + this.#destinationsData = this.#eventsModel.destinationsData; this.#getOffersMapByType = this.#eventsModel.getOffersMapByType; } @@ -44,7 +44,7 @@ export default class BoardPresenter { onDataChange: this.#handleEventChange, onModeChange: this.#handleModeChange, findDestinationData: this.#findDestinationData, - getDestinationsList: this.#getDestinationsData, + destinationsData: this.#destinationsData, getOffersMapByType: this.#getOffersMapByType, }); eventPresenter.init(inputUserEvent); diff --git a/src/presenter/event-presenter.js b/src/presenter/event-presenter.js index ffaf20c..99aeaf6 100644 --- a/src/presenter/event-presenter.js +++ b/src/presenter/event-presenter.js @@ -13,7 +13,7 @@ export default class EventPresenter { #handleDataChange = null; #handleModeChange = null; #findDestinationData = null; - #getDestinationsList = null; + #destinationsData = null; #getOffersMapByType = null; #eventComponent = null; @@ -22,12 +22,12 @@ export default class EventPresenter { #eventItem = null; #mode = Mode.DEFAULT; - constructor ({container, onDataChange, onModeChange, findDestinationData, getDestinationsList, getOffersMapByType}) { + constructor ({container, onDataChange, onModeChange, findDestinationData, destinationsData, getOffersMapByType}) { this.#container = container; this.#handleDataChange = onDataChange; this.#handleModeChange = onModeChange; this.#findDestinationData = findDestinationData; - this.#getDestinationsList = getDestinationsList; + this.#destinationsData = destinationsData; this.#getOffersMapByType = getOffersMapByType; } @@ -45,8 +45,8 @@ export default class EventPresenter { this.#eventEditComponent = new NewEventEditElementView({ userEvent: this.#eventItem, onClick: this.#handleSaveClick, - findDestination: this.#findDestinationData, - getDestinationsData: this.#getDestinationsList, + findDestinationData: this.#findDestinationData, + destinationsData: this.#destinationsData, getOffersMapByType: this.#getOffersMapByType, }); diff --git a/src/view/new-event-edit-element-view.js b/src/view/new-event-edit-element-view.js index 738e2c0..928264e 100644 --- a/src/view/new-event-edit-element-view.js +++ b/src/view/new-event-edit-element-view.js @@ -28,7 +28,7 @@ const createType = (type) => { const typeToLowerCase = type.toLowerCase(); return `
    - +
    `; }; @@ -129,7 +129,7 @@ export default class NewEventEditElementView extends AbstractStatefulView { #eventData = null; #handleClick = null; #rollupButton = null; - #getDestinationsData = null; + #destinationsData = null; #findDestinationData = null; #getOffersMapByType = null; #datepicker = null; @@ -137,11 +137,11 @@ export default class NewEventEditElementView extends AbstractStatefulView { #handleSubmit = null; #formElement = null; - constructor ({userEvent, onClick, findDestination, getDestinationsData, getOffersMapByType, onSubmit}) { + constructor ({userEvent, onClick, findDestinationData, destinationsData, getOffersMapByType, onSubmit}) { super(); this.#eventData = userEvent; - this.#findDestinationData = findDestination; - this.#getDestinationsData = getDestinationsData; + this.#findDestinationData = findDestinationData; + this.#destinationsData = destinationsData; this.#getOffersMapByType = getOffersMapByType; this._setState(NewEventEditElementView.parseEventDataToState(userEvent)); @@ -153,7 +153,7 @@ export default class NewEventEditElementView extends AbstractStatefulView { } get template () { - return createNewEventEditElementTemplate(this._state, this.#getDestinationsData()); + return createNewEventEditElementTemplate(this._state, this.#destinationsData); } removeElement() { @@ -283,11 +283,10 @@ export default class NewEventEditElementView extends AbstractStatefulView { #eventTypeToggleHandler = (evt) => { evt.preventDefault(); - const newType = evt.target.dataset.eventType.toLowerCase(); this.updateElement({ type: evt.target.dataset.eventType, - offers: this.#getOffersMapByType(newType), + offers: this.#getOffersMapByType(evt.target.dataset.eventType), }); };