diff --git a/mocks/const.js b/mocks/const.js deleted file mode 100644 index e69de29..0000000 diff --git a/mocks/destination.js b/mocks/destination.js new file mode 100644 index 0000000..9ec2a54 --- /dev/null +++ b/mocks/destination.js @@ -0,0 +1,108 @@ +export const DESTINATIONS = [ + { + id: 'c7fcf894-e8ef-4347-94cf-8c76aa6c6833', + description: 'Chamonix - a perfect place to stay with a family', + name: 'Chamonix', + pictures: [] + }, + { + id: '82a3bad6-6498-4989-ae4f-815082508c30', + description: '', + name: 'Venice', + pictures: [] + }, + { + id: 'ec9110f7-4767-4179-b856-5d8bb23324ec', + description: '', + name: 'Moscow', + pictures: [] + }, + { + id: '070ea513-2210-42df-bad4-ede76ba22a3e', + description: 'Sochi - is a beautiful city', + name: 'Sochi', + pictures: [] + }, + { + id: 'e4bc89ed-4df2-40a4-b8d1-21b953502799', + description: 'Berlin - for those who value comfort and coziness', + name: 'Berlin', + pictures: [ + { + src: 'https://22.objects.htmlacademy.pro/static/destinations/18.jpg', + description: 'Berlin a perfect place to stay with a family' + }, + { + src: 'https://22.objects.htmlacademy.pro/static/destinations/13.jpg', + description: 'Berlin with a beautiful old town' + } + ] + }, + { + id: '13a37338-b4b1-466e-aafe-092f3c247708', + description: 'Geneva - full of of cozy canteens where you can try the best coffee in the Middle East', + name: 'Geneva', + pictures: [ + { + src: 'https://22.objects.htmlacademy.pro/static/destinations/18.jpg', + description: 'Geneva middle-eastern paradise' + }, + { + src: 'https://22.objects.htmlacademy.pro/static/destinations/4.jpg', + description: 'Geneva a true asian pearl' + }, + { + src: 'https://22.objects.htmlacademy.pro/static/destinations/8.jpg', + description: 'Geneva with crowded streets' + } + ] + }, + { + id: '64a8701c-632e-48ae-9052-353c1a3465df', + description: 'Frankfurt - for those who value comfort and coziness', + name: 'Frankfurt', + pictures: [ + { + src: 'https://22.objects.htmlacademy.pro/static/destinations/10.jpg', + description: 'Frankfurt a perfect place to stay with a family' + } + ] + }, + { + id: 'fdd837e1-fd2d-463c-9a2d-555cf8efa89e', + description: 'Valencia - a true asian pearl', + name: 'Valencia', + pictures: [ + { + src: 'https://22.objects.htmlacademy.pro/static/destinations/20.jpg', + description: 'Valencia with an embankment of a mighty river as a centre of attraction' + }, + { + src: 'https://22.objects.htmlacademy.pro/static/destinations/15.jpg', + description: 'Valencia for those who value comfort and coziness' + }, + { + src: 'https://22.objects.htmlacademy.pro/static/destinations/16.jpg', + description: 'Valencia famous for its crowded street markets with the best street food in Asia' + } + ] + }, + { + id: 'fed714ef-b342-45dc-8788-633def721433', + description: '', + name: 'Monaco', + pictures: [] + }, + { + id: '466c2985-2db8-47fa-85e4-0d07cb9e48fe', + description: 'Vien - is a beautiful city', + name: 'Vien', + pictures: [ + { + src: 'https://22.objects.htmlacademy.pro/static/destinations/1.jpg', + description: 'Vien a true asian pearl' + } + ] + } +]; + diff --git a/mocks/generate-data.js b/mocks/generate-data.js new file mode 100644 index 0000000..2e8a6cf --- /dev/null +++ b/mocks/generate-data.js @@ -0,0 +1,9 @@ +import { mix } from '../src/utils'; +import { DESTINATIONS } from './destination'; +import { OFFERS } from './offer'; +import { POINTS } from './point'; + +const EVENT_QUANTITY = 5; + +export const generateTripData = (quantity = EVENT_QUANTITY) => [mix(POINTS, quantity), OFFERS, DESTINATIONS]; + diff --git a/mocks/offer.js b/mocks/offer.js new file mode 100644 index 0000000..93fb0e5 --- /dev/null +++ b/mocks/offer.js @@ -0,0 +1,206 @@ +export const OFFERS = [ + { + type: 'taxi', + offers: [ + { + id: 'df01f087-8af8-4024-ade2-434e572a1939', + title: 'Upgrade to a business class', + price: 187 + }, + { + id: '0690c5cc-879c-4aa5-80c1-244935547967', + title: 'Choose the radio station', + price: 193 + }, + { + id: 'bba6b2a4-115c-4470-9e70-bab52e7b6da9', + title: 'Choose temperature', + price: 36 + }, + { + id: '61a27c8e-492d-4194-8d09-c85e8fbc0ed2', + title: 'Drive quickly, I\'m in a hurry', + price: 133 + }, + { + id: '428136ce-b310-4e4e-bb3e-e09946178b81', + title: 'Drive slowly', + price: 50 + } + ] + }, + { + type: 'bus', + offers: [ + { + id: '3529d1ca-96d4-4530-8cf5-3f3b0f570ed4', + title: 'Infotainment system', + price: 68 + }, + { + id: '32f9d674-3739-427f-903f-daa3c2cf722b', + title: 'Order meal', + price: 116 + }, + { + id: '199e8e51-dc14-4795-9638-5f707452da51', + title: 'Choose seats', + price: 134 + } + ] + }, + { + type: 'train', + offers: [ + { + id: 'b64cbb73-09c3-4d68-be36-95d4478d344c', + title: 'Book a taxi at the arrival point', + price: 42 + }, + { + id: 'f7429a41-90e3-448e-af6a-91f77c1d1f49', + title: 'Order a breakfast', + price: 139 + }, + { + id: '5093df18-a4d0-42f7-b9cd-b6ef378563fa', + title: 'Wake up at a certain time', + price: 69 + } + ] + }, + { + type: 'flight', + offers: [ + { + id: '9b77d7bc-4650-465d-98da-c12fe7f3495c', + title: 'Choose meal', + price: 63 + }, + { + id: '1021812a-82b3-4fb5-a657-51d6b005d366', + title: 'Choose seats', + price: 122 + }, + { + id: '4dc8e801-b123-489e-816a-1b900e12d270', + title: 'Upgrade to comfort class', + price: 157 + }, + { + id: '151f488d-e03a-4347-8837-2e7171061983', + title: 'Upgrade to business class', + price: 140 + }, + { + id: '7511ff9e-227d-4732-9e27-d4cd1b78c220', + title: 'Add luggage', + price: 158 + }, + { + id: '52cfccc0-c186-4dea-9fe4-b04528650af9', + title: 'Business lounge', + price: 41 + } + ] + }, + { + type: 'check-in', + offers: [ + { + id: 'e3dbdc12-23b8-453f-afd1-e0dcb281bf7a', + title: 'Choose the time of check-in', + price: 195 + }, + { + id: '56b72afa-d7d0-4dab-9d80-e64a2442783e', + title: 'Choose the time of check-out', + price: 155 + }, + { + id: '73522a17-f05d-49b9-a141-5ebccae6fb24', + title: 'Add breakfast', + price: 107 + }, + { + id: 'd658acef-46f7-4c88-b7ed-df131ffabe0c', + title: 'Laundry', + price: 183 + }, + { + id: '4a956f99-a055-4143-969f-b29f2be5da50', + title: 'Order a meal from the restaurant', + price: 101 + } + ] + }, + { + type: 'sightseeing', + offers: [] + }, + { + type: 'ship', + offers: [ + { + id: 'ed0ddafb-1f70-4d26-b212-c40f42b92c4f', + title: 'Choose meal', + price: 146 + }, + { + id: '35aa9a64-dbfd-4059-bb77-117e839bfcc2', + title: 'Choose seats', + price: 60 + }, + { + id: '0a72ec19-d026-4a7b-9158-f00431ff883f', + title: 'Upgrade to comfort class', + price: 160 + }, + { + id: '1ca8aa30-0131-4b9f-956a-b7f90d2bf6a8', + title: 'Upgrade to business class', + price: 57 + }, + { + id: '00fdf487-91f5-4175-97cc-4b8cf30240a0', + title: 'Add luggage', + price: 81 + }, + { + id: '6e21cc5e-e5ef-453d-a401-86846f294d02', + title: 'Business lounge', + price: 146 + } + ] + }, + { + type: 'drive', + offers: [ + { + id: '50cd2308-6764-4111-9800-4027c4516b90', + title: 'With automatic transmission', + price: 34 + }, + { + id: 'd82abaa6-4c4e-4bb4-a49d-83e80c37cdd5', + title: 'With air conditioning', + price: 101 + } + ] + }, + { + type: 'restaurant', + offers: [ + { + id: '89b4eb84-3d09-475f-814a-879bcf96a900', + title: 'Choose live music', + price: 110 + }, + { + id: 'd1d46527-003a-43e2-a3c1-714db831a54e', + title: 'Choose VIP area', + price: 33 + } + ] + } +]; diff --git a/mocks/point.js b/mocks/point.js new file mode 100644 index 0000000..392e68e --- /dev/null +++ b/mocks/point.js @@ -0,0 +1,320 @@ +export const POINTS = [ + { + id: '8220b3c4-7c8b-43b9-a36c-87860c29a201', + basePrice: 7863, + dateFrom: '2024-12-03T11:14:08.471Z', + dateTo: '2024-12-05T04:59:08.471Z', + destination: '82a3bad6-6498-4989-ae4f-815082508c30', + isFavorite: false, + offers: [], + type: 'sightseeing' + }, + { + id: '7bd944c8-0d45-426b-aaf7-a87f096e8c53', + basePrice: 67, + dateFrom: '2024-12-06T08:46:08.471Z', + dateTo: '2024-12-06T16:52:08.471Z', + destination: '070ea513-2210-42df-bad4-ede76ba22a3e', + isFavorite: false, + offers: [ + '89b4eb84-3d09-475f-814a-879bcf96a900', + 'd1d46527-003a-43e2-a3c1-714db831a54e' + ], + type: 'restaurant' + }, + { + id: '33280e36-1490-4a7a-b497-d2c0e38f5442', + basePrice: 3087, + dateFrom: '2024-12-07T04:06:08.471Z', + dateTo: '2024-12-09T00:49:08.471Z', + destination: '466c2985-2db8-47fa-85e4-0d07cb9e48fe', + isFavorite: true, + offers: [], + type: 'sightseeing' + }, + { + id: '939e4b88-b4d0-49e7-97bb-3544cc12e1a1', + basePrice: 2921, + dateFrom: '2024-12-09T14:04:08.471Z', + dateTo: '2024-12-10T12:35:08.471Z', + destination: 'ec9110f7-4767-4179-b856-5d8bb23324ec', + isFavorite: false, + offers: [], + type: 'sightseeing' + }, + { + id: '8319ffe9-6f6b-4fc7-bc7e-6266c4ac0fab', + basePrice: 1499, + dateFrom: '2024-12-10T18:44:08.471Z', + dateTo: '2024-12-12T16:09:08.471Z', + destination: 'e4bc89ed-4df2-40a4-b8d1-21b953502799', + isFavorite: true, + offers: [ + '35aa9a64-dbfd-4059-bb77-117e839bfcc2', + '0a72ec19-d026-4a7b-9158-f00431ff883f', + '1ca8aa30-0131-4b9f-956a-b7f90d2bf6a8', + '00fdf487-91f5-4175-97cc-4b8cf30240a0', + '6e21cc5e-e5ef-453d-a401-86846f294d02' + ], + type: 'ship' + }, + { + id: 'b4fae42a-bfa8-48eb-8b53-c01c591185b6', + basePrice: 1350, + dateFrom: '2024-12-13T19:10:08.471Z', + dateTo: '2024-12-15T17:23:08.471Z', + destination: '64a8701c-632e-48ae-9052-353c1a3465df', + isFavorite: false, + offers: [ + '199e8e51-dc14-4795-9638-5f707452da51' + ], + type: 'bus' + }, + { + id: '58bae41c-eb1d-4ef5-ac46-b22681b052b1', + basePrice: 3905, + dateFrom: '2024-12-17T11:38:08.471Z', + dateTo: '2024-12-17T21:26:08.471Z', + destination: '82a3bad6-6498-4989-ae4f-815082508c30', + isFavorite: false, + offers: [ + '73522a17-f05d-49b9-a141-5ebccae6fb24', + 'd658acef-46f7-4c88-b7ed-df131ffabe0c', + '4a956f99-a055-4143-969f-b29f2be5da50' + ], + type: 'check-in' + }, + { + id: 'ab96a7c9-cd35-4ae0-8ca6-33e50c3376a5', + basePrice: 8353, + dateFrom: '2024-12-19T14:12:08.471Z', + dateTo: '2024-12-21T11:32:08.471Z', + destination: 'c7fcf894-e8ef-4347-94cf-8c76aa6c6833', + isFavorite: false, + offers: [ + 'd1d46527-003a-43e2-a3c1-714db831a54e' + ], + type: 'restaurant' + }, + { + id: 'bcc01238-cfd9-4432-b53e-fcea1b958e08', + basePrice: 330, + dateFrom: '2024-12-21T23:11:08.471Z', + dateTo: '2024-12-23T20:10:08.471Z', + destination: 'ec9110f7-4767-4179-b856-5d8bb23324ec', + isFavorite: false, + offers: [ + 'd82abaa6-4c4e-4bb4-a49d-83e80c37cdd5' + ], + type: 'drive' + }, + { + id: '91214a0f-dec3-4402-873c-a66f63a024f4', + basePrice: 1387, + dateFrom: '2024-12-25T07:41:08.471Z', + dateTo: '2024-12-27T08:06:08.471Z', + destination: '82a3bad6-6498-4989-ae4f-815082508c30', + isFavorite: false, + offers: [], + type: 'bus' + }, + { + id: 'fc4b109b-7a99-444d-a583-caa344411cb8', + basePrice: 12, + dateFrom: '2024-12-27T21:44:08.471Z', + dateTo: '2024-12-29T10:43:08.471Z', + destination: 'ec9110f7-4767-4179-b856-5d8bb23324ec', + isFavorite: false, + offers: [ + 'e3dbdc12-23b8-453f-afd1-e0dcb281bf7a', + '56b72afa-d7d0-4dab-9d80-e64a2442783e', + '73522a17-f05d-49b9-a141-5ebccae6fb24', + 'd658acef-46f7-4c88-b7ed-df131ffabe0c', + '4a956f99-a055-4143-969f-b29f2be5da50' + ], + type: 'check-in' + }, + { + id: '0de5090a-72e9-418b-89b0-5926aea97d4a', + basePrice: 4884, + dateFrom: '2024-12-31T10:15:08.471Z', + dateTo: '2025-01-01T23:58:08.471Z', + destination: '82a3bad6-6498-4989-ae4f-815082508c30', + isFavorite: false, + offers: [], + type: 'drive' + }, + { + id: '4df8e4e0-bbca-4b72-91c3-ef2654b4d60f', + basePrice: 6597, + dateFrom: '2025-01-02T10:15:08.471Z', + dateTo: '2025-01-02T19:36:08.471Z', + destination: '070ea513-2210-42df-bad4-ede76ba22a3e', + isFavorite: false, + offers: [ + '6e21cc5e-e5ef-453d-a401-86846f294d02' + ], + type: 'ship' + }, + { + id: 'da0271c3-fc37-4eaa-a2be-290c8903b02f', + basePrice: 7863, + dateFrom: '2025-01-03T22:15:08.471Z', + dateTo: '2025-01-04T19:50:08.471Z', + destination: 'fdd837e1-fd2d-463c-9a2d-555cf8efa89e', + isFavorite: true, + offers: [ + '5093df18-a4d0-42f7-b9cd-b6ef378563fa' + ], + type: 'train' + }, + { + id: '5a73c621-2544-4995-990e-3ffaf72464f9', + basePrice: 9488, + dateFrom: '2025-01-05T19:34:08.471Z', + dateTo: '2025-01-06T16:19:08.471Z', + destination: '466c2985-2db8-47fa-85e4-0d07cb9e48fe', + isFavorite: false, + offers: [ + 'df01f087-8af8-4024-ade2-434e572a1939', + '0690c5cc-879c-4aa5-80c1-244935547967', + 'bba6b2a4-115c-4470-9e70-bab52e7b6da9', + '61a27c8e-492d-4194-8d09-c85e8fbc0ed2', + '428136ce-b310-4e4e-bb3e-e09946178b81' + ], + type: 'taxi' + }, + { + id: 'cf1362d0-2e46-4f39-aa39-85e2c602b7e8', + basePrice: 6644, + dateFrom: '2025-01-08T06:43:08.471Z', + dateTo: '2025-01-09T13:18:08.471Z', + destination: '82a3bad6-6498-4989-ae4f-815082508c30', + isFavorite: false, + offers: [], + type: 'sightseeing' + }, + { + id: 'c6b22ee7-7613-4853-8baf-291c479d879a', + basePrice: 5731, + dateFrom: '2025-01-10T14:06:08.471Z', + dateTo: '2025-01-12T12:22:08.471Z', + destination: '070ea513-2210-42df-bad4-ede76ba22a3e', + isFavorite: true, + offers: [ + '1021812a-82b3-4fb5-a657-51d6b005d366', + '4dc8e801-b123-489e-816a-1b900e12d270', + '151f488d-e03a-4347-8837-2e7171061983', + '7511ff9e-227d-4732-9e27-d4cd1b78c220', + '52cfccc0-c186-4dea-9fe4-b04528650af9' + ], + type: 'flight' + }, + { + id: '0e475bfd-d99a-412c-8270-efab3cfe7399', + basePrice: 3306, + dateFrom: '2025-01-14T04:58:08.471Z', + dateTo: '2025-01-14T18:23:08.471Z', + destination: 'ec9110f7-4767-4179-b856-5d8bb23324ec', + isFavorite: true, + offers: [ + '00fdf487-91f5-4175-97cc-4b8cf30240a0', + '6e21cc5e-e5ef-453d-a401-86846f294d02' + ], + type: 'ship' + }, + { + id: '4402d983-2d5e-46da-8c96-3c06f2b24e51', + basePrice: 1890, + dateFrom: '2025-01-15T13:06:08.471Z', + dateTo: '2025-01-16T01:26:08.471Z', + destination: '070ea513-2210-42df-bad4-ede76ba22a3e', + isFavorite: true, + offers: [ + '61a27c8e-492d-4194-8d09-c85e8fbc0ed2', + '428136ce-b310-4e4e-bb3e-e09946178b81' + ], + type: 'taxi' + }, + { + id: 'fdae4f8a-f63b-418c-8a92-2d4001eecad4', + basePrice: 5102, + dateFrom: '2025-01-17T17:21:08.471Z', + dateTo: '2025-01-19T05:47:08.471Z', + destination: 'ec9110f7-4767-4179-b856-5d8bb23324ec', + isFavorite: true, + offers: [ + '56b72afa-d7d0-4dab-9d80-e64a2442783e', + '73522a17-f05d-49b9-a141-5ebccae6fb24', + 'd658acef-46f7-4c88-b7ed-df131ffabe0c', + '4a956f99-a055-4143-969f-b29f2be5da50' + ], + type: 'check-in' + }, + { + id: '59a69467-c2cc-45d2-bde2-10cb698e7260', + basePrice: 2276, + dateFrom: '2025-01-21T04:56:08.471Z', + dateTo: '2025-01-22T00:39:08.471Z', + destination: 'c7fcf894-e8ef-4347-94cf-8c76aa6c6833', + isFavorite: false, + offers: [ + '7511ff9e-227d-4732-9e27-d4cd1b78c220', + '52cfccc0-c186-4dea-9fe4-b04528650af9' + ], + type: 'flight' + }, + { + id: 'f7737a0d-3124-49eb-8487-d16d1c690d42', + basePrice: 1237, + dateFrom: '2025-01-22T22:40:08.471Z', + dateTo: '2025-01-24T16:43:08.471Z', + destination: '64a8701c-632e-48ae-9052-353c1a3465df', + isFavorite: false, + offers: [ + '6e21cc5e-e5ef-453d-a401-86846f294d02' + ], + type: 'ship' + }, + { + id: '73edeee1-d1fe-4371-a2f3-1012eaef876f', + basePrice: 2489, + dateFrom: '2025-01-26T06:26:08.471Z', + dateTo: '2025-01-27T18:41:08.471Z', + destination: 'fdd837e1-fd2d-463c-9a2d-555cf8efa89e', + isFavorite: true, + offers: [ + '151f488d-e03a-4347-8837-2e7171061983', + '7511ff9e-227d-4732-9e27-d4cd1b78c220', + '52cfccc0-c186-4dea-9fe4-b04528650af9' + ], + type: 'flight' + }, + { + id: '3a88f1e1-5926-43aa-aa65-be250ff0947f', + basePrice: 5807, + dateFrom: '2025-01-28T23:33:08.471Z', + dateTo: '2025-01-30T12:51:08.471Z', + destination: 'e4bc89ed-4df2-40a4-b8d1-21b953502799', + isFavorite: true, + offers: [ + '4dc8e801-b123-489e-816a-1b900e12d270', + '151f488d-e03a-4347-8837-2e7171061983', + '7511ff9e-227d-4732-9e27-d4cd1b78c220', + '52cfccc0-c186-4dea-9fe4-b04528650af9' + ], + type: 'flight' + }, + { + id: '413b04c2-c606-48f9-b0b7-d2a062bbbf6a', + basePrice: 7758, + dateFrom: '2025-01-31T09:26:08.471Z', + dateTo: '2025-02-02T04:15:08.471Z', + destination: '64a8701c-632e-48ae-9052-353c1a3465df', + isFavorite: true, + offers: [ + '4a956f99-a055-4143-969f-b29f2be5da50' + ], + type: 'check-in' + } +]; diff --git a/mocks/route.js b/mocks/route.js deleted file mode 100644 index e69de29..0000000 diff --git a/package-lock.json b/package-lock.json index 3788136..d72dbd9 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.9" }, "devDependencies": { "@babel/core": "7.21.4", @@ -5949,10 +5950,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", - "dev": true, + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.9.tgz", + "integrity": "sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q==", "funding": [ { "type": "github", @@ -5961,10 +5961,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": { @@ -6604,6 +6604,25 @@ "dev": true, "license": "MIT" }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "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 d42de59..75b156d 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "dependencies": { "dayjs": "1.11.7", "flatpickr": "4.6.13", - "he": "1.2.0" + "he": "1.2.0", + "nanoid": "5.0.9" } } diff --git a/public/index.html b/public/index.html index 44c26b4..ed0c5f1 100644 --- a/public/index.html +++ b/public/index.html @@ -18,11 +18,11 @@

Filter events

- +
- + @@ -31,9 +31,9 @@

Filter events

Trip events

- + - +
diff --git a/src/const.js b/src/const.js index 37bf9f9..bc3d417 100644 --- a/src/const.js +++ b/src/const.js @@ -3,21 +3,30 @@ export const EventStates = { EVENT_DEFAULT: false, }; -export const RoutePointType = { - TAXI: 'Taxi', - BUS: 'Bus', - TRAIN: 'Train', - SHIP: 'Ship', - DRIVE: 'Drive', - FLIGHT: 'Flight', - CHECK_IN: 'Check-in', - SIGHTSEEING: 'Sightseeing', - RESTAURANT: 'Restaurant', +export const FormMode = { + CREATE: 'CREATE', + EDIT: 'EDIT', }; -export const RouteFilterType = { +export const DateFormat = { + EVENT_DEFAULT: 'YYYY-MM-DD', + EVENT_HUMAN: 'MMM DD', + EVENT_START: 'YYYY-MM-DDTHH:mm', + EVENT_TIME: 'HH:mm', + FORM_START: 'YY/MM/DD HH:mm' +}; + +export const FilterType = { EVERYTHING: 'Everything', FUTURE: 'Future', PRESENT: 'Present', PAST: 'Past', }; + +export const TripDefault = { + PRICE: 0, + TYPE: 'flight', + DESTINATION: null, + IS_FAVORITE: false, + OFFERS: [], +}; diff --git a/src/main.js b/src/main.js index 2d03149..997689b 100644 --- a/src/main.js +++ b/src/main.js @@ -1,18 +1,16 @@ -import RouteFiltersPresenter from './presenter/route-filters-presenter'; -import RouteDeskPresenter from './presenter/route-desk-presenter'; -import TripSummaryPresenter from './presenter/trip-summary-presenter'; +import TripModel from './model/trip-model'; +import BoardPresenter from './presenter/board-presenter'; +import TripPresenter from './presenter/trip-presenter'; -const tripSummaryContainer = document.querySelector('.trip-main'); -const filtersContainer = tripSummaryContainer.querySelector('.trip-controls__filters'); +const tripInfoContainer = document.querySelector('.trip-main'); +const filtersContainer = tripInfoContainer.querySelector('.trip-controls__filters'); const contentContainer = document.querySelector('.trip-events'); +const tripModel = new TripModel(); +tripModel.init(); -const tripSummary = new TripSummaryPresenter(tripSummaryContainer); -tripSummary.init(); +const trip = new TripPresenter(tripInfoContainer, filtersContainer, tripModel); +trip.init(); -const routeFilters = new RouteFiltersPresenter(filtersContainer); -routeFilters.init(); - -const routeDesk = new RouteDeskPresenter(contentContainer); +const routeDesk = new BoardPresenter(contentContainer, tripModel); routeDesk.init(); - diff --git a/src/model/data.type.ts b/src/model/data.type.ts new file mode 100644 index 0000000..4df0768 --- /dev/null +++ b/src/model/data.type.ts @@ -0,0 +1,52 @@ +enum EventType { + Taxi, Bus, Train, Ship, Drive, Flight, CheckIn, Sightseeing, Restaurant +}; + +type Id = string; + +type Picture = { + src: string; + description: string; +}; + +type Destination = { + id: string; + description: string; + name: string; + pictures: Picture[]; +}; + +type Offer = { + id: string; + title: string; + price: number +} + +type OfferList = [ + { + type: EventType; + offers: Offer[]; + } +]; + +type RawEvent = { + id: string; + basePrice: number; + dateFrom: string; + dateTo: string; + destination: Id; + isFavorite: boolean; + offers: Id[]; + type: EventType; +}; + +type TripEvent = { + id: string; + basePrice: number; + dateFrom: string; + dateTo: string; + destination: Destination; + isFavorite: boolean; + offers: Offer[]; + type: EventType; +}; diff --git a/src/model/route-model.js b/src/model/route-model.js deleted file mode 100644 index 9a309c4..0000000 --- a/src/model/route-model.js +++ /dev/null @@ -1,7 +0,0 @@ -export default class RouteModel { - #routes = []; - - get routes() { - return this.#routes; - } -} diff --git a/src/model/trip-model.js b/src/model/trip-model.js new file mode 100644 index 0000000..c4c562d --- /dev/null +++ b/src/model/trip-model.js @@ -0,0 +1,48 @@ +import { generateTripData } from '../../mocks/generate-data'; +import { TripDefault } from '../const'; + +export default class TripModel { + #events = []; + #destinations = []; + #offers = []; + + get events() { + return this.#events.map((event) => ({ + ...event, + destination: this.#destinations.find(({ id }) => id === event.destination), + offers: this.#prepareOffers(event.type, event.offers), + })); + } + + get destinations() { + return this.#destinations; + } + + get offerTypes() { + return this.#offers.map(({ type }) => type); + } + + getDefaultEvent() { + const date = new Date().toISOString(); + const type = this.#offers.some((offer) => offer.type === TripDefault.TYPE) ? TripDefault.TYPE : this.#offers[0]; + + return { + basePrice: TripDefault.PRICE, + dateFrom: date, + dateTo: date, + destination: TripDefault.DESTINATION, + isFavorite: TripDefault.IS_FAVORITE, + offers: this.#prepareOffers(type, TripDefault.OFFERS), + type, + }; + } + + #prepareOffers = (type, offerIds) => { + const offers = this.#offers.find((offer) => offer.type === type)?.offers || []; + return offers.map((offer) => ({ ...offer, isChecked: offerIds.some((offerId) => offerId === offer.id) })); + }; + + init() { + [this.#events, this.#offers, this.#destinations] = generateTripData(); + } +} diff --git a/src/presenter/board-presenter.js b/src/presenter/board-presenter.js new file mode 100644 index 0000000..abba779 --- /dev/null +++ b/src/presenter/board-presenter.js @@ -0,0 +1,37 @@ +import { render } from '../render'; +import FormView from '../view/form-view'; +import EventListView from '../view/event-list-view'; +import EventView from '../view/event-view'; +import SortView from '../view/sort-view'; +import { FormMode } from '../const'; + +export default class BoardPresenter { + #board = null; + #eventList = new EventListView(); + #eventSort = new SortView(); + + #events = []; + #destinations = []; + #offerTypes = []; + + #tripModel = null; + + constructor(boardContainer, tripModel) { + this.#board = boardContainer; + this.#tripModel = tripModel; + } + + init() { + this.#events = [...this.#tripModel.events]; + this.#destinations = [...this.#tripModel.destinations]; + this.#offerTypes = [...this.#tripModel.offerTypes]; + + render(this.#board, this.#eventSort); + render(this.#board, this.#eventList); + render(this.#eventList, new FormView(this.#tripModel.getDefaultEvent(), this.#destinations, this.#offerTypes, FormMode.CREATE)); + render(this.#eventList, new FormView(this.#events[0], this.#destinations, this.#offerTypes, FormMode.EDIT)); + for (let i = 1; i < this.#events.length; i++) { + render(this.#eventList, new EventView(this.#events[i])); + } + } +} diff --git a/src/presenter/route-desk-presenter.js b/src/presenter/route-desk-presenter.js deleted file mode 100644 index 221b041..0000000 --- a/src/presenter/route-desk-presenter.js +++ /dev/null @@ -1,24 +0,0 @@ -import { render } from '../render'; -import RouteCreateView from '../view/route-create-view'; -import RouteListView from '../view/route-list-view'; -import RoutePointView from '../view/route-point-view'; -import SortView from '../view/sort-view'; - -export default class RouteDeskPresenter { - #routeDesk = null; - #routeList = new RouteListView(); - #routeSort = new SortView(); - - constructor(deskContainer) { - this.#routeDesk = deskContainer; - } - - init() { - render(this.#routeDesk, this.#routeSort); - render(this.#routeDesk, this.#routeList); - render(this.#routeList, new RouteCreateView()); - render(this.#routeList, new RoutePointView()); - render(this.#routeList, new RoutePointView()); - render(this.#routeList, new RoutePointView()); - } -} diff --git a/src/presenter/route-filters-presenter.js b/src/presenter/route-filters-presenter.js deleted file mode 100644 index 44ef01c..0000000 --- a/src/presenter/route-filters-presenter.js +++ /dev/null @@ -1,16 +0,0 @@ -import { render } from '../render'; -import FiltersView from '../view/filters-view'; - -export default class RouteFiltersPresenter { - #filtersContainer = null; - #filtersComponent = null; - - constructor(filtersContainer) { - this.#filtersContainer = filtersContainer; - } - - init() { - this.#filtersComponent = new FiltersView(); - render(this.#filtersContainer, this.#filtersComponent); - } -} diff --git a/src/presenter/trip-presenter.js b/src/presenter/trip-presenter.js new file mode 100644 index 0000000..0180b77 --- /dev/null +++ b/src/presenter/trip-presenter.js @@ -0,0 +1,26 @@ +import { render } from '../render'; +import FilterView from '../view/filter-view'; +import NewEventButtonView from '../view/new-event-button-view'; +import TripInfoView from '../view/trip-info-view'; + +export default class TripPresenter { + #tripInfoContainer = null; + #tripModel = null; + #filterContainer = null; + #filter = new FilterView(); + #tripInfo = new TripInfoView(); + #newEventButton = new NewEventButtonView(); + + constructor(tripInfoContainer, filtersContainer, tripModel) { + this.#tripInfoContainer = tripInfoContainer; + this.#filterContainer = filtersContainer; + this.#tripModel = tripModel; + } + + init() { + render(this.#tripInfoContainer, this.#tripInfo); + render(this.#filterContainer, this.#filter); + render(this.#tripInfoContainer, this.#newEventButton); + + } +} diff --git a/src/presenter/trip-summary-presenter.js b/src/presenter/trip-summary-presenter.js deleted file mode 100644 index 954d2be..0000000 --- a/src/presenter/trip-summary-presenter.js +++ /dev/null @@ -1,11 +0,0 @@ -export default class TripSummaryPresenter { - #tripSummaryContainer = null; - - constructor(tripSummaryContainer) { - this.#tripSummaryContainer = tripSummaryContainer; - } - - init() { - - } -} diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..5c5e530 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,23 @@ +import dayjs from 'dayjs'; +import duration from 'dayjs/plugin/duration'; +dayjs.extend(duration); + +export const mix = (data = [], length = 0) => { + const mixedData = []; + while (data.length && length--) { + const item = Math.round(Math.random() * (data.length - 1)); + mixedData.push(data[item]); + data.splice(item, 1); + } + + return mixedData; +}; + +export const getDuration = (timeStart, timeEnd) => { + const [start, end] = [dayjs(timeStart), dayjs(timeEnd)]; + const { days, hours, minutes } = dayjs.duration(end.diff(start)).$d; + + return [start, end, `${days ? `${days}D`.padStart(3, '0') : ''} ${hours ? `${hours}H`.padStart(3, '0') : ''} ${minutes ? `${minutes}M`.padStart(3, '0') : ''}`]; +}; + +export const toUpperCaseFirstLetter = (string) => string?.replace(/^./i, (char) => char.toUpperCase()); diff --git a/src/view/event-list-view.js b/src/view/event-list-view.js new file mode 100644 index 0000000..336c8e8 --- /dev/null +++ b/src/view/event-list-view.js @@ -0,0 +1,12 @@ +import AbstractView from './abstract-view'; + +const createEventListTemplate = () => (` + ` +); + +export default class EventListView extends AbstractView { + get template() { + return createEventListTemplate(); + } +} diff --git a/src/view/event-view.js b/src/view/event-view.js new file mode 100644 index 0000000..7739e21 --- /dev/null +++ b/src/view/event-view.js @@ -0,0 +1,87 @@ +import dayjs from 'dayjs'; +import AbstractView from './abstract-view'; +import { DateFormat } from '../const'; +import { getDuration } from '../utils'; + +const createOfferListTemplate = (offers) => ( + `${offers.map(({ title, price }) => (` +
  • + ${title} + +€  + ${price} +
  • `)).join('')}` +); + +const createEventTemplate = (event) => { + const { + basePrice, + destination: { name }, + dateFrom, + dateTo, + isFavorite, + offers, + type, + } = event || {}; + + const date = dayjs(dateFrom).format(DateFormat.EVENT_DEFAULT); + const dateHuman = dayjs(dateFrom).format(DateFormat.EVENT_HUMAN); + + const [start, end, duration] = getDuration(dateFrom, dateTo); + const dateStart = start.format(DateFormat.EVENT_START); + const dateEnd = end.format(DateFormat.EVENT_START); + const timeStart = start.format(DateFormat.EVENT_TIME); + const timeEnd = end.format(DateFormat.EVENT_TIME); + + const eventTitle = `${type} ${name}`; + const eventClass = isFavorite ? 'event__favorite-btn--active' : ''; + + return (` +
  • +
    + +
    + Event type icon +
    +

    ${eventTitle}

    +
    +

    + + — + +

    +

    ${duration}

    +
    +

    + € ${basePrice} +

    + ${offers.length ? ` +

    Offers:

    + ` : ''} + + +
    +
  • ` + ); +}; + +export default class EventView extends AbstractView { + #event = null; + + constructor(event) { + super(); + this.#event = event; + } + + get template() { + return createEventTemplate(this.#event); + } +} diff --git a/src/view/filters-view.js b/src/view/filter-view.js similarity index 91% rename from src/view/filters-view.js rename to src/view/filter-view.js index 2bc8cc6..17c5c29 100644 --- a/src/view/filters-view.js +++ b/src/view/filter-view.js @@ -1,6 +1,6 @@ import AbstractView from './abstract-view'; -const getComponentTemplate = () => (` +const createFiltersTemplate = () => (`
    @@ -26,8 +26,8 @@ const getComponentTemplate = () => (` ` ); -export default class FiltersView extends AbstractView { +export default class FilterView extends AbstractView { get template() { - return getComponentTemplate(); + return createFiltersTemplate(); } } diff --git a/src/view/form-view.js b/src/view/form-view.js new file mode 100644 index 0000000..2bd7aec --- /dev/null +++ b/src/view/form-view.js @@ -0,0 +1,160 @@ +import AbstractView from './abstract-view'; +import { DateFormat, FormMode } from '../const'; +import { nanoid } from 'nanoid'; +import { toUpperCaseFirstLetter } from '../utils'; +import dayjs from 'dayjs'; + + +const createEventTypeItemTemplate = (eventType, offerTypes = []) => offerTypes.map((type) => { + const id = nanoid(); + const checkStatus = eventType === type ? 'checked' : ''; + return (` +
    + + +
    ` + ); +}).join(''); + +const createEventTypeListTemplate = (eventType, offerTypes) => (` +
    +
    + Event type + ${createEventTypeItemTemplate(eventType, offerTypes)} +
    +
    ` +); + +const createOfferListItemTemplate = (offers) => offers.map(({ id, title, price, isChecked }) => (` +
    + + +
    ` +)).join(''); + +const createOfferListTemplate = (offers) => (` +
    +

    Offers

    +
    + ${createOfferListItemTemplate(offers)} +
    +
    ` +); + +const createDestinationListTemplate = ({ type, destination }, destinations = []) => { + const [labelId, listId] = [nanoid(), nanoid()]; + return (` +
    + + + + ${destinations.map(({ name }) => ``).join('')} + +
    ` + ); +}; + +const createPicturesTemplate = (pictures = []) => pictures.map(({ src, description }) => `${description}`).join(''); + +const createDestinationDescriptionTemplate = ({ description, pictures }) => (` +
    +

    Destination

    + ${description ? `

    ${description}

    ` : ''} +
    +
    + ${createPicturesTemplate(pictures)} +
    +
    +
    ` +); + + +const createFormTemplate = (event, destinations, offerTypes, mode) => { + const { + basePrice, + dateFrom, + dateTo, + destination, + offers, + type, + } = event || {}; + + const id = nanoid(); + const exitButtonText = mode === FormMode.EDIT ? 'Delete' : 'Cancel'; + const eventTypeList = createEventTypeListTemplate(type, offerTypes); + const destinationList = createDestinationListTemplate({ type, destination }, destinations); + const offerDetailsList = offers.length ? createOfferListTemplate(offers) : ''; + const destinationDescription = destination && (destination.pictures.length || destination.description) ? createDestinationDescriptionTemplate(destination) : ''; + + const dateStart = dayjs(dateFrom).format(DateFormat.FORM_START); + const dateEnd = dayjs(dateTo).format(DateFormat.FORM_START); + const price = basePrice > 0 ? basePrice : ''; + + return (` +
  • +
    +
    +
    + + + ${eventTypeList} +
    + + ${destinationList} + +
    + + + — + + +
    + +
    + + +
    + + + +
    + +
    + ${offerDetailsList} + ${destinationDescription} +
    +
    +
  • ` + ); +}; + +export default class FormView extends AbstractView { + + #eventData = []; + #destinations = []; + #offerTypes = []; + #mode = FormMode.EDIT; + + constructor(event, destinations, offerTypes, mode = FormMode.EDIT) { + super(); + this.#eventData = event; + this.#destinations = destinations; + this.#offerTypes = offerTypes; + this.#mode = mode in FormMode ? mode : FormMode.EDIT; + } + + get template() { + return createFormTemplate(this.#eventData, this.#destinations, this.#offerTypes, this.#mode); + } +} + diff --git a/src/view/new-event-button-view.js b/src/view/new-event-button-view.js new file mode 100644 index 0000000..8661e10 --- /dev/null +++ b/src/view/new-event-button-view.js @@ -0,0 +1,28 @@ +import AbstractView from './abstract-view'; + +const createButtonTemplate = (isDisabled) => (` + + `); + +export default class NewEventButtonView extends AbstractView { + #isDisabled; + + constructor(isDisabled = false) { + super(); + this.#isDisabled = isDisabled; + } + + get template() { + return createButtonTemplate(this.#isDisabled); + } + + disable() { + this.element.disabled = true; + } + + enable() { + this.element.disabled = false; + } +} diff --git a/src/view/new-route-button-view.js b/src/view/new-route-button-view.js deleted file mode 100644 index 839fe54..0000000 --- a/src/view/new-route-button-view.js +++ /dev/null @@ -1,11 +0,0 @@ -import AbstractView from './abstract-view'; - -const getComponentTemplate = () => (` - - `); - -export default class NewRouteButtonView extends AbstractView { - get template() { - return getComponentTemplate(); - } -} diff --git a/src/view/route-create-view.js b/src/view/route-create-view.js deleted file mode 100644 index bd38527..0000000 --- a/src/view/route-create-view.js +++ /dev/null @@ -1,187 +0,0 @@ -import AbstractView from './abstract-view'; - -const getComponentTemplate = () => (` -
  • -
    -
    -
    - - - -
    -
    - Event type - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    -
    -
    -
    - -
    - - - - - - - -
    - -
    - - - — - - -
    - -
    - - -
    - - - - -
    -
    -
    -

    Offers

    - -
    -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    -
    -
    - -
    -

    Destination

    -

    Chamonix-Mont-Blanc (usually shortened to Chamonix) is a resort area - near the junction of France, Switzerland and Italy. At the base of Mont Blanc, the highest summit in the Alps, - it's renowned for its skiing.

    -
    -
    -
    -
  • ` -); - -export default class RouteCreateView extends AbstractView { - get template() { - return getComponentTemplate(); - } -} - diff --git a/src/view/route-edit-view.js b/src/view/route-edit-view.js deleted file mode 100644 index ebf20cb..0000000 --- a/src/view/route-edit-view.js +++ /dev/null @@ -1,194 +0,0 @@ -import AbstractView from './abstract-view'; - -const getComponentTemplate = () => (` -
  • -
    -
    -
    - - - -
    -
    - Event type - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    -
    -
    -
    - -
    - - - - - - - -
    - -
    - - - — - - -
    - -
    - - -
    - - - -
    -
    -
    -

    Offers

    - -
    -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    -
    -
    - -
    -

    Destination

    -

    Geneva is a city in Switzerland that lies at the southern tip of - expansive Lac Léman (Lake Geneva). Surrounded by the Alps and Jura mountains, the city has views of dramatic - Mont Blanc.

    - -
    -
    - Event photo - Event photo - Event photo - Event photo - Event photo -
    -
    -
    -
    -
    -
  • ` -); - -export default class RouteEditView extends AbstractView { - get template() { - return getComponentTemplate(); - } -} - diff --git a/src/view/route-list-view.js b/src/view/route-list-view.js deleted file mode 100644 index 58c6029..0000000 --- a/src/view/route-list-view.js +++ /dev/null @@ -1,12 +0,0 @@ -import AbstractView from './abstract-view'; - -const getComponentTemplate = () => (` - ` -); - -export default class RouteListView extends AbstractView { - get template() { - return getComponentTemplate(); - } -} diff --git a/src/view/route-point-view.js b/src/view/route-point-view.js deleted file mode 100644 index ee493e4..0000000 --- a/src/view/route-point-view.js +++ /dev/null @@ -1,47 +0,0 @@ -import AbstractView from './abstract-view'; - -const getComponentTemplate = () => (` -
  • -
    - -
    - Event type icon -
    -

    Taxi Amsterdam

    -
    -

    - - — - -

    -

    30M

    -
    -

    - € 20 -

    -

    Offers:

    -
      -
    • - Order Uber - +€  - 20 -
    • -
    - - -
    -
  • ` -); - -export default class RoutePointView extends AbstractView { - get template() { - return getComponentTemplate(); - } -} diff --git a/src/view/sort-view.js b/src/view/sort-view.js index 310cd57..1c83dd3 100644 --- a/src/view/sort-view.js +++ b/src/view/sort-view.js @@ -1,6 +1,6 @@ import AbstractView from './abstract-view'; -const getComponentTemplate = () => (` +const createSortTemplate = () => (`
    @@ -31,7 +31,7 @@ const getComponentTemplate = () => (` export default class SortView extends AbstractView { get template() { - return getComponentTemplate(); + return createSortTemplate(); } } diff --git a/src/view/trip-info-view.js b/src/view/trip-info-view.js new file mode 100644 index 0000000..805978c --- /dev/null +++ b/src/view/trip-info-view.js @@ -0,0 +1,22 @@ +import AbstractView from './abstract-view'; + +const createTripInfoTemplate = () => (` +
    +
    +

    Amsterdam — Chamonix — Geneva

    + +

    18 — 20 Mar

    +
    + +

    + Total: € 1230 +

    +
    ` +); + +export default class TripInfoView extends AbstractView { + get template() { + return createTripInfoTemplate(); + } +} +