diff --git a/.eslintignore b/.eslintignore index 6de001d..956aac3 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ webpack.config.js +src/mock/* diff --git a/package-lock.json b/package-lock.json index 7075a01..b164ac5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "big-trip", "version": "22.0.0", + "dependencies": { + "dayjs": "1.11.6" + }, "devDependencies": { "@babel/core": "7.21.4", "@babel/preset-env": "7.21.4", @@ -15,6 +18,7 @@ "eslint": "8.38.0", "eslint-config-htmlacademy": "9.0.0", "html-webpack-plugin": "5.5.1", + "lodash": "4.17.21", "webpack": "5.92.0", "webpack-cli": "5.1.4", "webpack-dev-server": "4.13.3" @@ -3265,6 +3269,11 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/dayjs": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.6.tgz", + "integrity": "sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ==" + }, "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 c6b700c..2862c3c 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,15 @@ "eslint": "8.38.0", "eslint-config-htmlacademy": "9.0.0", "html-webpack-plugin": "5.5.1", + "lodash": "4.17.21", "webpack": "5.92.0", "webpack-cli": "5.1.4", "webpack-dev-server": "4.13.3" }, "engines": { "node": "20" + }, + "dependencies": { + "dayjs": "1.11.6" } } diff --git a/src/mock/destinations.js b/src/mock/destinations.js new file mode 100644 index 0000000..e6f2d9e --- /dev/null +++ b/src/mock/destinations.js @@ -0,0 +1,31 @@ +export const destinations = [ + { + id: 'bfa5cb75-a1fe-4b77-a83c-0e528e910e04', + description: 'St. Petersburg, is a beautiful city, a true asian pearl, with crowded streets.', + name: 'St. Petersburg', + pictures: [ + { + src: 'http://picsum.photos/300/200?r=0.0762563005163317', + description: 'Chamonix parliament building' + }] + }, + { + id: 'bfa5cb75-a1fe-4b77-a83c-0e528e910e05', + description: 'Paris is a major railway, highway, and air-transport hub served by two international airports.', + name: 'Paris', + pictures: [ + { + src: 'http://picsum.photos/300/200?r=0.0762563005163317', + description: 'Chamonix parliament building' + }] + }, + { + id: 'bfa5cb75-a1fe-4b77-a83c-0e528e910e06', + description: 'Moscow is a very green city, if compared to other cities of comparable size in Western Europe and North America.', + name: 'Moscow', + pictures: [ + { + src: 'http://picsum.photos/300/200?r=0.0762563005163317', + description: 'Chamonix parliament building' + }] + }]; diff --git a/src/mock/offers.js b/src/mock/offers.js new file mode 100644 index 0000000..a034b7d --- /dev/null +++ b/src/mock/offers.js @@ -0,0 +1,153 @@ +export const offers = [ + { + type: 'taxi', + offers: [ + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa31', + title: 'taxi offer 1', + price: 120 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa32', + title: 'taxi offer 2', + price: 130 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa33', + title: 'taxi offer 3', + price: 140 + }] + }, + { + type: 'bus', + offers: [ + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa34', + title: 'bus offer 1', + price: 150 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa35', + title: 'bus offer 2', + price: 160 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa36', + title: 'bus offer 3', + price: 170 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa37', + title: 'bus offer 4', + price: 180 + }] + }, + { + type: 'train', + offers: [ + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa38', + title: 'train offer 1', + price: 190 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa39', + title: 'train offer 2', + price: 200 + }] + }, + { + type: 'ship', + offers: [ + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa30', + title: 'ship offer 1', + price: 210 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa41', + title: 'ship offer 2', + price: 220 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa42', + title: 'ship offer 3', + price: 230 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa43', + title: 'ship offer 4', + price: 240 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa44', + title: 'ship offer 5', + price: 250 + }] + }, + { + type: 'drive', + offers: [ + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa45', + title: 'drive offer 1', + price: 260 + }] + }, + { + type: 'flight', + offers: [ + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa46', + title: 'flight offer 1', + price: 270 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa47', + title: 'flight offer 2', + price: 280 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa48', + title: 'flight offer 3', + price: 290 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa49', + title: 'flight offer 4', + price: 300 + }] + }, + { + type: 'check-in', + offers: [ + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa40', + title: 'chick-in offer 1', + price: 312 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa51', + title: 'chick-in offer 2', + price: 320 + }, + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa52', + title: 'chick-in offer 3', + price: 330 + }] + }, + { + type: 'sightseeing', + offers: [] + }, + { + type: 'restaurant', + offers: [ + { + id: 'b4c3e4e6-9053-42ce-b747-e281314baa53', + title: 'restaurant offer 1', + price: 340 + }] + } +]; diff --git a/src/mock/points.js b/src/mock/points.js new file mode 100644 index 0000000..d823685 --- /dev/null +++ b/src/mock/points.js @@ -0,0 +1,52 @@ +import { toCamelCase } from '../util.js' + +export const points = [ + { + id: 'f4b62099-293f-4c3d-a702-94eec4a2808c', + basePrice: 11300, + dateFrom: '2019-07-10T00:55:56.845Z', + dateTo: '2019-07-11T11:01:13.375Z', + destination: 'bfa5cb75-a1fe-4b77-a83c-0e528e910e04', + isFavorite: false, + offers: [ + 'b4c3e4e6-9053-42ce-b747-e281314baa31' + ], + type: 'taxi' + }, + { + id: 'f4b62099-293f-4c3d-a702-94eec4a2808d', + basePrice: 11200, + dateFrom: '2019-07-12T22:02:56.845Z', + dateTo: '2019-07-13T11:03:13.375Z', + destination: 'bfa5cb75-a1fe-4b77-a83c-0e528e910e05', + isFavorite: true, + offers: [ + 'b4c3e4e6-9053-42ce-b747-e281314baa34', + 'b4c3e4e6-9053-42ce-b747-e281314baa35', + 'b4c3e4e6-9053-42ce-b747-e281314baa36' + ], + type: 'bus' + }, + { + id: 'f4b62099-293f-4c3d-a702-94eec4a2808e', + basePrice: 11100, + dateFrom: '2019-07-14T22:55:56.845Z', + dateTo: '2019-07-15T11:22:13.375Z', + destination: 'bfa5cb75-a1fe-4b77-a83c-0e528e910e06', + isFavorite: false, + offers: [], + type: 'ship' + }]; + +export const defaultPoint = [ + { + id: '000', + basePrice: '000', + dateFrom: '2001-01-01T00:00:00.845Z', + dateTo: '2001-01-01T00:00:00.845Z', + destination: '', + isFavorite: false, + offers: [], + type: 'flight' + } +] diff --git a/src/model/point-model.js b/src/model/point-model.js new file mode 100644 index 0000000..231a807 --- /dev/null +++ b/src/model/point-model.js @@ -0,0 +1,35 @@ +import { points, defaultPoint } from '../mock/points.js'; +import { destinations } from '../mock/destinations.js'; +import { offers } from '../mock/offers.js'; + +export default class PointModel { + constructor() { + this.points = []; + this.destinations = []; + this.offers = []; + this.defaultPoint = []; + } + + init() { + this.points = points; + this.destinations = destinations; + this.offers = offers; + this.defaultPoint = defaultPoint; + } + + getPoints() { + return this.points; + } + + getDestinations() { + return this.destinations; + } + + getOffers() { + return this.offers; + } + + getDefaultPoint() { + return this.defaultPoint; + } +} diff --git a/src/presenter/header-presenter.js b/src/presenter/header-presenter.js index 8f2b758..60a9121 100644 --- a/src/presenter/header-presenter.js +++ b/src/presenter/header-presenter.js @@ -1,6 +1,6 @@ import { render, RenderPosition } from '../render.js'; -import Filters from '../view/filters.js'; -import TripInfo from '../view/trip-info.js'; +import FiltersView from '../view/filters-view.js'; +import TripInfoView from '../view/trip-info-view.js'; export default class HeaderPresenter { constructor ({ container }) { @@ -8,10 +8,10 @@ export default class HeaderPresenter { } initInfo () { - render (new TripInfo, this.container, RenderPosition.AFTERBEGIN); + render (new TripInfoView, this.container, RenderPosition.AFTERBEGIN); } initFilters () { - render(new Filters, this.container); + render(new FiltersView, this.container); } } diff --git a/src/presenter/trip-presenter.js b/src/presenter/trip-presenter.js index b3bd27f..4c63f24 100644 --- a/src/presenter/trip-presenter.js +++ b/src/presenter/trip-presenter.js @@ -1,25 +1,33 @@ import { render } from '../render.js'; -import CreateForm from '../view/create-form.js'; -import EditForm from '../view/edit-form.js'; -import Sorting from '../view/sorting.js'; -import TripList from '../view/trip-list.js'; -import TripPoint from '../view/trip-point.js'; +import CreateFormView from '../view/create-form-view.js'; +import EditFormView from '../view/edit-form-view.js'; +import SortingView from '../view/sorting-view.js'; +import TripListView from '../view/trip-list-view.js'; +import TripPointView from '../view/trip-point-view.js'; +import PointModel from '../model/point-model.js'; export default class TripPresenter { - tripList = new TripList(); + tripList = new TripListView(); - constructor ({ container }) { + constructor ({ container, pointModel = new PointModel }) { this.container = container; + this.pointModel = pointModel; } init () { - render(new Sorting, this.container); + this.pointModel.init(); + const points = this.pointModel.getPoints(); + const destinations = this.pointModel.getDestinations(); + const offers = this.pointModel.getOffers(); + const defaultPoint = this.pointModel.getDefaultPoint(); + + render(new SortingView, this.container); render(this.tripList, this.container); - render(new EditForm, this.tripList.getElement()); - render(new CreateForm, this.tripList.getElement()); + render(new EditFormView(points[0], destinations, offers), this.tripList.getElement()); + render(new CreateFormView(defaultPoint[0], destinations, offers), this.tripList.getElement()); - for (let i = 0; i < 3; i++) { - render (new TripPoint, this.tripList.getElement()); - } + points.forEach((point) => { + render(new TripPointView(point, destinations, offers), this.tripList.getElement()); + }); } } diff --git a/src/util.js b/src/util.js new file mode 100644 index 0000000..0e3229f --- /dev/null +++ b/src/util.js @@ -0,0 +1,28 @@ +import dayjs from 'dayjs'; +import lodash from 'lodash'; + +const humanizeDueDate = (dueDate, dateFormat) => dueDate ? dayjs(dueDate).format(dateFormat) : ''; + +const capitalize = (str) => str[0].toUpperCase() + str.slice(1); + +const findDuration = (date1, date2) => { + let minutesDuration = dayjs(date1).diff(dayjs(date2), 'm'); + const minutesAfterHours = minutesDuration % 60; + + if (minutesDuration >= 60 && minutesAfterHours !== 0) { + minutesDuration /= 60; + + return `${Math.floor(minutesDuration)}H ${minutesAfterHours}M`; + } + + if (minutesDuration >= 60) { + minutesDuration /= 60; + } + + return `${Math.floor(minutesDuration)}H`; +}; + +const toCamelCase = (str) => lodash.camelCase(str); + + +export { humanizeDueDate, capitalize, findDuration, toCamelCase }; diff --git a/src/view/create-form.js b/src/view/create-form-view.js similarity index 68% rename from src/view/create-form.js rename to src/view/create-form-view.js index 044dd82..2f7d775 100644 --- a/src/view/create-form.js +++ b/src/view/create-form-view.js @@ -1,13 +1,22 @@ import { createElement } from '../render.js'; +import { capitalize, humanizeDueDate } from '../util.js'; -const createFormTemplate = () => ` -