diff --git a/package-lock.json b/package-lock.json index 313fabb..0498669 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "big-trip", "version": "22.0.0", + "dependencies": { + "dayjs": "1.11.13" + }, "devDependencies": { "@babel/core": "7.21.4", "@babel/preset-env": "7.21.4", @@ -20,7 +23,7 @@ "webpack-dev-server": "4.13.3" }, "engines": { - "node": "20" + "node": "18.20.5" } }, "node_modules/@ampproject/remapping": { @@ -3325,6 +3328,12 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/package.json b/package.json index 12ed2cc..b118b2a 100644 --- a/package.json +++ b/package.json @@ -29,5 +29,8 @@ }, "engines": { "node": "18.20.5" + }, + "dependencies": { + "dayjs": "1.11.13" } } diff --git a/src/main.js b/src/main.js index b4fbb26..e25fdd7 100644 --- a/src/main.js +++ b/src/main.js @@ -1,8 +1,11 @@ import BoardPresenter from './presenter/board-presenter.js'; +import Model from './model/model.js'; const filtersContainer = document.querySelector('.trip-controls__filters'); const eventsContainer = document.querySelector('.trip-events'); -const boardPresenter = new BoardPresenter({filtersContainer, eventsContainer}); +const model = new Model(); +const boardPresenter = new BoardPresenter({filtersContainer, eventsContainer, model}); boardPresenter.init(); + diff --git a/src/mocks/destinations.js b/src/mocks/destinations.js new file mode 100644 index 0000000..f322103 --- /dev/null +++ b/src/mocks/destinations.js @@ -0,0 +1,46 @@ +export const destinations = + [ + { + id: '1', + description: 'Chamonix, is a beautiful city, a true asian pearl, with crowded streets.', + name: 'Chamonix', + pictures: [ + { + src: 'http://picsum.photos/300/200?r=0.0762563005163317', + description: 'Chamonix parliament building' + } + ] + }, + { + id: '2', + description: 'Bangkok is a vibrant metropolis known for its ornate temples, bustling street life.', + name: 'Bangkok', + pictures: [ + { + src: 'https://en.wikipedia.org/wiki/King_Power_Mahanakhon#/media/File:Tha%C3%AFlande_Bangkok_MahaNakhon.jpg', + description: 'King Power Mahanakhon' + } + ] + }, + { + id: '3', + description: 'Istanbul is a vibrant city bridging Europe and Asia.', + name: 'Istanbul', + pictures: [ + { + src: 'http://picsum.photos/300/200?r=0.0762563005163317', + description: 'Istanbul Grand Bazaar' + } + ] + }, + { + id: '4', + description: 'Amsterdam is a picturesque city known for its canals.', + name: 'Amsterdam', + pictures: [] + } + ]; + +export function getDestinations (){ + return destinations; +} diff --git a/src/mocks/events.js b/src/mocks/events.js new file mode 100644 index 0000000..b688a94 --- /dev/null +++ b/src/mocks/events.js @@ -0,0 +1,48 @@ +export const events = [ + { + id: '1', + basePrice: 200, + dateFrom: '2024-11-10T22:55:56', + dateTo: '2024-11-11T11:22:13', + destination: '3', + isFavorite: false, + offers: ['1', '4'], + type: 'taxi' + }, + { + id: '2', + basePrice: 90, + dateFrom: '2024-11-12T22:55:56', + dateTo: '2024-11-13T11:22:13', + destination: '1', + isFavorite: false, + offers: ['2', '4'], + type: 'bus' + }, + { + id: '3', + basePrice: 900, + dateFrom: '2024-11-14T02:42:56', + dateTo: '2024-11-14T11:22:13', + destination: '2', + isFavorite: true, + offers: ['1', '2'], + type: 'flight' + }, + { + id: '4', + basePrice: 300, + dateFrom: '2024-11-16T22:55:56', + dateTo: '2024-11-17T11:22:13', + destination: '4', + isFavorite: false, + offers: [], + type: 'train' + } +]; + +export function getEvents (){ + return events; +} + + diff --git a/src/mocks/offers.js b/src/mocks/offers.js new file mode 100644 index 0000000..5c0fd8c --- /dev/null +++ b/src/mocks/offers.js @@ -0,0 +1,100 @@ +export const offers = [ + { + type: 'taxi', + offers: [ + { + id: '1', + title: 'Upgrade to a business class', + price: 120 + }, + { + id: '2', + title: 'Upgrade to a limousine', + price: 300 + }, + { + id: '3', + title: 'Upgrade to a minivan', + price: 30 + }, + { + id: '4', + title: 'oversized luggage', + price: 60 + }, + { + id: '5', + title: 'chair for child', + price: 30 + } + ] + }, + { + type: 'bus', + offers: [] + }, + { + type: 'flight', + offers: [ + { + id: '1', + title: 'Upgrade to a business class', + price: 500 + }, + { + id: '2', + title: 'Upgrade to a first class', + price: 1000 + }, + { + id: '3', + title: 'Platinum last minute upgrade.', + price: 20 + }, + { + id: '4', + title: 'luggage +10kg', + price: 40 + } + ] + }, + { + type: 'train', + offers: [ + { + id: '1', + title: 'Upgrade to a business class', + price: 120 + }, + { + id: '2', + title: 'Upgrade to a luxury class', + price: 120 + }, + { + id: '3', + title: 'shower', + price: 10 + }, + { + id: '4', + title: 'Dinner', + price: 20 + }, + { + id: '5', + title: '', + price: 120 + }, + { + id: '6', + title: 'Upgrade to a business class', + price: 120 + } + ] + } +]; + +export function getOffers(){ + return offers; +} diff --git a/src/model/model.js b/src/model/model.js new file mode 100644 index 0000000..d16ff95 --- /dev/null +++ b/src/model/model.js @@ -0,0 +1,23 @@ +import {events} from '../mocks/events.js'; +import {destinations} from '../mocks/destinations.js'; +import {offers} from '../mocks/offers.js'; + +export default class Model{ + + events = events; + destinations = destinations; + offers = offers; + + getEvents (){ + return this.events; + } + + getDestinations (){ + return this.destinations; + } + + getOffers(){ + return this.offers; + } +} + diff --git a/src/presenter/board-presenter.js b/src/presenter/board-presenter.js index 1d389e2..4780ec8 100644 --- a/src/presenter/board-presenter.js +++ b/src/presenter/board-presenter.js @@ -3,7 +3,7 @@ import FiltersView from '../view/filters-view.js'; import SortsView from '../view/sorts-view.js'; import EventListView from '../view/events-list-view.js'; import EventEditView from '../view/event-edit-view.js'; -import EventsItemsView from '../view/events-items-view.js'; +import EventItemView from '../view/event-item-view.js'; import EventView from '../view/event-view.js'; export default class BoardPresenter{ @@ -13,21 +13,27 @@ export default class BoardPresenter{ eventListView = new EventListView(); eventView = new EventView(); - constructor({filtersContainer, eventsContainer}){ + constructor({filtersContainer, eventsContainer, model}){ + this.model = model; this.filtersContainer = filtersContainer; this.eventsContainer = eventsContainer; } init(){ + const destinations = this.model.getDestinations(); + const offers = this.model.getOffers(); + const events = this.model.getEvents(); + render(new FiltersView(), this.filtersContainer); render(new SortsView(), this.eventsContainer); render(this.eventListView, this.eventsContainer, RenderPosition.AFTEREND); - render(new EventEditView(), this.eventListView.getElement(), RenderPosition.AFTERBEGIN); - render(new EventView(), this.eventListView.getElement(), RenderPosition.AFTERBEGIN); + render(new EventEditView(events[0], destinations, offers), this.eventListView.getElement(), RenderPosition.AFTERBEGIN); + //render(new EventView(), this.eventListView.getElement(), RenderPosition.AFTERBEGIN); - for(let i = 0; i < 3; i += 1){ - render(new EventsItemsView(), this.eventListView.getElement()); + for (let i = 0; i < events.length ; i += 1) { + const event = events[i]; + render(new EventItemView(event, destinations, offers), this.eventListView.getElement()); } } } diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..654a987 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,34 @@ +import dayjs from 'dayjs'; + +const DATE_FORMAT = 'MMM DD'; +const TIME_FORMAT = 'HH:mm'; + +function humanizePointDueTime(dueDate) { + return dueDate ? dayjs(dueDate).format(TIME_FORMAT) : ''; +} + +function humanizePointDueDate(dueDate) { + return dueDate ? dayjs(dueDate).format(DATE_FORMAT) : ''; +} + +function getDiferenceTime(dateFrom, dateTo){ + const startDate = dayjs(dateFrom); + const endDate = dayjs(dateTo); + const difference = endDate.diff(startDate); + const differenceHours = Math.floor(difference / (1000 * 60 * 60)); + const differenceMin = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60)); + const differenceDays = Math.ceil(differenceHours / 24); + + let differenceTime = `${differenceDays}D ${differenceHours }H ${differenceMin}M`; + if(differenceDays === 0){ + differenceTime = `${differenceHours }H ${differenceMin }M`; + } + if(differenceDays === 0 && differenceHours === 0){ + differenceTime = `${differenceMin}M`; + } + return differenceTime; +} + +const capitalize = (word) => word[0].toUpperCase() + word.slice(1); + +export {humanizePointDueDate, humanizePointDueTime, getDiferenceTime, capitalize}; diff --git a/src/view/event-edit-view.js b/src/view/event-edit-view.js index 27c883a..befc463 100644 --- a/src/view/event-edit-view.js +++ b/src/view/event-edit-view.js @@ -1,159 +1,130 @@ import {createElement} from '../render.js'; +import { getEvents } from '../mocks/events.js'; +import { humanizePointDueTime, capitalize } from '../utils'; + +function createEventEditTemplate(event, destinations, offers){ + + const pointDestination = destinations.find((destination) => destination.id === event.destination); + const typeOffers = offers.find((offer) => offer.type === event.type).offers; + const pointOffers = typeOffers.filter((typeOffer) => event.offers.includes(typeOffer.id)); + const {basePrice, dateFrom, dateTo, type} = event; + const {name, description, pictures} = pointDestination; + const eventId = event.id; + + const allEventTypes = new Set(); + getEvents().forEach((getEvent) => allEventTypes.add(getEvent.type)); + const eventTypes = [...allEventTypes]; -function createEventEditTemplate(){ return `
  • -
    -
    -
    - - - -
    -
    - Event type - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    -
    -
    -
    - -
    - - - - - - - -
    - -
    - - - — - - -
    - -
    - - -
    - - - -
    -
    -
    -

    Offers

    - -
    -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    -
    -
    -
    -
    -
  • `; +
    +
    +
    + + + +
    +
    + Event type + ${eventTypes.map((eventType) => ( + `
    + + +
    ` + )).join('')} +
    +
    +
    + +
    + + + + ${destinations.map((destination) => ``).join('')} + +
    + +
    + + + — + + +
    + +
    + + +
    + + + + ${event.id ? ( + `` + ) : ''} +
    + +
    + ${typeOffers.length > 0 ? + `
    +

    Offers

    + +
    + + ${typeOffers.map((typeOffer)=>( + `
    + offer.id === typeOffer.id) ? 'checked' : ''}> + +
    ` + )).join('')} +
    +
    ` + : ''} + ${pointDestination ? ( + `
    +

    Destination

    +

    ${description}

    + ${pictures.length > 0 ? ( + `
    +
    + ${pictures.map((picture) => `${picture.description}`)} +
    +
    ` + ) : ''} +
    ` + ) : ''} +
    +
    + `; } - export default class EventEditView{ + + constructor (events, destinations, offers){ + this.events = events; + this.destinations = destinations; + this.offers = offers; + } + getTemplate(){ - return createEventEditTemplate(); + + return createEventEditTemplate(this.events, this.destinations, this.offers); } getElement(){ diff --git a/src/view/event-item-view.js b/src/view/event-item-view.js new file mode 100644 index 0000000..f77d110 --- /dev/null +++ b/src/view/event-item-view.js @@ -0,0 +1,85 @@ +import {createElement} from '../render.js'; +import { humanizePointDueDate, humanizePointDueTime } from '../utils'; +import {getDiferenceTime} from '../utils.js'; + +function createEventOffersTemplate(eventOffers) { + return eventOffers.map((eventOffer) => ( + `
  • + ${eventOffer.title} + +€  + ${eventOffer.price} +
  • ` + )).join(''); +} + +function createEventItemTemplate(event, destinations, offers){ + + const {basePrice, isFavorite, dateFrom, dateTo, type} = event; + + const typeOffers = offers.find((offer) => offer.type === event.type).offers; + const eventOffers = typeOffers.filter((typeOffer) => event.offers.includes(typeOffer.id)); + const eventDestination = destinations.filter((destination) => destination.id === event.destination); + + const diferenceTime = getDiferenceTime(dateFrom, dateTo); + + return` +
  • +
    + +
    + Event type icon +
    +

    ${type} ${eventDestination[0].name}

    +
    +

    + + — + +

    +

    ${diferenceTime}

    +
    +

    + € ${basePrice} +

    +

    Offers:

    + + + +
    +
  • +`; +} + +export default class EventItemView{ + constructor (event, destinations, offers){ + this.event = event; + this.destinations = destinations; + this.offers = offers; + } + + getTemplate(){ + return createEventItemTemplate(this.event, this.destinations, this.offers); + } + + getElement(){ + if(!this.element){ + this.element = createElement(this.getTemplate()); + } + return this.element; + } + + removeElement(){ + this.element = null; + } +} + + diff --git a/src/view/event-view.js b/src/view/event-view.js index 2ad8110..189caa7 100644 --- a/src/view/event-view.js +++ b/src/view/event-view.js @@ -1,6 +1,7 @@ import {createElement} from '../render.js'; function createEventTemplate(){ + return`
  • @@ -168,6 +169,8 @@ function createEventTemplate(){ } export default class EventView{ + + getTemplate(){ return createEventTemplate(); } diff --git a/src/view/events-items-view.js b/src/view/events-items-view.js deleted file mode 100644 index dc0736e..0000000 --- a/src/view/events-items-view.js +++ /dev/null @@ -1,62 +0,0 @@ -import {createElement} from '../render.js'; - -function createEventsItemsTemplate(){ - return` -
  • -
    - -
    - Event type icon -
    -

    Taxi Amsterdam

    -
    -

    - - — - -

    -

    30M

    -
    -

    - € 20 -

    -

    Offers:

    -
      -
    • - Order Uber - +€  - 20 -
    • -
    - - -
    -
  • - `; -} - -export default class EventsItemsView{ - getTemplate(){ - return createEventsItemsTemplate(); - } - - getElement(){ - if(!this.element){ - this.element = createElement(this.getTemplate()); - } - return this.element; - } - - removeElement(){ - this.element = null; - } -} - - diff --git a/src/view/sorts-view.js b/src/view/sorts-view.js index 60081be..51effaa 100644 --- a/src/view/sorts-view.js +++ b/src/view/sorts-view.js @@ -1,6 +1,6 @@ import {createElement} from '../render.js'; -function createSortTemplateView(){ +function createSortTemplate(){ return `
    @@ -33,7 +33,7 @@ function createSortTemplateView(){ export default class SortsView{ getTemplate(){ - return createSortTemplateView(); + return createSortTemplate(); } getElement(){