Add long yacht + reservations + reviews
This commit is contained in:
parent
4b7c100b3d
commit
11d59be696
|
|
@ -6,9 +6,11 @@ import { CatalogModule } from './catalog/catalog.module';
|
|||
import { AuthModule } from './auth/auth.module';
|
||||
import { UsersModule } from './users/users.module';
|
||||
import { FilesModule } from './files/files.module';
|
||||
import { ReviewsModule } from './reviews/reviews.module';
|
||||
import { ReservationsModule } from './reservations/reservations.module';
|
||||
|
||||
@Module({
|
||||
imports: [YachtsModule, CatalogModule, AuthModule, UsersModule, FilesModule],
|
||||
imports: [YachtsModule, CatalogModule, AuthModule, UsersModule, FilesModule, ReviewsModule, ReservationsModule],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
describe('AuthService', () => {
|
||||
let service: AuthService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [AuthService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<AuthService>(AuthService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { CatalogController } from './catalog.controller';
|
||||
|
||||
describe('CatalogController', () => {
|
||||
let controller: CatalogController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [CatalogController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<CatalogController>(CatalogController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
import { Controller, Get, Query } from '@nestjs/common';
|
||||
import { Controller, Get, Query, Param } from '@nestjs/common';
|
||||
import { CatalogService } from './catalog.service';
|
||||
import { CatalogResponseDto } from './dto/catalog-response.dto';
|
||||
import { CatalogItemDto } from './dto/catalog-item.dto';
|
||||
import {
|
||||
CatalogItemShortDto,
|
||||
CatalogItemLongDto,
|
||||
} from './dto/catalog-item.dto';
|
||||
import { CatalogParamsDto } from './dto/catalog-params.dto';
|
||||
import {
|
||||
ApiQuery,
|
||||
|
|
@ -56,9 +59,9 @@ export class CatalogController {
|
|||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'All catalog items',
|
||||
type: [CatalogItemDto],
|
||||
type: [CatalogItemShortDto],
|
||||
})
|
||||
async getAllCatalogItems(): Promise<CatalogItemDto[]> {
|
||||
async getAllCatalogItems(): Promise<CatalogItemShortDto[]> {
|
||||
return this.catalogService.getAllCatalogItems();
|
||||
}
|
||||
|
||||
|
|
@ -69,4 +72,17 @@ export class CatalogController {
|
|||
): Promise<CatalogResponseDto> {
|
||||
return this.catalogService.getCatalog(params);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Get catalog item by ID with full details' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Catalog item with reviews, reservations and owner',
|
||||
type: CatalogItemLongDto,
|
||||
})
|
||||
async getCatalogItemById(
|
||||
@Param('id') id: string,
|
||||
): Promise<CatalogItemLongDto | null> {
|
||||
return this.catalogService.getCatalogItemById(Number(id));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { CatalogService } from './catalog.service';
|
||||
|
||||
describe('CatalogService', () => {
|
||||
let service: CatalogService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [CatalogService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<CatalogService>(CatalogService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,15 +1,27 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { CatalogItemDto } from './dto/catalog-item.dto';
|
||||
import { UsersService } from '../users/users.service';
|
||||
import { ReservationsService } from '../reservations/reservations.service';
|
||||
import { ReviewsService } from '../reviews/reviews.service';
|
||||
import {
|
||||
CatalogItemShortDto,
|
||||
CatalogItemLongDto,
|
||||
} from './dto/catalog-item.dto';
|
||||
import { CatalogParamsDto } from './dto/catalog-params.dto';
|
||||
import { CatalogResponseDto } from './dto/catalog-response.dto';
|
||||
import { MainPageCatalogResponseDto } from './dto/main-page-catalog-response.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CatalogService {
|
||||
private catalogItems: CatalogItemDto[] = [
|
||||
constructor(
|
||||
private readonly usersService: UsersService,
|
||||
private readonly reservationsService: ReservationsService,
|
||||
private readonly reviewsService: ReviewsService,
|
||||
) {}
|
||||
|
||||
private catalogItems: CatalogItemLongDto[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Azimut 55',
|
||||
name: 'Азимут 55',
|
||||
length: 16.7,
|
||||
speed: 32,
|
||||
minCost: 85000,
|
||||
|
|
@ -27,10 +39,22 @@ export class CatalogService {
|
|||
],
|
||||
hasQuickRent: true,
|
||||
isFeatured: true,
|
||||
year: 2022,
|
||||
comfortCapacity: 8,
|
||||
maxCapacity: 12,
|
||||
width: 4.8,
|
||||
cabinsCount: 3,
|
||||
matherial: 'Стеклопластик',
|
||||
power: 1200,
|
||||
description:
|
||||
'Роскошная моторная яхта Азимут 55 - это воплощение итальянского стиля и российского качества. Идеально подходит для прогулок по Финскому заливу, корпоративных мероприятий и романтических свиданий. На борту: три комфортабельные каюты, просторный салон с панорамным остеклением, полностью оборудованная кухня и две ванные комнаты. Максимальная скорость 32 узла позволяет быстро добраться до самых живописных мест Карельского перешейка.',
|
||||
owner: null as any,
|
||||
reviews: [],
|
||||
reservations: [],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Sunseeker Manhattan 52',
|
||||
name: 'Сансикер Манхэттен 52',
|
||||
length: 15.8,
|
||||
speed: 34,
|
||||
minCost: 92000,
|
||||
|
|
@ -49,10 +73,22 @@ export class CatalogService {
|
|||
hasQuickRent: false,
|
||||
isFeatured: false,
|
||||
topText: '🔥 Лучшее предложение',
|
||||
year: 2023,
|
||||
comfortCapacity: 6,
|
||||
maxCapacity: 10,
|
||||
width: 4.5,
|
||||
cabinsCount: 3,
|
||||
matherial: 'Стеклопластик',
|
||||
power: 1400,
|
||||
description:
|
||||
'Британский шик и русская душа в одной яхте! Сансикер Манхэттен 52 - выбор настоящих ценителей морских путешествий. Просторный кокпит с мягкими диванами, бар на 8 персон, система мультимедиа премиум-класса. Идеальна для празднования дня рождения на воде или деловой встречи с партнерами. Отличная маневренность позволяет заходить в марины Санкт-Петербурга и Кронштадта.',
|
||||
owner: null as any,
|
||||
reviews: [],
|
||||
reservations: [],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Princess V55',
|
||||
name: 'Принцесс V55',
|
||||
length: 16.7,
|
||||
speed: 33,
|
||||
minCost: 78000,
|
||||
|
|
@ -71,10 +107,22 @@ export class CatalogService {
|
|||
hasQuickRent: true,
|
||||
isFeatured: false,
|
||||
topText: '🍷 Идеальна для заката с бокалом вина',
|
||||
year: 2021,
|
||||
comfortCapacity: 8,
|
||||
maxCapacity: 12,
|
||||
width: 4.7,
|
||||
cabinsCount: 4,
|
||||
matherial: 'Стеклопластик',
|
||||
power: 1100,
|
||||
description:
|
||||
'Принцесс V55 - королева российских вод! Эта яхта создана для тех, кто ценит комфорт и элегантность. Четыре уютные каюты с кондиционером, гальюн с душем, полностью оборудованная камбузная зона. Особенность - огромный платц с гидравлическим трапом для купания в Ладожском озере. Отличный выбор для семейного отдыха или рыбалки с друзьями.',
|
||||
owner: null as any,
|
||||
reviews: [],
|
||||
reservations: [],
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Ferretti 500',
|
||||
name: 'Ферретти 500',
|
||||
length: 15.2,
|
||||
speed: 31,
|
||||
minCost: 68000,
|
||||
|
|
@ -93,10 +141,22 @@ export class CatalogService {
|
|||
hasQuickRent: true,
|
||||
isFeatured: false,
|
||||
topText: '⏳ Часто бронируется - успей',
|
||||
year: 2020,
|
||||
comfortCapacity: 6,
|
||||
maxCapacity: 8,
|
||||
width: 4.3,
|
||||
cabinsCount: 3,
|
||||
matherial: 'Стеклопластик',
|
||||
power: 900,
|
||||
description:
|
||||
'Итальянская страсть в русской стихии! Ферретти 500 сочетает в себе средиземноморский шарм и надежность для суровых условий Балтики. Просторный салон с панорамными окнами, обеденная зона на 6 человек, современная навигационная система. Идеально подходит для фотосессий на фоне разводных мостов Петербурга или романтического ужина под звуки волн.',
|
||||
owner: null as any,
|
||||
reviews: [],
|
||||
reservations: [],
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Sea Ray 510 Sundancer',
|
||||
name: 'Си Рей 510 Сандансер',
|
||||
length: 15.5,
|
||||
speed: 35,
|
||||
minCost: 72000,
|
||||
|
|
@ -114,10 +174,22 @@ export class CatalogService {
|
|||
],
|
||||
hasQuickRent: false,
|
||||
isFeatured: false,
|
||||
year: 2023,
|
||||
comfortCapacity: 8,
|
||||
maxCapacity: 10,
|
||||
width: 4.6,
|
||||
cabinsCount: 3,
|
||||
matherial: 'Стеклопластик',
|
||||
power: 1300,
|
||||
description:
|
||||
'Американская мощь для русского моря! Си Рей 510 Сандансер - самая быстрая яхта в нашем флоте. Развивает скорость до 35 узлов, что позволяет за день обогнуть весь Финский залив. Три комфортабельные каюты, система стабилизации на стоянке, мощная аудиосистема с сабвуфером. Отличный выбор для любителей острых ощущений и скоростных прогулок.',
|
||||
owner: null as any,
|
||||
reviews: [],
|
||||
reservations: [],
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Bavaria SR41',
|
||||
name: 'Бавария SR41',
|
||||
length: 12.5,
|
||||
speed: 28,
|
||||
minCost: 45000,
|
||||
|
|
@ -135,10 +207,22 @@ export class CatalogService {
|
|||
],
|
||||
hasQuickRent: true,
|
||||
isFeatured: false,
|
||||
year: 2019,
|
||||
comfortCapacity: 6,
|
||||
maxCapacity: 8,
|
||||
width: 3.9,
|
||||
cabinsCount: 2,
|
||||
matherial: 'Стеклопластик',
|
||||
power: 320,
|
||||
description:
|
||||
'Немецкое качество для русского характера! Бавария SR41 - надежная и экономичная яхта для спокойных прогулок по Ладоге. Две уютные каюты, просторный кокпит с тентом от дождя, лебедка для подъема парусов. Идеальный выбор для начинающих яхтсменов или семейного отдыха с детьми. Расход топлива всего 15 литров в час!',
|
||||
owner: null as any,
|
||||
reviews: [],
|
||||
reservations: [],
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'Jeanneau Merry Fisher 895',
|
||||
name: 'Жанно Мери Фишер 895',
|
||||
length: 8.9,
|
||||
speed: 25,
|
||||
minCost: 32000,
|
||||
|
|
@ -156,10 +240,22 @@ export class CatalogService {
|
|||
],
|
||||
hasQuickRent: true,
|
||||
isFeatured: false,
|
||||
year: 2022,
|
||||
comfortCapacity: 4,
|
||||
maxCapacity: 6,
|
||||
width: 3.0,
|
||||
cabinsCount: 1,
|
||||
matherial: 'Стеклопластик',
|
||||
power: 250,
|
||||
description:
|
||||
'Французская элегантность для русского простора! Жанно Мери Фишер 895 - компактная, но вместительная яхта для рыбалки и пикников. Одна просторная каюта, открытый кокпит, столик для барбекю. Отлично подходит для выездов на природу, ночевки в бухтах или обучения детей управлению яхтой. Самый экономичный вариант в нашем флоте!',
|
||||
owner: null as any,
|
||||
reviews: [],
|
||||
reservations: [],
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'Beneteau Swift Trawler 41',
|
||||
name: 'Бенето Свифт Троулер 41',
|
||||
length: 12.5,
|
||||
speed: 22,
|
||||
minCost: 55000,
|
||||
|
|
@ -177,10 +273,22 @@ export class CatalogService {
|
|||
],
|
||||
hasQuickRent: false,
|
||||
isFeatured: false,
|
||||
year: 2021,
|
||||
comfortCapacity: 6,
|
||||
maxCapacity: 8,
|
||||
width: 4.2,
|
||||
cabinsCount: 2,
|
||||
matherial: 'Стеклопластик',
|
||||
power: 425,
|
||||
description:
|
||||
'Французский траулер для русского севера! Бенето Свифт Троулер 41 создан для длительных путешествий по Белому морю. Экономичный дизельный двигатель, большой запас топлива, система опреснения воды. Две комфортабельные каюты с подогревом пола. Идеальный выбор для экспедиций или многодневных круизов по северным островам.',
|
||||
owner: null as any,
|
||||
reviews: [],
|
||||
reservations: [],
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'Lagoon 450',
|
||||
name: 'Лагун 450',
|
||||
length: 13.5,
|
||||
speed: 20,
|
||||
minCost: 65000,
|
||||
|
|
@ -198,10 +306,22 @@ export class CatalogService {
|
|||
],
|
||||
hasQuickRent: true,
|
||||
isFeatured: false,
|
||||
year: 2020,
|
||||
comfortCapacity: 8,
|
||||
maxCapacity: 10,
|
||||
width: 7.8,
|
||||
cabinsCount: 4,
|
||||
matherial: 'Стеклопластик',
|
||||
power: 90,
|
||||
description:
|
||||
'Французский катамаран для русского размаха! Лагун 450 - невероятно устойчивая и просторная яхта. Четыре отдельные каюты с санузлами, огромный салон-трансформер, две кухни. Идеально подходит для больших компаний, свадебных церемоний на воде или длительных круизов всей семьей. Не кренится даже в шторм!',
|
||||
owner: null as any,
|
||||
reviews: [],
|
||||
reservations: [],
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: 'Fountaine Pajot Lucia 40',
|
||||
name: 'Фонтен Пажо Люсия 40',
|
||||
length: 11.7,
|
||||
speed: 18,
|
||||
minCost: 58000,
|
||||
|
|
@ -219,10 +339,22 @@ export class CatalogService {
|
|||
],
|
||||
hasQuickRent: true,
|
||||
isFeatured: false,
|
||||
year: 2023,
|
||||
comfortCapacity: 8,
|
||||
maxCapacity: 10,
|
||||
width: 7.1,
|
||||
cabinsCount: 4,
|
||||
matherial: 'Стеклопластик',
|
||||
power: 80,
|
||||
description:
|
||||
'Французский катамаран класса люкс! Фонтен Пажо Люсия 40 - это плавающий пятизвездочный отель. Четыре каюты-люкс с джакузи, салон с камином, профессиональная кухня с шеф-поваром. Система стабилизации на якоре, гидромассажный бассейн на палубе. Выбор настоящих ценителей роскоши и комфорта на воде.',
|
||||
owner: null as any,
|
||||
reviews: [],
|
||||
reservations: [],
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: 'Dufour 460',
|
||||
name: 'Дюфур 460',
|
||||
length: 14.1,
|
||||
speed: 26,
|
||||
minCost: 62000,
|
||||
|
|
@ -240,10 +372,22 @@ export class CatalogService {
|
|||
],
|
||||
hasQuickRent: false,
|
||||
isFeatured: false,
|
||||
year: 2022,
|
||||
comfortCapacity: 8,
|
||||
maxCapacity: 10,
|
||||
width: 4.5,
|
||||
cabinsCount: 3,
|
||||
matherial: 'Стеклопластик',
|
||||
power: 380,
|
||||
description:
|
||||
'Французская парусная яхта для русского ветра! Дюфур 460 - мечта любого яхтсмена. Три просторные каюты, кокпит с мягкими сиденьями, современное парусное вооружение. Идеально сбалансированная, легко управляется даже новичками. Отличный выбор для регат, обучения парусному спорту или романтических круизов под парусами.',
|
||||
owner: null as any,
|
||||
reviews: [],
|
||||
reservations: [],
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: 'Grand Banks 60',
|
||||
name: 'Гранд Бэнкс 60',
|
||||
length: 18.3,
|
||||
speed: 24,
|
||||
minCost: 125000,
|
||||
|
|
@ -261,9 +405,65 @@ export class CatalogService {
|
|||
],
|
||||
hasQuickRent: true,
|
||||
isFeatured: false,
|
||||
year: 2023,
|
||||
comfortCapacity: 6,
|
||||
maxCapacity: 8,
|
||||
width: 5.2,
|
||||
cabinsCount: 3,
|
||||
matherial: 'Стеклопластик',
|
||||
power: 1600,
|
||||
description:
|
||||
'Американская легенда для русского океана! Гранд Бэнкс 60 - экспедиционная яхта для самых смелых путешествий. Три каюты-люкс, салон с библиотекой, зимний сад, сауна. Автономность плавания - 30 дней! Способна пересечь Баренцево море и дойти до Шпицбергена. Выбор для настоящих морских волков и исследователей Арктики.',
|
||||
owner: null as any,
|
||||
reviews: [],
|
||||
reservations: [],
|
||||
},
|
||||
];
|
||||
|
||||
private toShortDto(item: CatalogItemLongDto): CatalogItemShortDto {
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
length,
|
||||
speed,
|
||||
minCost,
|
||||
mainImageUrl,
|
||||
galleryUrls,
|
||||
hasQuickRent,
|
||||
isFeatured,
|
||||
topText,
|
||||
} = item;
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
length,
|
||||
speed,
|
||||
minCost,
|
||||
mainImageUrl,
|
||||
galleryUrls,
|
||||
hasQuickRent,
|
||||
isFeatured,
|
||||
topText,
|
||||
};
|
||||
}
|
||||
|
||||
async getCatalogItemById(id: number): Promise<CatalogItemLongDto | null> {
|
||||
const item = this.catalogItems.find((item) => item.id === id);
|
||||
if (!item) return null;
|
||||
|
||||
const ownerId = [1, 2][(id - 1) % 2];
|
||||
const owner = await this.usersService.findById(ownerId);
|
||||
|
||||
if (owner) {
|
||||
item.owner = owner;
|
||||
}
|
||||
|
||||
item.reviews = this.reviewsService.getReviewsByYachtId(id);
|
||||
item.reservations = this.reservationsService.getReservationsByYachtId(id);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
async getMainPageCatalog(): Promise<MainPageCatalogResponseDto | null> {
|
||||
const clonedCatalog = [...this.catalogItems];
|
||||
const filteredCatalog = clonedCatalog.filter(
|
||||
|
|
@ -280,12 +480,12 @@ export class CatalogService {
|
|||
);
|
||||
|
||||
const mappedRestYachts = filteredCatalog.slice(0, 6).map((item) => ({
|
||||
...item,
|
||||
...this.toShortDto(item),
|
||||
isBestOffer: item.minCost === minCost,
|
||||
}));
|
||||
|
||||
return {
|
||||
featuredYacht: clonedCatalog[featuredYachtIndex],
|
||||
featuredYacht: this.toShortDto(clonedCatalog[featuredYachtIndex]),
|
||||
restYachts: mappedRestYachts,
|
||||
};
|
||||
}
|
||||
|
|
@ -294,11 +494,10 @@ export class CatalogService {
|
|||
}
|
||||
|
||||
async getCatalog(params?: CatalogParamsDto): Promise<CatalogResponseDto> {
|
||||
let filteredItems = [...this.catalogItems];
|
||||
let filteredItems = this.catalogItems.map((item) => this.toShortDto(item));
|
||||
|
||||
if (params?.search) {
|
||||
const searchTerm = params.search.toLowerCase();
|
||||
|
||||
filteredItems = filteredItems.filter((item) =>
|
||||
item.name.toLowerCase().includes(searchTerm),
|
||||
);
|
||||
|
|
@ -306,7 +505,6 @@ export class CatalogService {
|
|||
|
||||
if (params?.filter?.price) {
|
||||
const { minvalue, maxvalue } = params.filter.price;
|
||||
|
||||
filteredItems = filteredItems.filter((item) => {
|
||||
if (minvalue !== undefined && item.minCost < minvalue) return false;
|
||||
if (maxvalue !== undefined && item.minCost > maxvalue) return false;
|
||||
|
|
@ -316,7 +514,6 @@ export class CatalogService {
|
|||
|
||||
if (params?.sort?.field) {
|
||||
const { field, direction = 'asc' } = params.sort;
|
||||
|
||||
filteredItems.sort((a, b) => {
|
||||
let valueA = a[field];
|
||||
let valueB = b[field];
|
||||
|
|
@ -326,7 +523,6 @@ export class CatalogService {
|
|||
|
||||
if (valueA < valueB) return direction === 'asc' ? -1 : 1;
|
||||
if (valueA > valueB) return direction === 'asc' ? 1 : -1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
|
@ -340,7 +536,7 @@ export class CatalogService {
|
|||
};
|
||||
}
|
||||
|
||||
async getAllCatalogItems(): Promise<CatalogItemDto[]> {
|
||||
return this.catalogItems;
|
||||
async getAllCatalogItems(): Promise<CatalogItemShortDto[]> {
|
||||
return this.catalogItems.map((item) => this.toShortDto(item));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
export class CatalogItemDto {
|
||||
import { type Reservation } from 'src/reservations/reservations.service';
|
||||
import { type Review } from 'src/reviews/reviews.service';
|
||||
import { User } from 'src/users/user.entity';
|
||||
|
||||
export class CatalogItemShortDto {
|
||||
id?: number;
|
||||
name: string;
|
||||
length: number;
|
||||
|
|
@ -11,3 +15,17 @@ export class CatalogItemDto {
|
|||
topText?: string;
|
||||
isBestOffer?: boolean;
|
||||
}
|
||||
|
||||
export class CatalogItemLongDto extends CatalogItemShortDto {
|
||||
year: number;
|
||||
comfortCapacity: number;
|
||||
maxCapacity: number;
|
||||
width: number;
|
||||
cabinsCount: number;
|
||||
matherial: string;
|
||||
power: number;
|
||||
description: string;
|
||||
owner: User;
|
||||
reviews: Review[];
|
||||
reservations: Reservation[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { CatalogItemDto } from './catalog-item.dto';
|
||||
import { CatalogItemShortDto } from './catalog-item.dto';
|
||||
|
||||
export class CatalogResponseDto {
|
||||
items: CatalogItemDto[];
|
||||
items: CatalogItemShortDto[];
|
||||
total: number;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { CatalogItemDto } from './catalog-item.dto';
|
||||
import { CatalogItemShortDto } from './catalog-item.dto';
|
||||
|
||||
export class MainPageCatalogResponseDto {
|
||||
featuredYacht: CatalogItemDto;
|
||||
restYachts: CatalogItemDto[];
|
||||
featuredYacht: CatalogItemShortDto;
|
||||
restYachts: CatalogItemShortDto[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
export class ReservationItemDto {
|
||||
yachtId: number;
|
||||
reservatorId: number;
|
||||
startUtc: number;
|
||||
endUtc: number;
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
|
||||
import { ReservationsService } from './reservations.service';
|
||||
import { ReservationItemDto } from './reservation-item.dto';
|
||||
|
||||
@Controller('reservations')
|
||||
export class ReservationsController {
|
||||
constructor(private readonly reservationsService: ReservationsService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: ReservationItemDto) {
|
||||
return this.reservationsService.createReservation(dto);
|
||||
}
|
||||
|
||||
@Get('user/:userId')
|
||||
findByUserId(@Param('userId') userId: string) {
|
||||
return this.reservationsService.getReservationsByUserId(Number(userId));
|
||||
}
|
||||
|
||||
@Get('yacht/:yachtId')
|
||||
findByYachtId(@Param('yachtId') yachtId: string) {
|
||||
return this.reservationsService.getReservationsByYachtId(Number(yachtId));
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.reservationsService.getAllReservations();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { ReservationsService } from './reservations.service';
|
||||
import { ReservationsController } from './reservations.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [ReservationsController],
|
||||
providers: [ReservationsService],
|
||||
})
|
||||
export class ReservationsModule {}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { ReservationItemDto } from './reservation-item.dto';
|
||||
|
||||
export interface Reservation extends ReservationItemDto {
|
||||
id: number;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReservationsService {
|
||||
private reservations: Reservation[] = [
|
||||
{
|
||||
id: 1,
|
||||
yachtId: 1,
|
||||
reservatorId: 1,
|
||||
startUtc: 1733097600,
|
||||
endUtc: 1733133600,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
yachtId: 3,
|
||||
reservatorId: 2,
|
||||
startUtc: 1733212800,
|
||||
endUtc: 1733436000,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
yachtId: 5,
|
||||
reservatorId: 1,
|
||||
startUtc: 1733860800,
|
||||
endUtc: 1734048000,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
yachtId: 7,
|
||||
reservatorId: 2,
|
||||
startUtc: 1734216000,
|
||||
endUtc: 1734472800,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
yachtId: 9,
|
||||
reservatorId: 1,
|
||||
startUtc: 1734705600,
|
||||
endUtc: 1734883200,
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
yachtId: 11,
|
||||
reservatorId: 2,
|
||||
startUtc: 1735113600,
|
||||
endUtc: 1735336800,
|
||||
},
|
||||
];
|
||||
|
||||
private idCounter = 7;
|
||||
|
||||
createReservation(dto: ReservationItemDto): Reservation {
|
||||
const reservation = {
|
||||
id: this.idCounter++,
|
||||
...dto,
|
||||
};
|
||||
|
||||
this.reservations.push(reservation);
|
||||
return reservation;
|
||||
}
|
||||
|
||||
getReservationsByUserId(userId: number): Reservation[] {
|
||||
return this.reservations.filter((r) => r.reservatorId === userId);
|
||||
}
|
||||
|
||||
getReservationsByYachtId(yachtId: number): Reservation[] {
|
||||
return this.reservations.filter((r) => r.yachtId === yachtId);
|
||||
}
|
||||
|
||||
getAllReservations(): Reservation[] {
|
||||
return this.reservations;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
export class ReviewItemDto {
|
||||
reviewerId: number;
|
||||
yachtId: number;
|
||||
starsCount: number;
|
||||
description: string;
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
|
||||
import { ReviewsService } from './reviews.service';
|
||||
import { ReviewItemDto } from './review-item.dto';
|
||||
|
||||
@Controller('reviews')
|
||||
export class ReviewsController {
|
||||
constructor(private readonly reviewsService: ReviewsService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: ReviewItemDto) {
|
||||
return this.reviewsService.createReview(dto);
|
||||
}
|
||||
|
||||
@Get('user/:userId')
|
||||
findByUserId(@Param('userId') userId: string) {
|
||||
return this.reviewsService.getReviewsByUserId(Number(userId));
|
||||
}
|
||||
|
||||
@Get('yacht/:yachtId')
|
||||
findByYachtId(@Param('yachtId') yachtId: string) {
|
||||
return this.reviewsService.getReviewsByYachtId(Number(yachtId));
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.reviewsService.getAllReviews();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { ReviewsService } from './reviews.service';
|
||||
import { ReviewsController } from './reviews.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [ReviewsController],
|
||||
providers: [ReviewsService],
|
||||
})
|
||||
export class ReviewsModule {}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { ReviewItemDto } from './review-item.dto';
|
||||
|
||||
export interface Review extends ReviewItemDto {
|
||||
id: number;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReviewsService {
|
||||
private reviews: Review[] = [
|
||||
{
|
||||
id: 1,
|
||||
reviewerId: 1,
|
||||
yachtId: 1,
|
||||
starsCount: 5,
|
||||
description: 'Excellent yacht!',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
reviewerId: 2,
|
||||
yachtId: 1,
|
||||
starsCount: 4,
|
||||
description: 'Very good experience',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
reviewerId: 1,
|
||||
yachtId: 3,
|
||||
starsCount: 3,
|
||||
description: 'Average condition',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
reviewerId: 2,
|
||||
yachtId: 5,
|
||||
starsCount: 5,
|
||||
description: 'Perfect for sailing',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
reviewerId: 1,
|
||||
yachtId: 7,
|
||||
starsCount: 4,
|
||||
description: 'Comfortable and fast',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
reviewerId: 2,
|
||||
yachtId: 9,
|
||||
starsCount: 2,
|
||||
description: 'Needs maintenance',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
reviewerId: 1,
|
||||
yachtId: 11,
|
||||
starsCount: 5,
|
||||
description: 'Luxury experience',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
reviewerId: 2,
|
||||
yachtId: 12,
|
||||
starsCount: 4,
|
||||
description: 'Great value for money',
|
||||
},
|
||||
];
|
||||
|
||||
private idCounter = 9;
|
||||
|
||||
createReview(dto: ReviewItemDto): Review {
|
||||
const review = {
|
||||
id: this.idCounter++,
|
||||
...dto,
|
||||
};
|
||||
|
||||
this.reviews.push(review);
|
||||
return review;
|
||||
}
|
||||
|
||||
getReviewsByUserId(userId: number): Review[] {
|
||||
return this.reviews.filter((r) => r.reviewerId === userId);
|
||||
}
|
||||
|
||||
getReviewsByYachtId(yachtId: number): Review[] {
|
||||
return this.reviews.filter((r) => r.yachtId === yachtId);
|
||||
}
|
||||
|
||||
getAllReviews(): Review[] {
|
||||
return this.reviews;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,4 +8,7 @@ export type User = {
|
|||
email: string;
|
||||
password: string;
|
||||
yachts?: Yacht[];
|
||||
companyName?: string;
|
||||
inn?: number;
|
||||
ogrn?: number;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
describe('UsersService', () => {
|
||||
let service: UsersService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UsersService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<UsersService>(UsersService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -9,19 +9,47 @@ export class UsersService {
|
|||
private readonly users: User[] = [
|
||||
{
|
||||
userId: 1,
|
||||
firstName: 'Ivan',
|
||||
lastName: 'Andreev',
|
||||
firstName: 'Иван',
|
||||
lastName: 'Андреев',
|
||||
phone: '+79009009090',
|
||||
email: 'email@email.com',
|
||||
email: 'ivan@yachting.ru',
|
||||
password: 'admin',
|
||||
companyName: 'Северный Флот',
|
||||
inn: 1234567890,
|
||||
ogrn: 1122334455667,
|
||||
},
|
||||
{
|
||||
userId: 2,
|
||||
firstName: 'Sergey',
|
||||
lastName: 'Bolshakov',
|
||||
phone: '+79009009090',
|
||||
email: 'email1@email.com',
|
||||
firstName: 'Сергей',
|
||||
lastName: 'Большаков',
|
||||
phone: '+79119119191',
|
||||
email: 'sergey@yachting.ru',
|
||||
password: 'admin',
|
||||
companyName: 'Балтийские Просторы',
|
||||
inn: 9876543210,
|
||||
ogrn: 9988776655443,
|
||||
},
|
||||
{
|
||||
userId: 3,
|
||||
firstName: 'Анна',
|
||||
lastName: 'Петрова',
|
||||
phone: '+79229229292',
|
||||
email: 'anna@yachting.ru',
|
||||
password: 'admin',
|
||||
companyName: 'Ладожские Ветры',
|
||||
inn: 5555555555,
|
||||
ogrn: 3333444455556,
|
||||
},
|
||||
{
|
||||
userId: 4,
|
||||
firstName: 'Дмитрий',
|
||||
lastName: 'Соколов',
|
||||
phone: '+79339339393',
|
||||
email: 'dmitry@yachting.ru',
|
||||
password: 'admin',
|
||||
companyName: 'Финский Залив',
|
||||
inn: 1111222233,
|
||||
ogrn: 7777888899990,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -32,8 +60,7 @@ export class UsersService {
|
|||
const user = this.users.find((user) => user.email === email);
|
||||
|
||||
if (user && includeYachts) {
|
||||
// Fetch yachts for this user
|
||||
user.yachts = await [];
|
||||
user.yachts = [];
|
||||
}
|
||||
|
||||
return user;
|
||||
|
|
@ -57,7 +84,6 @@ export class UsersService {
|
|||
return this.users;
|
||||
}
|
||||
|
||||
// Fetch all users with their yachts
|
||||
const usersWithYachts = await Promise.all(
|
||||
this.users.map(async (user) => {
|
||||
const yachts = [];
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { YachtsController } from './yacht.controller';
|
||||
|
||||
describe('YachtController', () => {
|
||||
let controller: YachtsController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [YachtsController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<YachtsController>(YachtsController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { YachtsService } from './yachts.service';
|
||||
|
||||
describe('YachtsService', () => {
|
||||
let service: YachtsService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [YachtsService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<YachtsService>(YachtsService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue