Repository: htmlacademy-univer-js2/1831559-big-trip-4
Pull request #7: Большие перемены (часть 1)
Author: login: iti-rina
Created: 2025-03-03T07:57:49Z | Merged: 2025-03-03T13:52:08Z



---
:mortar_board: [Большие перемены (часть 1)](https://up.htmlacademy.ru/univer-js2/4/user/1831559/tasks/10)


Diff:
diff --git a/src/mock/events.js b/src/mock/events.js
index d1d009e..7ee8053 100644
--- a/src/mock/events.js
+++ b/src/mock/events.js
@@ -206,6 +206,7 @@ const mockEvents = [
     dateFrom: new Date(2019, 3, 20, 11, 15),
     dateTo: new Date(2019, 3, 20, 12, 15),
     basePrice: 180,
+    isFavorite: true,
     offers: ['0fb0a3bd-30f1-4d55-84f6-fb05d684c47b']
   }
 ];
diff --git a/src/presenter/event-list-presenter.js b/src/presenter/event-list-presenter.js
index 74a1f32..37581b9 100644
--- a/src/presenter/event-list-presenter.js
+++ b/src/presenter/event-list-presenter.js
@@ -1,9 +1,9 @@
-import { render, replace } from '../framework/render.js';
+import { render } from '../framework/render.js';
+import { updateItem } from '../utils.js';
 import EventListView from '../view/event-list-view.js';
-import EventPointView from '../view/event-point-view.js';
 import FilterView from '../view/filter-view.js';
 import SortView from '../view/sort-view.js';
-import EditPointView from '../view/edit-point-view.js';
+import EventPointPresenter from './event-point-presenter.js';
 
 export default class EventAppPresenter {
   #eventListContainer = null;
@@ -16,65 +16,53 @@ export default class EventAppPresenter {
 
   #events = [];
   #destinations = [];
+  #eventsPresenters = new Map();
 
   constructor({ eventListContainer, filterContainer, eventsModel }) {
     this.#eventListContainer = eventListContainer;
     this.#filterContainer = filterContainer;
     this.#eventsModel = eventsModel;
+    this.#destinations = [...this.#eventsModel.destinations];
   }
 
   init() {
+    this.#renderFilter();
+    this.#renderSort();
+
+    this.#renderEventList();
+  }
+
+  #renderFilter() {
     render(this.#filterComponent, this.#filterContainer);
+  }
+
+  #renderSort() {
     render(this.#sortComponent, this.#eventListContainer);
+  }
 
+  #renderEventList() {
     this.#events = [...this.#eventsModel.events];
     render(this.#eventListComponent, this.#eventListContainer);
 
-    this.#destinations = [...this.#eventsModel.destinations];
-
-    for (let i = 0; i < this.#events.length; i++) {
-      this.#renderEventPoint(this.#events[i]);
-    }
+    this.#events.forEach((event) => this.#renderEventPoint(event));
   }
 
   #renderEventPoint(event) {
-    const escKeyDownHandler = (evt) => {
-      if (evt.key === 'Escape') {
-        evt.preventDefault();
-        replaceEditToView();
-        document.removeEventListener('keydown', escKeyDownHandler);
-      }
-    };
-
-    const eventPointComponent = new EventPointView({
-      event,
-      onEditClick: () => {
-        replaceViewToEdit();
-        document.addEventListener('keydown', escKeyDownHandler);
-      }
-    });
-
-    const eventEditComponent = new EditPointView({
-      event,
-      destinations: this.#destinations,
-      onFormSubmit: () => {
-        replaceEditToView();
-        document.removeEventListener('keydown', escKeyDownHandler);
-      },
-      onCloseForm: () => {
-        replaceEditToView();
-        document.removeEventListener('keydown', escKeyDownHandler);
-      }
+    const eventPointPresenter = new EventPointPresenter({
+      eventListContainer: this.#eventListComponent.element,
+      onEventChange: this.#handleEventChange,
+      onModeChange: this.#handleModeChange
     });
+    eventPointPresenter.init(event, this.#destinations);
+    this.#eventsPresenters.set(event.id, eventPointPresenter);
+  }
 
-    function replaceViewToEdit() {
-      return replace(eventEditComponent, eventPointComponent);
-    }
-
-    function replaceEditToView() {
-      return replace(eventPointComponent, eventEditComponent);
-    }
+  #handleEventChange = (updatedEvent) => {
+    this.#events = updateItem(this.#events, updatedEvent);
+    this.#eventsPresenters.get(updatedEvent.id).init(updatedEvent, this.#destinations);
+  };
 
-    render(eventPointComponent, this.#eventListComponent.element);
-  }
+  #handleModeChange = () => {
+    this.#eventsPresenters.forEach((presenter) => presenter.resetView());
+  };
 }
diff --git a/src/presenter/event-point-presenter.js b/src/presenter/event-point-presenter.js
new file mode 100644
index 0000000..c6fe8a0
--- /dev/null
+++ b/src/presenter/event-point-presenter.js
@@ -0,0 +1,113 @@
+import { remove, render, replace } from '../framework/render';
+import EditPointView from '../view/edit-point-view';
+import EventPointView from '../view/event-point-view';
+
+const mode = {
+  default: 'DEFAULT',
+  edit: 'EDIT'
+};
+
+export default class EventPointPresenter {
+  #eventListContainer = null;
+
+  #event = null;
+  #destinations = [];
+  #mode = mode.default;
+
+  #eventPointComponent = null;
+  #eventEditComponent = null;
+
+  #handleEventChange = null;
+  #handleModeChange = null;
+
+  constructor({ eventListContainer, onEventChange, onModeChange }) {
+    this.#eventListContainer = eventListContainer;
+    this.#handleEventChange = onEventChange;
+    this.#handleModeChange = onModeChange;
+  }
+
+  init(event, destinations) {
+    this.#event = event;
+    this.#destinations = destinations;
+
+    const prevEventPointComponent = this.#eventPointComponent;
+    const prevEditPointComponent = this.#eventEditComponent;
+
+    this.#eventPointComponent = new EventPointView({
+      event,
+      onEditClick: this.#handleEditClick,
+      onFavoriteClick: this.#handleFavoriteClick
+    });
+
+    this.#eventEditComponent = new EditPointView({
+      event,
+      destinations: this.#destinations,
+      onFormSubmit: this.#handleFormSubmit,
+      onCloseForm: this.#handleCloseForm,
+    });
+
+    if (prevEventPointComponent === null || prevEditPointComponent === null) {
+      render(this.#eventPointComponent, this.#eventListContainer);
+      return;
+    }
+
+    if (this.#mode === mode.default) {
+      replace(this.#eventPointComponent, prevEventPointComponent);
+    }
+
+    if (this.#mode === mode.edit) {
+      replace(this.#eventEditComponent, prevEditPointComponent);
+    }
+
+    remove(prevEventPointComponent);
+    remove(prevEditPointComponent);
+  }
+
+  #destroy() {
+    remove(this.#eventPointComponent);
+    remove(this.#eventEditComponent);
+  }
+
+  #replaceViewToEdit() {
+    replace(this.#eventEditComponent, this.#eventPointComponent);
+    document.addEventListener('keydown', this.#escKeyDownHandler);
+    this.#handleModeChange();
+    this.#mode = mode.edit;
+  }
+
+  #replaceEditToView() {
+    replace(this.#eventPointComponent, this.#eventEditComponent);
+    document.removeEventListener('keydown', this.#escKeyDownHandler);
+    this.#mode = mode.default;
+  }
+
+  #escKeyDownHandler = (evt) => {
+    if (evt.key === 'Escape') {
+      evt.preventDefault();
+      this.#replaceEditToView();
+    }
+  };
+
+  #handleEditClick = () => {
+    this.#replaceViewToEdit();
+  };
+
+  #handleCloseForm = () => {
+    this.#replaceEditToView();
+  };
+
+  #handleFormSubmit = (event) => {
+    this.#replaceEditToView();
+    this.#handleEventChange(event);
+  };
+
+  #handleFavoriteClick = () => {
+    this.#handleEventChange({...this.#event, isFavorite: !this.#event.isFavorite});
+  };
+
+  resetView() {
+    if (this.#mode !== mode.default) {
+      this.#replaceEditToView();
+    }
+  }
+}
diff --git a/src/utils.js b/src/utils.js
index ee35238..d036bfb 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -44,3 +44,7 @@ export function capitalize(text) {
 export function transformToKebabCase(text) {
   return text ? text.split(' ').join('-') : '';
 }
+
+export function updateItem(items, updatedItem) {
+  return items.map((item) => item.id === updatedItem.id ? updatedItem : item);
+}
diff --git a/src/view/edit-point-view.js b/src/view/edit-point-view.js
index 8615c6d..c3ca6ef 100644
--- a/src/view/edit-point-view.js
+++ b/src/view/edit-point-view.js
@@ -197,13 +197,13 @@ export default class EditPointView extends AbstractView {
     return editPointFormTemplate(this.#event, this.#destinations);
   }
 
-  #formSubmitHandler = (event) => {
-    event.preventDefault();
-    this.#handleFormSubmit();
+  #formSubmitHandler = (evt) => {
+    evt.preventDefault();
+    this.#handleFormSubmit(this.#event);
   };
 
-  #formCloseHandler = (event) => {
-    event.preventDefault();
+  #formCloseHandler = (evt) => {
+    evt.preventDefault();
     this.#handleCloseForm();
   };
 }
diff --git a/src/view/event-point-view.js b/src/view/event-point-view.js
index b4e3ce5..fa13325 100644
--- a/src/view/event-point-view.js
+++ b/src/view/event-point-view.js
@@ -3,7 +3,7 @@ import { calcDuration, capitalize, formatDateByPurpose } from '../utils';
 import AbstractView from '../framework/view/abstract-view';
 
 function createEventPointTemplate(event) {
-  const {type, dateFrom, dateTo, destination: destinationId, basePrice, offers: offerIds} = event;
+  const {type, dateFrom, dateTo, destination: destinationId, basePrice, offers: offerIds, isFavorite} = event;
 
   const destination = getDestinationById(destinationId);
   const duration = calcDuration(dateFrom, dateTo);
@@ -37,7 +37,7 @@ function createEventPointTemplate(event) {
                 <ul class="event__selected-offers">
                 ${offersContent.join('')}
                 </ul>
-                <button class="event__favorite-btn" type="button">
+                <button class="event__favorite-btn ${isFavorite ? 'event__favorite-btn--active' : ''}" type="button">
                   <span class="visually-hidden">Add to favorite</span>
                   <svg class="event__favorite-icon" width="28" height="28" viewBox="0 0 28 28">
                     <path d="M14 21l-8.22899 4.3262 1.57159-9.1631L.685209 9.67376 9.8855 8.33688 14 0l4.1145 8.33688 9.2003 1.33688-6.6574 6.48934 1.5716 9.1631L14 21z"/>
@@ -52,21 +52,29 @@ function createEventPointTemplate(event) {
 
 export default class EventPointView extends AbstractView {
   #handleEditClick = null;
+  #handleFavoriteClick = null;
 
-  constructor({ event, onEditClick }) {
+  constructor({ event, onEditClick, onFavoriteClick }) {
     super();
     this.event = event;
     this.#handleEditClick = onEditClick;
+    this.#handleFavoriteClick = onFavoriteClick;
 
     this.element.querySelector('.event__rollup-btn').addEventListener('click', this.#editClickHandler);
+    this.element.querySelector('.event__favorite-btn').addEventListener('click', this.#favoriteClickHandler);
   }
 
   get template() {
     return createEventPointTemplate(this.event);
   }
 
-  #editClickHandler = (event) => {
-    event.preventDefault();
+  #editClickHandler = (evt) => {
+    evt.preventDefault();
     this.#handleEditClick();
   };
+
+  #favoriteClickHandler = (evt) => {
+    evt.preventDefault();
+    this.#handleFavoriteClick();
+  };
 }


Reviews:
user: login: JokaTheGreat
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-03T13:51:33Z
user: login: iti-rina
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T14:00:43Z

Review comments:
diff_hunk: @@ -44,3 +44,7 @@ export function capitalize(text) {
 export function transformToKebabCase(text) {
   return text ? text.split(' ').join('-') : '';
 }
+
+export function updateItem(items, updatedItem) {
path: src/utils.js
user: login: JokaTheGreat
body: Давай тут немного с неймингом поиграемся

updateItem переименуем в updatePointData
items переименуем в points
updatedItem переименуем в updatedPointData

Чтобы было чуть по понятнее что функция делает без контекста использования
created_at: 2025-03-03T13:45:46Z
author_association: NONE
diff_hunk: @@ -197,13 +197,13 @@ export default class EditPointView extends AbstractView {
     return editPointFormTemplate(this.#event, this.#destinations);
   }
 
-  #formSubmitHandler = (event) => {
-    event.preventDefault();
-    this.#handleFormSubmit();
+  #formSubmitHandler = (evt) => {
+    evt.preventDefault();
+    this.#handleFormSubmit(this.#event);
   };
 
-  #formCloseHandler = (event) => {
-    event.preventDefault();
+  #formCloseHandler = (evt) => {
+    evt.preventDefault();
path: src/view/edit-point-view.js
user: login: JokaTheGreat
body: А зачем event до evt упростила?)
created_at: 2025-03-03T13:47:03Z
author_association: NONE
diff_hunk: @@ -197,13 +197,13 @@ export default class EditPointView extends AbstractView {
     return editPointFormTemplate(this.#event, this.#destinations);
   }
 
-  #formSubmitHandler = (event) => {
-    event.preventDefault();
-    this.#handleFormSubmit();
+  #formSubmitHandler = (evt) => {
+    evt.preventDefault();
+    this.#handleFormSubmit(this.#event);
path: src/view/edit-point-view.js
user: login: JokaTheGreat
body: Не совсем понимаю зачем тут эта строчка, разве у нас может при отправке формы измениться данные точки? (по крайней мере с текущим прогрессом заданий)
created_at: 2025-03-03T13:49:09Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+import { remove, render, replace } from '../framework/render';
+import EditPointView from '../view/edit-point-view';
+import EventPointView from '../view/event-point-view';
+
+const mode = {
+  default: 'DEFAULT',
+  edit: 'EDIT'
+};
+
+export default class EventPointPresenter {
+  #eventListContainer = null;
+
+  #event = null;
+  #destinations = [];
+  #mode = mode.default;
+
+  #eventPointComponent = null;
+  #eventEditComponent = null;
+
+  #handleEventChange = null;
+  #handleModeChange = null;
+
+  constructor({ eventListContainer, onEventChange, onModeChange }) {
+    this.#eventListContainer = eventListContainer;
+    this.#handleEventChange = onEventChange;
+    this.#handleModeChange = onModeChange;
+  }
+
+  init(event, destinations) {
+    this.#event = event;
+    this.#destinations = destinations;
+
+    const prevEventPointComponent = this.#eventPointComponent;
+    const prevEditPointComponent = this.#eventEditComponent;
+
+    this.#eventPointComponent = new EventPointView({
+      event,
+      onEditClick: this.#handleEditClick,
+      onFavoriteClick: this.#handleFavoriteClick
+    });
+
+    this.#eventEditComponent = new EditPointView({
+      event,
+      destinations: this.#destinations,
+      onFormSubmit: this.#handleFormSubmit,
+      onCloseForm: this.#handleCloseForm,
+    });
+
+    if (prevEventPointComponent === null || prevEditPointComponent === null) {
+      render(this.#eventPointComponent, this.#eventListContainer);
+      return;
+    }
+
+    if (this.#mode === mode.default) {
+      replace(this.#eventPointComponent, prevEventPointComponent);
+    }
+
+    if (this.#mode === mode.edit) {
+      replace(this.#eventEditComponent, prevEditPointComponent);
+    }
+
+    remove(prevEventPointComponent);
+    remove(prevEditPointComponent);
+  }
+
+  #destroy() {
+    remove(this.#eventPointComponent);
+    remove(this.#eventEditComponent);
+  }
path: src/presenter/event-point-presenter.js
user: login: JokaTheGreat
body: Метод нигде не используется, можно выпилить
created_at: 2025-03-03T13:50:26Z
author_association: NONE
diff_hunk: @@ -0,0 +1,113 @@
+import { remove, render, replace } from '../framework/render';
+import EditPointView from '../view/edit-point-view';
+import EventPointView from '../view/event-point-view';
+
+const mode = {
+  default: 'DEFAULT',
+  edit: 'EDIT'
+};
+
+export default class EventPointPresenter {
+  #eventListContainer = null;
+
+  #event = null;
+  #destinations = [];
+  #mode = mode.default;
+
+  #eventPointComponent = null;
+  #eventEditComponent = null;
+
+  #handleEventChange = null;
+  #handleModeChange = null;
+
+  constructor({ eventListContainer, onEventChange, onModeChange }) {
+    this.#eventListContainer = eventListContainer;
+    this.#handleEventChange = onEventChange;
+    this.#handleModeChange = onModeChange;
+  }
+
+  init(event, destinations) {
+    this.#event = event;
+    this.#destinations = destinations;
+
+    const prevEventPointComponent = this.#eventPointComponent;
+    const prevEditPointComponent = this.#eventEditComponent;
+
+    this.#eventPointComponent = new EventPointView({
+      event,
+      onEditClick: this.#handleEditClick,
+      onFavoriteClick: this.#handleFavoriteClick
+    });
+
+    this.#eventEditComponent = new EditPointView({
+      event,
+      destinations: this.#destinations,
+      onFormSubmit: this.#handleFormSubmit,
+      onCloseForm: this.#handleCloseForm,
+    });
+
+    if (prevEventPointComponent === null || prevEditPointComponent === null) {
+      render(this.#eventPointComponent, this.#eventListContainer);
+      return;
+    }
+
+    if (this.#mode === mode.default) {
+      replace(this.#eventPointComponent, prevEventPointComponent);
+    }
+
+    if (this.#mode === mode.edit) {
+      replace(this.#eventEditComponent, prevEditPointComponent);
+    }
+
+    remove(prevEventPointComponent);
+    remove(prevEditPointComponent);
+  }
+
+  #destroy() {
+    remove(this.#eventPointComponent);
+    remove(this.#eventEditComponent);
+  }
+
+  #replaceViewToEdit() {
+    replace(this.#eventEditComponent, this.#eventPointComponent);
+    document.addEventListener('keydown', this.#escKeyDownHandler);
+    this.#handleModeChange();
+    this.#mode = mode.edit;
+  }
+
+  #replaceEditToView() {
+    replace(this.#eventPointComponent, this.#eventEditComponent);
+    document.removeEventListener('keydown', this.#escKeyDownHandler);
+    this.#mode = mode.default;
+  }
+
+  #escKeyDownHandler = (evt) => {
+    if (evt.key === 'Escape') {
+      evt.preventDefault();
+      this.#replaceEditToView();
+    }
+  };
+
+  #handleEditClick = () => {
+    this.#replaceViewToEdit();
+  };
+
+  #handleCloseForm = () => {
+    this.#replaceEditToView();
+  };
+
+  #handleFormSubmit = (event) => {
+    this.#replaceEditToView();
+    this.#handleEventChange(event);
path: src/presenter/event-point-presenter.js
user: login: JokaTheGreat
body: Ну и тут такой же вопрос по поводу отправки формы
created_at: 2025-03-03T13:51:29Z
author_association: NONE
diff_hunk: @@ -197,13 +197,13 @@ export default class EditPointView extends AbstractView {
     return editPointFormTemplate(this.#event, this.#destinations);
   }
 
-  #formSubmitHandler = (event) => {
-    event.preventDefault();
-    this.#handleFormSubmit();
+  #formSubmitHandler = (evt) => {
+    evt.preventDefault();
+    this.#handleFormSubmit(this.#event);
   };
 
-  #formCloseHandler = (event) => {
-    event.preventDefault();
+  #formCloseHandler = (evt) => {
+    evt.preventDefault();
path: src/view/edit-point-view.js
user: login: iti-rina
body: Тут я просто сама уже путаться начала между event в обработчике и event, который поинт. Возможно, стоило везде, где используется event, в контексте точки маршрута заменить на point
created_at: 2025-03-04T14:00:43Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-03T07:58:39Z
body: Ваш пулреквест опубликован. Посмотреть можно [здесь](https://htmlacademy-univer-js2.github.io/1831559-big-trip-4/7/)
author_association: NONE
user: login: JokaTheGreat
created_at: 2025-03-03T13:52:01Z
body: В целом все работает, задание я одобрю, можешь правки со следующим заданием сделать
author_association: NONE