diff --git a/src/const.js b/src/const.js index 009b20f..e3f5356 100644 --- a/src/const.js +++ b/src/const.js @@ -2,15 +2,6 @@ const POINT_COUNT = 5; const DESTINATIONS = ['Paris', 'London', 'New York', 'Shanghai', 'Moscow']; -const FILTERS = { - 'Everything': POINT_COUNT, - 'Future': POINT_COUNT, - 'Present': POINT_COUNT, - 'Past': POINT_COUNT -}; - -const DEFAULT_FILTER = 'Everything'; - const DESCRIPTIONS = [ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras aliquet varius magna, non porta ligula feugiat eget.', 'Fusce tristique felis at fermentum pharetra. Aliquam id orci ut lectus varius viverra. Nullam nunc ex, convallis sed finibus eget, sollicitudin eget ante.', @@ -40,5 +31,23 @@ const DEFAULT_POINT_DATA = { type: 'Flight' }; +const FilterType = { + EVERYTHING: 'everything', + FUTURE: 'future', + PRESENT: 'present', + PAST: 'past', +}; + +const Mode = { + DEFAULT: 'DEFAULT', + EDITING: 'EDITING', +}; + +const SortType = { + DAY: 'day', + TIME: 'time', + PRICE: 'price', +}; + export {POINT_COUNT, DESTINATIONS, DESCRIPTIONS, DATE_FORMAT, TIME_FORMAT, HOUR_IN_DAY, MINUTES_IN_HOUR, - TYPES, DEFAULT_POINT_DATA, FILTERS, DEFAULT_FILTER}; + TYPES, DEFAULT_POINT_DATA, FilterType, Mode, SortType}; diff --git a/src/main.js b/src/main.js index 6c88765..5326d93 100644 --- a/src/main.js +++ b/src/main.js @@ -1,16 +1,18 @@ import FilterView from './view/filter-view.js'; -import ListPresenter from './presenter/presenter.js'; +import Presenter from './presenter/presenter.js'; import PointsModel from './model/points-model.js'; import {render} from './framework/render.js'; +import {generateFilter} from './mock/filter.js'; const filterContainer = document.querySelector('.trip-controls__filters'); const pointsListContainer = document.querySelector('.trip-events'); const pointsModel = new PointsModel(); -const listPresenter = new ListPresenter({ +const presenter = new Presenter({ container: pointsListContainer, pointsModel }); -render(new FilterView(), filterContainer); -listPresenter.init(); +const filters = generateFilter(pointsModel.points); +render(new FilterView({filters}), filterContainer); +presenter.init(); diff --git a/src/mock/filter.js b/src/mock/filter.js new file mode 100644 index 0000000..a4dd04e --- /dev/null +++ b/src/mock/filter.js @@ -0,0 +1,12 @@ +import {filter} from '../utils/filters.js'; + +function generateFilter(points) { + return Object.entries(filter).map( + ([filterType, filterPoints]) => ({ + type: filterType, + count: filterPoints(points).length, + }), + ); +} + +export {generateFilter}; diff --git a/src/mock/point.js b/src/mock/point.js index d72b0f9..da315fd 100644 --- a/src/mock/point.js +++ b/src/mock/point.js @@ -25,7 +25,7 @@ const mockPoints = [ { id: 3, basePrice: 75, - dateFrom: '2019-07-15T7:55', + dateFrom: '2019-07-12T7:55', dateTo: '2019-07-15T14:20', destination: 2, isFavorite: true, @@ -35,8 +35,8 @@ const mockPoints = [ { id: 4, basePrice: 75, - dateFrom: '2019-07-15T7:55', - dateTo: '2019-07-15T14:20', + dateFrom: '2025-07-15T7:55', + dateTo: '2025-07-15T14:20', destination: 2, isFavorite: true, offers: [0], @@ -45,8 +45,8 @@ const mockPoints = [ { id: 5, basePrice: 5, - dateFrom: '2019-07-15T7:55', - dateTo: '2019-07-15T14:20', + dateFrom: '2019-07-14T7:55', + dateTo: '2025-07-15T14:20', destination: 2, isFavorite: true, offers: [0], @@ -149,7 +149,8 @@ function getOffersByType(type) { return offers.find((offersByType) => offersByType.type === type).offers; } -function getSelectedOffers(offerIds, offersByType) { +function getSelectedOffers(offerIds, type) { + const offersByType = getOffersByType(type); const selectedOffers = []; for (let i = 0; i < offerIds.length; i++ ) { selectedOffers.push(offersByType.find((offer) => offer.id === offerIds[i])); diff --git a/src/presenter/point-presenter.js b/src/presenter/point-presenter.js index 26506c9..fe3a573 100644 --- a/src/presenter/point-presenter.js +++ b/src/presenter/point-presenter.js @@ -2,11 +2,7 @@ import EditPointView from '../view/edit-point-view.js'; import PointView from '../view/point-view.js'; import {render, replace, remove} from '../framework/render.js'; import {getDestinationById, getOffersByType, getSelectedOffers} from '../mock/point.js'; - -const Mode = { - DEFAULT: 'DEFAULT', - EDITING: 'EDITING', -}; +import {Mode} from '../const.js'; export default class PointPresenter { #container; @@ -36,7 +32,7 @@ export default class PointPresenter { this.#pointComponent = new PointView({ point: this.#point, destination, - selectedOffers: getSelectedOffers(this.#point.offers, offersByType), + selectedOffers: getSelectedOffers(this.#point.offers, this.#point.type), handleOpenEditClick: this.#handleOpenEditClick, handleFavoriteButtonClick: this.#handleFavoriteButtonClick }); @@ -111,4 +107,8 @@ export default class PointPresenter { } } + destroy() { + remove(this.#pointComponent); + remove(this.#pointEditComponent); + } } diff --git a/src/presenter/points-list-presenter.js b/src/presenter/points-list-presenter.js index 68675ac..c887802 100644 --- a/src/presenter/points-list-presenter.js +++ b/src/presenter/points-list-presenter.js @@ -4,19 +4,25 @@ import NoPointsView from '../view/no-points-view.js'; import PointPresenter from './point-presenter.js'; import {render} from '../framework/render.js'; import {updateItem} from '../utils/common.js'; +import {SortType} from '../const.js'; +import {sortByDay, sortByPrice, sortByTime} from '../utils/point.js'; export default class PointsListPresenter { #listComponent = new ListView(); + #pointPresenters = new Map(); + #currentSortType = SortType.DAY; + #sortComponent; #container; #points; - #pointPresenters = new Map(); + #sourcedPoints; constructor({container}) { this.#container = container; } init(points) { - this.#points = points; + this.#points = [...points].sort(sortByDay); + this.#sourcedPoints = [...this.#points]; this.#renderPointsList(); } @@ -32,12 +38,21 @@ export default class PointsListPresenter { } } + #clearPoints() { + this.#pointPresenters.forEach((presenter) => presenter.destroy()); + this.#pointPresenters.clear(); + } + #renderNoPoints() { render(new NoPointsView(), this.#container); } #renderSort() { - render(new SortView(), this.#container); + this.#sortComponent = new SortView({ + handleSortTypeChange: this.#handleSortTypeChange + }); + + render(this.#sortComponent, this.#container); } #renderPointsList() { @@ -54,9 +69,35 @@ export default class PointsListPresenter { #handlePointChange = (updatedPoint) => { this.#points = updateItem(this.#points, updatedPoint); this.#pointPresenters.get(updatedPoint.id).init(updatedPoint); + this.#sourcedPoints = updateItem(this.#sourcedPoints, updatedPoint); }; #handleModeChange = () => { this.#pointPresenters.forEach((presenter) => presenter.resetView()); }; + + #sortPoints(sortType) { + switch (sortType) { + case SortType.PRICE: + this.#points.sort(sortByPrice); + break; + case SortType.TIME: + this.#points.sort(sortByTime); + break; + default: + this.#points = [...this.#sourcedPoints]; + } + + this.#currentSortType = sortType; + } + + #handleSortTypeChange = (sortType) => { + if (this.#currentSortType === sortType) { + return; + } + + this.#sortPoints(sortType); + this.#clearPoints(); + this.#renderPoints(); + }; } diff --git a/src/utils/filters.js b/src/utils/filters.js index e69de29..539b7eb 100644 --- a/src/utils/filters.js +++ b/src/utils/filters.js @@ -0,0 +1,11 @@ +import {FilterType} from '../const.js'; +import dayjs from 'dayjs'; + +const filter = { + [FilterType.EVERYTHING]: (points) => points, + [FilterType.FUTURE]: (points) => points.filter((point) => dayjs().isBefore(point.dateFrom)), + [FilterType.PRESENT]: (points) => points.filter((point) => (dayjs().isAfter(point.dateFrom)) && dayjs().isBefore(point.dateTo)), + [FilterType.PAST]: (points) => points.filter((point) => dayjs().isAfter(point.dateTo)), +}; + +export {filter}; diff --git a/src/utils/point.js b/src/utils/point.js index 53d8b89..ac672b7 100644 --- a/src/utils/point.js +++ b/src/utils/point.js @@ -1,5 +1,6 @@ import dayjs from 'dayjs'; import {DATE_FORMAT, TIME_FORMAT, HOUR_IN_DAY, MINUTES_IN_HOUR} from '../const'; +import {getSelectedOffers} from '../mock/point'; function humanizeDueDate(dueDate) { return dueDate ? dayjs(dueDate).format(DATE_FORMAT) : ''; @@ -9,8 +10,12 @@ function humanizeDueTime(dueDate) { return dueDate ? dayjs(dueDate).format(TIME_FORMAT) : ''; } +function calculateDuration(start, end) { + return start && end ? dayjs(end).diff(start, 'minute') : ''; +} + function humanizeDuration(start, end) { - const duration = start && end ? dayjs(end).diff(start, 'minute') : ''; + const duration = calculateDuration(start, end); if (duration === '') { return ''; } @@ -30,4 +35,61 @@ function humanizeDuration(start, end) { } } -export {humanizeDueDate, humanizeDueTime, humanizeDuration}; +function calculateTotalPrice(point, selectedOffers) { + let totalPrice = point.basePrice; + for (const offer of selectedOffers) { + totalPrice += offer.price; + } + return totalPrice; +} + +function sortByTime(pointA, pointB) { + const durationA = calculateDuration(pointA.dateFrom, pointA.dateTo); + const durationB = calculateDuration(pointB.dateFrom, pointB.dateTo); + + if (durationA === durationB) { + return 0; + } + + if (durationA > durationB) { + return 1; + } + + if (durationB > durationA) { + return -1; + } +} + +function sortByPrice(pointA, pointB) { + const priceA = calculateTotalPrice(pointA, getSelectedOffers(pointA.offers, pointA.type)); + const priceB = calculateTotalPrice(pointB, getSelectedOffers(pointB.offers, pointB.type)); + if (priceA === priceB) { + return 0; + } + + if (priceA > priceB) { + return 1; + } + + if (priceB > priceA) { + return -1; + } +} + +function sortByDay(pointA, pointB) { + const startA = pointA.dateFrom; + const startB = pointB.dateFrom; + if (dayjs(startA).isSame(dayjs(startB))) { + return 0; + } + + if (dayjs(startB).isBefore(dayjs(startA))) { + return 1; + } + + if (dayjs(startA).isBefore(dayjs(startB))) { + return -1; + } +} + +export {humanizeDueDate, humanizeDueTime, humanizeDuration, sortByDay, sortByTime, sortByPrice, calculateTotalPrice}; diff --git a/src/view/filter-view.js b/src/view/filter-view.js index 0beb6ee..e893f21 100644 --- a/src/view/filter-view.js +++ b/src/view/filter-view.js @@ -1,27 +1,37 @@ import AbstractView from '../framework/view/abstract-view.js'; -import {FILTERS, DEFAULT_FILTER} from '../const.js'; -function createFilterTemplate(filterName, count) { - const filter = filterName.toLowerCase(); - const isChecked = filterName === DEFAULT_FILTER ? 'checked' : ''; +function createFilterItemTemplate(filter, isChecked) { + const {type, count} = filter; const isDisabled = count === 0 ? 'disabled' : ''; + return `