presenter refactoring

This commit is contained in:
2025-12-20 17:06:11 +01:00
parent 92be9d2e1b
commit e9d6f90bb2
109 changed files with 4159 additions and 1283 deletions

View File

@@ -23,6 +23,11 @@ describe('SponsorController', () => {
getPendingSponsorshipRequests: vi.fn(),
acceptSponsorshipRequest: vi.fn(),
rejectSponsorshipRequest: vi.fn(),
getSponsorBilling: vi.fn(),
getAvailableLeagues: vi.fn(),
getLeagueDetail: vi.fn(),
getSponsorSettings: vi.fn(),
updateSponsorSettings: vi.fn(),
},
},
],
@@ -35,7 +40,7 @@ describe('SponsorController', () => {
describe('getEntitySponsorshipPricing', () => {
it('should return sponsorship pricing', async () => {
const mockResult = { entityType: 'season', entityId: 'season-1', pricing: [] };
sponsorService.getEntitySponsorshipPricing.mockResolvedValue(mockResult);
sponsorService.getEntitySponsorshipPricing.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.getEntitySponsorshipPricing();
@@ -47,7 +52,7 @@ describe('SponsorController', () => {
describe('getSponsors', () => {
it('should return sponsors list', async () => {
const mockResult = { sponsors: [] };
sponsorService.getSponsors.mockResolvedValue(mockResult);
sponsorService.getSponsors.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.getSponsors();
@@ -59,10 +64,10 @@ describe('SponsorController', () => {
describe('createSponsor', () => {
it('should create sponsor', async () => {
const input = { name: 'Test Sponsor', contactEmail: 'test@example.com' };
const mockResult = { id: 'sponsor-1', name: 'Test Sponsor' };
sponsorService.createSponsor.mockResolvedValue(mockResult);
const mockResult = { sponsor: { id: 's1', name: 'Test Sponsor' } };
sponsorService.createSponsor.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.createSponsor(input);
const result = await controller.createSponsor(input as any);
expect(result).toEqual(mockResult);
expect(sponsorService.createSponsor).toHaveBeenCalledWith(input);
@@ -71,9 +76,9 @@ describe('SponsorController', () => {
describe('getSponsorDashboard', () => {
it('should return sponsor dashboard', async () => {
const sponsorId = 'sponsor-1';
const mockResult = { sponsorId, metrics: {}, sponsoredLeagues: [] };
sponsorService.getSponsorDashboard.mockResolvedValue(mockResult);
const sponsorId = 's1';
const mockResult = { sponsorId, metrics: {} as any, sponsoredLeagues: [], investment: {} as any };
sponsorService.getSponsorDashboard.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.getSponsorDashboard(sponsorId);
@@ -82,8 +87,8 @@ describe('SponsorController', () => {
});
it('should return null when sponsor not found', async () => {
const sponsorId = 'sponsor-1';
sponsorService.getSponsorDashboard.mockResolvedValue(null);
const sponsorId = 's1';
sponsorService.getSponsorDashboard.mockResolvedValue({ viewModel: null } as any);
const result = await controller.getSponsorDashboard(sponsorId);
@@ -93,9 +98,20 @@ describe('SponsorController', () => {
describe('getSponsorSponsorships', () => {
it('should return sponsor sponsorships', async () => {
const sponsorId = 'sponsor-1';
const mockResult = { sponsorId, sponsorships: [] };
sponsorService.getSponsorSponsorships.mockResolvedValue(mockResult);
const sponsorId = 's1';
const mockResult = {
sponsorId,
sponsorName: 'S1',
sponsorships: [],
summary: {
totalSponsorships: 0,
activeSponsorships: 0,
totalInvestment: 0,
totalPlatformFees: 0,
currency: 'USD',
},
};
sponsorService.getSponsorSponsorships.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.getSponsorSponsorships(sponsorId);
@@ -104,8 +120,8 @@ describe('SponsorController', () => {
});
it('should return null when sponsor not found', async () => {
const sponsorId = 'sponsor-1';
sponsorService.getSponsorSponsorships.mockResolvedValue(null);
const sponsorId = 's1';
sponsorService.getSponsorSponsorships.mockResolvedValue({ viewModel: null } as any);
const result = await controller.getSponsorSponsorships(sponsorId);
@@ -115,9 +131,9 @@ describe('SponsorController', () => {
describe('getSponsor', () => {
it('should return sponsor', async () => {
const sponsorId = 'sponsor-1';
const mockResult = { id: sponsorId, name: 'Test Sponsor' };
sponsorService.getSponsor.mockResolvedValue(mockResult);
const sponsorId = 's1';
const mockResult = { sponsor: { id: sponsorId, name: 'S1' } };
sponsorService.getSponsor.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.getSponsor(sponsorId);
@@ -126,8 +142,8 @@ describe('SponsorController', () => {
});
it('should return null when sponsor not found', async () => {
const sponsorId = 'sponsor-1';
sponsorService.getSponsor.mockResolvedValue(null);
const sponsorId = 's1';
sponsorService.getSponsor.mockResolvedValue({ viewModel: null } as any);
const result = await controller.getSponsor(sponsorId);
@@ -138,8 +154,13 @@ describe('SponsorController', () => {
describe('getPendingSponsorshipRequests', () => {
it('should return pending sponsorship requests', async () => {
const query = { entityType: 'season' as const, entityId: 'season-1' };
const mockResult = { entityType: 'season', entityId: 'season-1', requests: [], totalCount: 0 };
sponsorService.getPendingSponsorshipRequests.mockResolvedValue(mockResult);
const mockResult = {
entityType: 'season',
entityId: 'season-1',
requests: [],
totalCount: 0,
};
sponsorService.getPendingSponsorshipRequests.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.getPendingSponsorshipRequests(query);
@@ -150,30 +171,33 @@ describe('SponsorController', () => {
describe('acceptSponsorshipRequest', () => {
it('should accept sponsorship request', async () => {
const requestId = 'request-1';
const input = { respondedBy: 'user-1' };
const requestId = 'r1';
const input = { respondedBy: 'u1' };
const mockResult = {
requestId,
sponsorshipId: 'sponsorship-1',
sponsorshipId: 'sp1',
status: 'accepted' as const,
acceptedAt: new Date(),
platformFee: 10,
netAmount: 90,
};
sponsorService.acceptSponsorshipRequest.mockResolvedValue(mockResult);
sponsorService.acceptSponsorshipRequest.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.acceptSponsorshipRequest(requestId, input);
const result = await controller.acceptSponsorshipRequest(requestId, input as any);
expect(result).toEqual(mockResult);
expect(sponsorService.acceptSponsorshipRequest).toHaveBeenCalledWith(requestId, input.respondedBy);
expect(sponsorService.acceptSponsorshipRequest).toHaveBeenCalledWith(
requestId,
input.respondedBy,
);
});
it('should return null on error', async () => {
const requestId = 'request-1';
const input = { respondedBy: 'user-1' };
sponsorService.acceptSponsorshipRequest.mockResolvedValue(null);
const requestId = 'r1';
const input = { respondedBy: 'u1' };
sponsorService.acceptSponsorshipRequest.mockResolvedValue({ viewModel: null } as any);
const result = await controller.acceptSponsorshipRequest(requestId, input);
const result = await controller.acceptSponsorshipRequest(requestId, input as any);
expect(result).toBeNull();
});
@@ -181,30 +205,118 @@ describe('SponsorController', () => {
describe('rejectSponsorshipRequest', () => {
it('should reject sponsorship request', async () => {
const requestId = 'request-1';
const input = { respondedBy: 'user-1', reason: 'Not interested' };
const requestId = 'r1';
const input = { respondedBy: 'u1', reason: 'Not interested' };
const mockResult = {
requestId,
status: 'rejected' as const,
rejectedAt: new Date(),
reason: 'Not interested',
};
sponsorService.rejectSponsorshipRequest.mockResolvedValue(mockResult);
sponsorService.rejectSponsorshipRequest.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.rejectSponsorshipRequest(requestId, input);
const result = await controller.rejectSponsorshipRequest(requestId, input as any);
expect(result).toEqual(mockResult);
expect(sponsorService.rejectSponsorshipRequest).toHaveBeenCalledWith(requestId, input.respondedBy, input.reason);
expect(sponsorService.rejectSponsorshipRequest).toHaveBeenCalledWith(
requestId,
input.respondedBy,
input.reason,
);
});
it('should return null on error', async () => {
const requestId = 'request-1';
const input = { respondedBy: 'user-1' };
sponsorService.rejectSponsorshipRequest.mockResolvedValue(null);
const requestId = 'r1';
const input = { respondedBy: 'u1' };
sponsorService.rejectSponsorshipRequest.mockResolvedValue({ viewModel: null } as any);
const result = await controller.rejectSponsorshipRequest(requestId, input);
const result = await controller.rejectSponsorshipRequest(requestId, input as any);
expect(result).toBeNull();
});
});
});
describe('getSponsorBilling', () => {
it('should return sponsor billing', async () => {
const sponsorId = 's1';
const mockResult = {
paymentMethods: [],
invoices: [],
stats: {
totalSpent: 0,
pendingAmount: 0,
nextPaymentDate: '2025-01-01',
nextPaymentAmount: 0,
activeSponsorships: 0,
averageMonthlySpend: 0,
},
};
sponsorService.getSponsorBilling.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.getSponsorBilling(sponsorId);
expect(result).toEqual(mockResult);
expect(sponsorService.getSponsorBilling).toHaveBeenCalledWith(sponsorId);
});
});
describe('getAvailableLeagues', () => {
it('should return available leagues', async () => {
const mockResult: any[] = [];
sponsorService.getAvailableLeagues.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.getAvailableLeagues();
expect(result).toEqual(mockResult);
expect(sponsorService.getAvailableLeagues).toHaveBeenCalled();
});
});
describe('getLeagueDetail', () => {
it('should return league detail', async () => {
const leagueId = 'league-1';
const mockResult = {
league: { id: leagueId } as any,
drivers: [],
races: [],
};
sponsorService.getLeagueDetail.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.getLeagueDetail(leagueId);
expect(result).toEqual(mockResult);
expect(sponsorService.getLeagueDetail).toHaveBeenCalledWith(leagueId);
});
});
describe('getSponsorSettings', () => {
it('should return sponsor settings', async () => {
const sponsorId = 's1';
const mockResult = {
profile: {} as any,
notifications: {} as any,
privacy: {} as any,
};
sponsorService.getSponsorSettings.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.getSponsorSettings(sponsorId);
expect(result).toEqual(mockResult);
expect(sponsorService.getSponsorSettings).toHaveBeenCalledWith(sponsorId);
});
});
describe('updateSponsorSettings', () => {
it('should update sponsor settings', async () => {
const sponsorId = 's1';
const input = {};
const mockResult = { success: true };
sponsorService.updateSponsorSettings.mockResolvedValue({ viewModel: mockResult } as any);
const result = await controller.updateSponsorSettings(sponsorId, input);
expect(result).toEqual(mockResult);
expect(sponsorService.updateSponsorSettings).toHaveBeenCalledWith(sponsorId, input);
});
});
});

View File

@@ -23,7 +23,7 @@ import { RaceDTO } from './dtos/RaceDTO';
import { SponsorProfileDTO } from './dtos/SponsorProfileDTO';
import { NotificationSettingsDTO } from './dtos/NotificationSettingsDTO';
import { PrivacySettingsDTO } from './dtos/PrivacySettingsDTO';
import type { AcceptSponsorshipRequestResultPort } from '@core/racing/application/ports/output/AcceptSponsorshipRequestResultPort';
import type { AcceptSponsorshipRequestResultViewModel } from './presenters/AcceptSponsorshipRequestPresenter';
import type { RejectSponsorshipRequestResultDTO } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
@ApiTags('sponsors')
@@ -33,129 +33,212 @@ export class SponsorController {
@Get('pricing')
@ApiOperation({ summary: 'Get sponsorship pricing for an entity' })
@ApiResponse({ status: 200, description: 'Sponsorship pricing', type: GetEntitySponsorshipPricingResultDTO })
@ApiResponse({
status: 200,
description: 'Sponsorship pricing',
type: GetEntitySponsorshipPricingResultDTO,
})
async getEntitySponsorshipPricing(): Promise<GetEntitySponsorshipPricingResultDTO> {
return this.sponsorService.getEntitySponsorshipPricing();
const presenter = await this.sponsorService.getEntitySponsorshipPricing();
return presenter.viewModel;
}
@Get()
@ApiOperation({ summary: 'Get all sponsors' })
@ApiResponse({ status: 200, description: 'List of sponsors', type: GetSponsorsOutputDTO })
@ApiResponse({
status: 200,
description: 'List of sponsors',
type: GetSponsorsOutputDTO,
})
async getSponsors(): Promise<GetSponsorsOutputDTO> {
return this.sponsorService.getSponsors();
const presenter = await this.sponsorService.getSponsors();
return presenter.viewModel;
}
@Post()
@HttpCode(HttpStatus.CREATED)
@ApiOperation({ summary: 'Create a new sponsor' })
@ApiResponse({ status: 201, description: 'Sponsor created', type: CreateSponsorOutputDTO })
@ApiResponse({
status: 201,
description: 'Sponsor created',
type: CreateSponsorOutputDTO,
})
async createSponsor(@Body() input: CreateSponsorInputDTO): Promise<CreateSponsorOutputDTO> {
return this.sponsorService.createSponsor(input);
const presenter = await this.sponsorService.createSponsor(input);
return presenter.viewModel;
}
// Add other Sponsor endpoints here based on other presenters
@Get('dashboard/:sponsorId')
@ApiOperation({ summary: 'Get sponsor dashboard metrics and sponsored leagues' })
@ApiResponse({ status: 200, description: 'Sponsor dashboard data', type: SponsorDashboardDTO })
@ApiResponse({
status: 200,
description: 'Sponsor dashboard data',
type: SponsorDashboardDTO,
})
@ApiResponse({ status: 404, description: 'Sponsor not found' })
async getSponsorDashboard(@Param('sponsorId') sponsorId: string): Promise<SponsorDashboardDTO | null> {
return this.sponsorService.getSponsorDashboard({ sponsorId } as GetSponsorDashboardQueryParamsDTO);
async getSponsorDashboard(
@Param('sponsorId') sponsorId: string,
): Promise<SponsorDashboardDTO | null> {
const presenter = await this.sponsorService.getSponsorDashboard({
sponsorId,
} as GetSponsorDashboardQueryParamsDTO);
return presenter.viewModel;
}
@Get(':sponsorId/sponsorships')
@ApiOperation({ summary: 'Get all sponsorships for a given sponsor' })
@ApiResponse({ status: 200, description: 'List of sponsorships', type: SponsorSponsorshipsDTO })
@ApiOperation({
summary: 'Get all sponsorships for a given sponsor',
})
@ApiResponse({
status: 200,
description: 'List of sponsorships',
type: SponsorSponsorshipsDTO,
})
@ApiResponse({ status: 404, description: 'Sponsor not found' })
async getSponsorSponsorships(@Param('sponsorId') sponsorId: string): Promise<SponsorSponsorshipsDTO | null> {
return this.sponsorService.getSponsorSponsorships({ sponsorId } as GetSponsorSponsorshipsQueryParamsDTO);
async getSponsorSponsorships(
@Param('sponsorId') sponsorId: string,
): Promise<SponsorSponsorshipsDTO | null> {
const presenter = await this.sponsorService.getSponsorSponsorships({
sponsorId,
} as GetSponsorSponsorshipsQueryParamsDTO);
return presenter.viewModel;
}
@Get(':sponsorId')
@ApiOperation({ summary: 'Get a sponsor by ID' })
@ApiResponse({ status: 200, description: 'Sponsor data', type: GetSponsorOutputDTO })
@ApiResponse({
status: 200,
description: 'Sponsor data',
type: GetSponsorOutputDTO,
})
@ApiResponse({ status: 404, description: 'Sponsor not found' })
async getSponsor(@Param('sponsorId') sponsorId: string): Promise<GetSponsorOutputDTO | null> {
return this.sponsorService.getSponsor(sponsorId);
const presenter = await this.sponsorService.getSponsor(sponsorId);
return presenter.viewModel;
}
@Get('requests')
@ApiOperation({ summary: 'Get pending sponsorship requests' })
@ApiResponse({ status: 200, description: 'List of pending sponsorship requests', type: GetPendingSponsorshipRequestsOutputDTO })
async getPendingSponsorshipRequests(@Query() query: { entityType: string; entityId: string }): Promise<GetPendingSponsorshipRequestsOutputDTO> {
return this.sponsorService.getPendingSponsorshipRequests(query as { entityType: import('@core/racing/domain/entities/SponsorshipRequest').SponsorableEntityType; entityId: string });
@ApiResponse({
status: 200,
description: 'List of pending sponsorship requests',
type: GetPendingSponsorshipRequestsOutputDTO,
})
async getPendingSponsorshipRequests(
@Query() query: { entityType: string; entityId: string },
): Promise<GetPendingSponsorshipRequestsOutputDTO | null> {
const presenter = await this.sponsorService.getPendingSponsorshipRequests(
query as {
entityType: import('@core/racing/domain/entities/SponsorshipRequest').SponsorableEntityType;
entityId: string;
},
);
return presenter.viewModel;
}
@Post('requests/:requestId/accept')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Accept a sponsorship request' })
@ApiResponse({ status: 200, description: 'Sponsorship request accepted' })
@ApiResponse({ status: 400, description: 'Invalid request' })
@ApiResponse({ status: 404, description: 'Request not found' })
async acceptSponsorshipRequest(@Param('requestId') requestId: string, @Body() input: AcceptSponsorshipRequestInputDTO): Promise<AcceptSponsorshipRequestResultPort | null> {
return this.sponsorService.acceptSponsorshipRequest(requestId, input.respondedBy);
}
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Accept a sponsorship request' })
@ApiResponse({ status: 200, description: 'Sponsorship request accepted' })
@ApiResponse({ status: 400, description: 'Invalid request' })
@ApiResponse({ status: 404, description: 'Request not found' })
async acceptSponsorshipRequest(
@Param('requestId') requestId: string,
@Body() input: AcceptSponsorshipRequestInputDTO,
): Promise<AcceptSponsorshipRequestResultViewModel | null> {
const presenter = await this.sponsorService.acceptSponsorshipRequest(
requestId,
input.respondedBy,
);
return presenter.viewModel;
}
@Post('requests/:requestId/reject')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Reject a sponsorship request' })
@ApiResponse({ status: 200, description: 'Sponsorship request rejected' })
@ApiResponse({ status: 400, description: 'Invalid request' })
@ApiResponse({ status: 404, description: 'Request not found' })
async rejectSponsorshipRequest(@Param('requestId') requestId: string, @Body() input: RejectSponsorshipRequestInputDTO): Promise<RejectSponsorshipRequestResultDTO | null> {
return this.sponsorService.rejectSponsorshipRequest(requestId, input.respondedBy, input.reason);
}
@Post('requests/:requestId/reject')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Reject a sponsorship request' })
@ApiResponse({ status: 200, description: 'Sponsorship request rejected' })
@ApiResponse({ status: 400, description: 'Invalid request' })
@ApiResponse({ status: 404, description: 'Request not found' })
async rejectSponsorshipRequest(
@Param('requestId') requestId: string,
@Body() input: RejectSponsorshipRequestInputDTO,
): Promise<RejectSponsorshipRequestResultDTO | null> {
const presenter = await this.sponsorService.rejectSponsorshipRequest(
requestId,
input.respondedBy,
input.reason,
);
return presenter.viewModel;
}
@Get('billing/:sponsorId')
@ApiOperation({ summary: 'Get sponsor billing information' })
@ApiResponse({ status: 200, description: 'Sponsor billing data', type: Object })
async getSponsorBilling(@Param('sponsorId') sponsorId: string): Promise<{
paymentMethods: PaymentMethodDTO[];
invoices: InvoiceDTO[];
stats: BillingStatsDTO;
}> {
return this.sponsorService.getSponsorBilling(sponsorId);
}
@Get('billing/:sponsorId')
@ApiOperation({ summary: 'Get sponsor billing information' })
@ApiResponse({ status: 200, description: 'Sponsor billing data', type: Object })
async getSponsorBilling(
@Param('sponsorId') sponsorId: string,
): Promise<{
paymentMethods: PaymentMethodDTO[];
invoices: InvoiceDTO[];
stats: BillingStatsDTO;
}> {
const presenter = await this.sponsorService.getSponsorBilling(sponsorId);
return presenter.viewModel;
}
@Get('leagues/available')
@ApiOperation({ summary: 'Get available leagues for sponsorship' })
@ApiResponse({ status: 200, description: 'Available leagues', type: [AvailableLeagueDTO] })
async getAvailableLeagues(): Promise<AvailableLeagueDTO[]> {
return this.sponsorService.getAvailableLeagues();
}
@Get('leagues/available')
@ApiOperation({ summary: 'Get available leagues for sponsorship' })
@ApiResponse({
status: 200,
description: 'Available leagues',
type: [AvailableLeagueDTO],
})
async getAvailableLeagues(): Promise<AvailableLeagueDTO[] | null> {
const presenter = await this.sponsorService.getAvailableLeagues();
return presenter.viewModel;
}
@Get('leagues/:leagueId/detail')
@ApiOperation({ summary: 'Get detailed league information for sponsors' })
@ApiResponse({ status: 200, description: 'League detail data', type: Object })
async getLeagueDetail(@Param('leagueId') leagueId: string): Promise<{
league: LeagueDetailDTO;
drivers: DriverDTO[];
races: RaceDTO[];
}> {
return this.sponsorService.getLeagueDetail(leagueId);
}
@Get('leagues/:leagueId/detail')
@ApiOperation({ summary: 'Get detailed league information for sponsors' })
@ApiResponse({ status: 200, description: 'League detail data', type: Object })
async getLeagueDetail(
@Param('leagueId') leagueId: string,
): Promise<{
league: LeagueDetailDTO;
drivers: DriverDTO[];
races: RaceDTO[];
} | null> {
const presenter = await this.sponsorService.getLeagueDetail(leagueId);
return presenter.viewModel;
}
@Get('settings/:sponsorId')
@ApiOperation({ summary: 'Get sponsor settings' })
@ApiResponse({ status: 200, description: 'Sponsor settings', type: Object })
async getSponsorSettings(@Param('sponsorId') sponsorId: string): Promise<{
profile: SponsorProfileDTO;
notifications: NotificationSettingsDTO;
privacy: PrivacySettingsDTO;
}> {
return this.sponsorService.getSponsorSettings(sponsorId);
}
@Get('settings/:sponsorId')
@ApiOperation({ summary: 'Get sponsor settings' })
@ApiResponse({ status: 200, description: 'Sponsor settings', type: Object })
async getSponsorSettings(
@Param('sponsorId') sponsorId: string,
): Promise<{
profile: SponsorProfileDTO;
notifications: NotificationSettingsDTO;
privacy: PrivacySettingsDTO;
} | null> {
const presenter = await this.sponsorService.getSponsorSettings(sponsorId);
return presenter.viewModel;
}
@Put('settings/:sponsorId')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Update sponsor settings' })
@ApiResponse({ status: 200, description: 'Settings updated successfully' })
async updateSponsorSettings(
@Param('sponsorId') sponsorId: string,
@Body() input: {
profile?: Partial<SponsorProfileDTO>;
notifications?: Partial<NotificationSettingsDTO>;
privacy?: Partial<PrivacySettingsDTO>;
}
): Promise<void> {
return this.sponsorService.updateSponsorSettings(sponsorId, input);
}
@Put('settings/:sponsorId')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Update sponsor settings' })
@ApiResponse({ status: 200, description: 'Settings updated successfully' })
async updateSponsorSettings(
@Param('sponsorId') sponsorId: string,
@Body()
input: {
profile?: Partial<SponsorProfileDTO>;
notifications?: Partial<NotificationSettingsDTO>;
privacy?: Partial<PrivacySettingsDTO>;
},
): Promise<{ success: boolean; errorCode?: string; message?: string } | null> {
const presenter = await this.sponsorService.updateSponsorSettings(sponsorId, input);
return presenter.viewModel;
}
}

View File

@@ -1,5 +1,6 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { SponsorService } from './SponsorService';
import { Result } from '@core/shared/application/Result';
import type { Logger } from '@core/shared/application';
import type { GetSponsorshipPricingUseCase } from '@core/racing/application/use-cases/GetSponsorshipPricingUseCase';
import type { GetSponsorsUseCase } from '@core/racing/application/use-cases/GetSponsorsUseCase';
import type { CreateSponsorUseCase } from '@core/racing/application/use-cases/CreateSponsorUseCase';
@@ -9,8 +10,7 @@ import type { GetSponsorUseCase } from '@core/racing/application/use-cases/GetSp
import type { GetPendingSponsorshipRequestsUseCase } from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
import type { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
import type { RejectSponsorshipRequestUseCase } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/application/Result';
import { SponsorService } from './SponsorService';
describe('SponsorService', () => {
let service: SponsorService;
@@ -23,12 +23,7 @@ describe('SponsorService', () => {
let getPendingSponsorshipRequestsUseCase: { execute: Mock };
let acceptSponsorshipRequestUseCase: { execute: Mock };
let rejectSponsorshipRequestUseCase: { execute: Mock };
let logger: {
debug: Mock;
info: Mock;
warn: Mock;
error: Mock;
};
let logger: Logger;
beforeEach(() => {
getSponsorshipPricingUseCase = { execute: vi.fn() };
@@ -45,7 +40,7 @@ describe('SponsorService', () => {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
} as unknown as Logger;
service = new SponsorService(
getSponsorshipPricingUseCase as unknown as GetSponsorshipPricingUseCase,
@@ -57,136 +52,199 @@ describe('SponsorService', () => {
getPendingSponsorshipRequestsUseCase as unknown as GetPendingSponsorshipRequestsUseCase,
acceptSponsorshipRequestUseCase as unknown as AcceptSponsorshipRequestUseCase,
rejectSponsorshipRequestUseCase as unknown as RejectSponsorshipRequestUseCase,
logger as unknown as Logger,
logger,
);
});
describe('getEntitySponsorshipPricing', () => {
it('should return sponsorship pricing', async () => {
const mockPresenter = {
viewModel: { entityType: 'season', entityId: 'season-1', pricing: [] },
it('returns presenter with pricing data on success', async () => {
const outputPort = {
entityType: 'season',
entityId: 'season-1',
pricing: [
{ id: 'tier-gold', level: 'Gold', price: 500, currency: 'USD' },
],
};
getSponsorshipPricingUseCase.execute.mockResolvedValue(undefined);
getSponsorshipPricingUseCase.execute.mockResolvedValue(Result.ok(outputPort));
// Mock the presenter
const originalGetSponsorshipPricingPresenter = await import('./presenters/GetSponsorshipPricingPresenter');
const mockPresenterClass = vi.fn().mockImplementation(() => mockPresenter);
vi.doMock('./presenters/GetSponsorshipPricingPresenter', () => ({
GetSponsorshipPricingPresenter: mockPresenterClass,
}));
const presenter = await service.getEntitySponsorshipPricing();
const result = await service.getEntitySponsorshipPricing();
expect(presenter.viewModel).toEqual({
entityType: 'season',
entityId: 'season-1',
pricing: [
{ id: 'tier-gold', level: 'Gold', price: 500, currency: 'USD' },
],
});
});
expect(result).toEqual(mockPresenter.viewModel);
expect(getSponsorshipPricingUseCase.execute).toHaveBeenCalledWith(undefined, mockPresenter);
it('returns empty pricing on error', async () => {
getSponsorshipPricingUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
const presenter = await service.getEntitySponsorshipPricing();
expect(presenter.viewModel).toEqual({
entityType: 'season',
entityId: '',
pricing: [],
});
});
});
describe('getSponsors', () => {
it('should return sponsors list', async () => {
const mockPresenter = {
viewModel: { sponsors: [] },
};
getSponsorsUseCase.execute.mockResolvedValue(undefined);
it('returns sponsors in presenter on success', async () => {
const outputPort = { sponsors: [{ id: 's1', name: 'S1', contactEmail: 's1@test', createdAt: new Date() }] };
getSponsorsUseCase.execute.mockResolvedValue(Result.ok(outputPort));
const result = await service.getSponsors();
const presenter = await service.getSponsors();
expect(result).toEqual(mockPresenter.viewModel);
expect(getSponsorsUseCase.execute).toHaveBeenCalledWith(undefined, expect.any(Object));
expect(presenter.viewModel).toEqual({ sponsors: outputPort.sponsors });
});
it('returns empty list on error', async () => {
getSponsorsUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
const presenter = await service.getSponsors();
expect(presenter.viewModel).toEqual({ sponsors: [] });
});
});
describe('createSponsor', () => {
it('should create sponsor successfully', async () => {
const input = { name: 'Test Sponsor', contactEmail: 'test@example.com' };
const mockPresenter = {
viewModel: { id: 'sponsor-1', name: 'Test Sponsor' },
it('returns created sponsor in presenter on success', async () => {
const input = { name: 'Test', contactEmail: 'test@example.com' };
const outputPort = {
sponsor: {
id: 's1',
name: 'Test',
contactEmail: 'test@example.com',
createdAt: new Date(),
},
};
createSponsorUseCase.execute.mockResolvedValue(undefined);
createSponsorUseCase.execute.mockResolvedValue(Result.ok(outputPort));
const result = await service.createSponsor(input);
const presenter = await service.createSponsor(input as any);
expect(result).toEqual(mockPresenter.viewModel);
expect(createSponsorUseCase.execute).toHaveBeenCalledWith(input, expect.any(Object));
expect(presenter.viewModel).toEqual({ sponsor: outputPort.sponsor });
});
it('throws on error', async () => {
const input = { name: 'Test', contactEmail: 'test@example.com' };
createSponsorUseCase.execute.mockResolvedValue(
Result.err({ code: 'VALIDATION_ERROR', details: { message: 'Invalid' } }),
);
await expect(service.createSponsor(input as any)).rejects.toThrow('Invalid');
});
});
describe('getSponsorDashboard', () => {
it('should return sponsor dashboard', async () => {
const params = { sponsorId: 'sponsor-1' };
const mockPresenter = {
viewModel: { sponsorId: 'sponsor-1', metrics: {}, sponsoredLeagues: [] },
it('returns dashboard in presenter on success', async () => {
const params = { sponsorId: 's1' };
const outputPort = {
sponsorId: 's1',
sponsorName: 'S1',
metrics: {} as any,
sponsoredLeagues: [],
investment: {} as any,
};
getSponsorDashboardUseCase.execute.mockResolvedValue(undefined);
getSponsorDashboardUseCase.execute.mockResolvedValue(Result.ok(outputPort));
const result = await service.getSponsorDashboard(params);
const presenter = await service.getSponsorDashboard(params as any);
expect(result).toEqual(mockPresenter.viewModel);
expect(getSponsorDashboardUseCase.execute).toHaveBeenCalledWith(params, expect.any(Object));
expect(presenter.viewModel).toEqual(outputPort);
});
it('returns null in presenter on error', async () => {
const params = { sponsorId: 's1' };
getSponsorDashboardUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
const presenter = await service.getSponsorDashboard(params as any);
expect(presenter.viewModel).toBeNull();
});
});
describe('getSponsorSponsorships', () => {
it('should return sponsor sponsorships', async () => {
const params = { sponsorId: 'sponsor-1' };
const mockPresenter = {
viewModel: { sponsorId: 'sponsor-1', sponsorships: [] },
it('returns sponsorships in presenter on success', async () => {
const params = { sponsorId: 's1' };
const outputPort = {
sponsorId: 's1',
sponsorName: 'S1',
sponsorships: [],
summary: {
totalSponsorships: 0,
activeSponsorships: 0,
totalInvestment: 0,
totalPlatformFees: 0,
currency: 'USD',
},
};
getSponsorSponsorshipsUseCase.execute.mockResolvedValue(undefined);
getSponsorSponsorshipsUseCase.execute.mockResolvedValue(Result.ok(outputPort));
const result = await service.getSponsorSponsorships(params);
const presenter = await service.getSponsorSponsorships(params as any);
expect(result).toEqual(mockPresenter.viewModel);
expect(getSponsorSponsorshipsUseCase.execute).toHaveBeenCalledWith(params, expect.any(Object));
expect(presenter.viewModel).toEqual(outputPort);
});
it('returns null in presenter on error', async () => {
const params = { sponsorId: 's1' };
getSponsorSponsorshipsUseCase.execute.mockResolvedValue(
Result.err({ code: 'REPOSITORY_ERROR' }),
);
const presenter = await service.getSponsorSponsorships(params as any);
expect(presenter.viewModel).toBeNull();
});
});
describe('getSponsor', () => {
it('should return sponsor when found', async () => {
const sponsorId = 'sponsor-1';
const mockSponsor = { id: sponsorId, name: 'Test Sponsor' };
getSponsorUseCase.execute.mockResolvedValue(Result.ok(mockSponsor));
it('returns sponsor in presenter when found', async () => {
const sponsorId = 's1';
const output = { sponsor: { id: sponsorId, name: 'S1' } };
getSponsorUseCase.execute.mockResolvedValue(Result.ok(output));
const result = await service.getSponsor(sponsorId);
const presenter = await service.getSponsor(sponsorId);
expect(result).toEqual(mockSponsor);
expect(getSponsorUseCase.execute).toHaveBeenCalledWith({ sponsorId });
expect(presenter.viewModel).toEqual({ sponsor: output.sponsor });
});
it('should return null when sponsor not found', async () => {
const sponsorId = 'sponsor-1';
getSponsorUseCase.execute.mockResolvedValue(Result.err({ code: 'NOT_FOUND' }));
it('returns null in presenter when not found', async () => {
const sponsorId = 's1';
getSponsorUseCase.execute.mockResolvedValue(Result.ok(null));
const result = await service.getSponsor(sponsorId);
const presenter = await service.getSponsor(sponsorId);
expect(result).toBeNull();
expect(presenter.viewModel).toBeNull();
});
});
describe('getPendingSponsorshipRequests', () => {
it('should return pending sponsorship requests', async () => {
it('returns requests in presenter on success', async () => {
const params = { entityType: 'season' as const, entityId: 'season-1' };
const mockResult = {
const outputPort = {
entityType: 'season',
entityId: 'season-1',
requests: [],
totalCount: 0,
};
getPendingSponsorshipRequestsUseCase.execute.mockResolvedValue(Result.ok(mockResult));
getPendingSponsorshipRequestsUseCase.execute.mockResolvedValue(Result.ok(outputPort));
const result = await service.getPendingSponsorshipRequests(params);
const presenter = await service.getPendingSponsorshipRequests(params);
expect(result).toEqual(mockResult);
expect(getPendingSponsorshipRequestsUseCase.execute).toHaveBeenCalledWith(params);
expect(presenter.viewModel).toEqual(outputPort);
});
it('should return empty result on error', async () => {
it('returns empty result on error', async () => {
const params = { entityType: 'season' as const, entityId: 'season-1' };
getPendingSponsorshipRequestsUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
getPendingSponsorshipRequestsUseCase.execute.mockResolvedValue(
Result.err({ code: 'REPOSITORY_ERROR' }),
);
const result = await service.getPendingSponsorshipRequests(params);
const presenter = await service.getPendingSponsorshipRequests(params);
expect(result).toEqual({
expect(presenter.viewModel).toEqual({
entityType: 'season',
entityId: 'season-1',
requests: [],
@@ -196,63 +254,113 @@ describe('SponsorService', () => {
});
describe('acceptSponsorshipRequest', () => {
it('should accept sponsorship request successfully', async () => {
const requestId = 'request-1';
const respondedBy = 'user-1';
const mockResult = {
it('returns accept result in presenter on success', async () => {
const requestId = 'r1';
const respondedBy = 'u1';
const outputPort = {
requestId,
sponsorshipId: 'sponsorship-1',
sponsorshipId: 'sp1',
status: 'accepted' as const,
acceptedAt: new Date(),
platformFee: 10,
netAmount: 90,
};
acceptSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(mockResult));
acceptSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(outputPort));
const result = await service.acceptSponsorshipRequest(requestId, respondedBy);
const presenter = await service.acceptSponsorshipRequest(requestId, respondedBy);
expect(result).toEqual(mockResult);
expect(acceptSponsorshipRequestUseCase.execute).toHaveBeenCalledWith({ requestId, respondedBy });
expect(presenter.viewModel).toEqual(outputPort);
});
it('should return null on error', async () => {
const requestId = 'request-1';
const respondedBy = 'user-1';
acceptSponsorshipRequestUseCase.execute.mockResolvedValue(Result.err({ code: 'NOT_FOUND' }));
it('returns null in presenter on error', async () => {
const requestId = 'r1';
const respondedBy = 'u1';
acceptSponsorshipRequestUseCase.execute.mockResolvedValue(
Result.err({ code: 'SPONSORSHIP_REQUEST_NOT_FOUND' }),
);
const result = await service.acceptSponsorshipRequest(requestId, respondedBy);
const presenter = await service.acceptSponsorshipRequest(requestId, respondedBy);
expect(result).toBeNull();
expect(presenter.viewModel).toBeNull();
});
});
describe('rejectSponsorshipRequest', () => {
it('should reject sponsorship request successfully', async () => {
const requestId = 'request-1';
const respondedBy = 'user-1';
it('returns reject result in presenter on success', async () => {
const requestId = 'r1';
const respondedBy = 'u1';
const reason = 'Not interested';
const mockResult = {
const output = {
requestId,
status: 'rejected' as const,
rejectedAt: new Date(),
reason,
};
rejectSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(mockResult));
rejectSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(output));
const result = await service.rejectSponsorshipRequest(requestId, respondedBy, reason);
const presenter = await service.rejectSponsorshipRequest(requestId, respondedBy, reason);
expect(result).toEqual(mockResult);
expect(rejectSponsorshipRequestUseCase.execute).toHaveBeenCalledWith({ requestId, respondedBy, reason });
expect(presenter.viewModel).toEqual(output);
});
it('should return null on error', async () => {
const requestId = 'request-1';
const respondedBy = 'user-1';
rejectSponsorshipRequestUseCase.execute.mockResolvedValue(Result.err({ code: 'NOT_FOUND' }));
it('returns null in presenter on error', async () => {
const requestId = 'r1';
const respondedBy = 'u1';
rejectSponsorshipRequestUseCase.execute.mockResolvedValue(
Result.err({ code: 'SPONSORSHIP_REQUEST_NOT_FOUND' }),
);
const result = await service.rejectSponsorshipRequest(requestId, respondedBy);
const presenter = await service.rejectSponsorshipRequest(requestId, respondedBy);
expect(result).toBeNull();
expect(presenter.viewModel).toBeNull();
});
});
});
describe('getSponsorBilling', () => {
it('returns mock billing data in presenter', async () => {
const presenter = await service.getSponsorBilling('s1');
expect(presenter.viewModel).not.toBeNull();
expect(presenter.viewModel?.paymentMethods).toBeInstanceOf(Array);
expect(presenter.viewModel?.invoices).toBeInstanceOf(Array);
expect(presenter.viewModel?.stats).toBeDefined();
});
});
describe('getAvailableLeagues', () => {
it('returns mock leagues in presenter', async () => {
const presenter = await service.getAvailableLeagues();
expect(presenter.viewModel).not.toBeNull();
expect(presenter.viewModel?.length).toBeGreaterThan(0);
});
});
describe('getLeagueDetail', () => {
it('returns league detail in presenter', async () => {
const presenter = await service.getLeagueDetail('league-1');
expect(presenter.viewModel).not.toBeNull();
expect(presenter.viewModel?.league.id).toBe('league-1');
});
});
describe('getSponsorSettings', () => {
it('returns settings in presenter', async () => {
const presenter = await service.getSponsorSettings('s1');
expect(presenter.viewModel).not.toBeNull();
expect(presenter.viewModel?.profile).toBeDefined();
expect(presenter.viewModel?.notifications).toBeDefined();
expect(presenter.viewModel?.privacy).toBeDefined();
});
});
describe('updateSponsorSettings', () => {
it('returns success result in presenter', async () => {
const presenter = await service.updateSponsorSettings('s1', {});
expect(presenter.viewModel).toEqual({ success: true });
});
});
});

View File

@@ -1,14 +1,7 @@
import { Injectable, Inject } from '@nestjs/common';
import { GetEntitySponsorshipPricingResultDTO } from './dtos/GetEntitySponsorshipPricingResultDTO';
import { GetSponsorsOutputDTO } from './dtos/GetSponsorsOutputDTO';
import { CreateSponsorInputDTO } from './dtos/CreateSponsorInputDTO';
import { CreateSponsorOutputDTO } from './dtos/CreateSponsorOutputDTO';
import { GetSponsorDashboardQueryParamsDTO } from './dtos/GetSponsorDashboardQueryParamsDTO';
import { SponsorDashboardDTO } from './dtos/SponsorDashboardDTO';
import { GetSponsorSponsorshipsQueryParamsDTO } from './dtos/GetSponsorSponsorshipsQueryParamsDTO';
import { SponsorSponsorshipsDTO } from './dtos/SponsorSponsorshipsDTO';
import { GetSponsorOutputDTO } from './dtos/GetSponsorOutputDTO';
import { GetPendingSponsorshipRequestsOutputDTO } from './dtos/GetPendingSponsorshipRequestsOutputDTO';
import { AcceptSponsorshipRequestInputDTO } from './dtos/AcceptSponsorshipRequestInputDTO';
import { RejectSponsorshipRequestInputDTO } from './dtos/RejectSponsorshipRequestInputDTO';
import { PaymentMethodDTO } from './dtos/PaymentMethodDTO';
@@ -29,139 +22,253 @@ import { CreateSponsorUseCase } from '@core/racing/application/use-cases/CreateS
import { GetSponsorDashboardUseCase } from '@core/racing/application/use-cases/GetSponsorDashboardUseCase';
import { GetSponsorSponsorshipsUseCase } from '@core/racing/application/use-cases/GetSponsorSponsorshipsUseCase';
import { GetSponsorUseCase } from '@core/racing/application/use-cases/GetSponsorUseCase';
import { GetPendingSponsorshipRequestsUseCase, GetPendingSponsorshipRequestsDTO } from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
import {
GetPendingSponsorshipRequestsUseCase,
GetPendingSponsorshipRequestsDTO,
} from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
import { AcceptSponsorshipRequestUseCase } from '@core/racing/application/use-cases/AcceptSponsorshipRequestUseCase';
import { RejectSponsorshipRequestUseCase } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
import type { SponsorableEntityType } from '@core/racing/domain/entities/SponsorshipRequest';
import type { AcceptSponsorshipRequestResultPort } from '@core/racing/application/ports/output/AcceptSponsorshipRequestResultPort';
import type { RejectSponsorshipRequestResultDTO } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
import type { Logger } from '@core/shared/application';
// Presenters
import { GetEntitySponsorshipPricingPresenter } from './presenters/GetEntitySponsorshipPricingPresenter';
import { GetSponsorsPresenter } from './presenters/GetSponsorsPresenter';
import { CreateSponsorPresenter } from './presenters/CreateSponsorPresenter';
import { GetSponsorDashboardPresenter } from './presenters/GetSponsorDashboardPresenter';
import { GetSponsorSponsorshipsPresenter } from './presenters/GetSponsorSponsorshipsPresenter';
import { GetSponsorPresenter } from './presenters/GetSponsorPresenter';
import { GetPendingSponsorshipRequestsPresenter } from './presenters/GetPendingSponsorshipRequestsPresenter';
import { AcceptSponsorshipRequestPresenter } from './presenters/AcceptSponsorshipRequestPresenter';
import { RejectSponsorshipRequestPresenter } from './presenters/RejectSponsorshipRequestPresenter';
import { SponsorBillingPresenter } from './presenters/SponsorBillingPresenter';
import { AvailableLeaguesPresenter } from './presenters/AvailableLeaguesPresenter';
import { LeagueDetailPresenter } from './presenters/LeagueDetailPresenter';
import { SponsorSettingsPresenter } from './presenters/SponsorSettingsPresenter';
import { SponsorSettingsUpdatePresenter } from './presenters/SponsorSettingsUpdatePresenter';
// Tokens
import { GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN, GET_SPONSORS_USE_CASE_TOKEN, CREATE_SPONSOR_USE_CASE_TOKEN, GET_SPONSOR_DASHBOARD_USE_CASE_TOKEN, GET_SPONSOR_SPONSORSHIPS_USE_CASE_TOKEN, GET_SPONSOR_USE_CASE_TOKEN, GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN, ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN, REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN, LOGGER_TOKEN } from './SponsorProviders';
import type { Logger } from '@core/shared/application';
import {
GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN,
GET_SPONSORS_USE_CASE_TOKEN,
CREATE_SPONSOR_USE_CASE_TOKEN,
GET_SPONSOR_DASHBOARD_USE_CASE_TOKEN,
GET_SPONSOR_SPONSORSHIPS_USE_CASE_TOKEN,
GET_SPONSOR_USE_CASE_TOKEN,
GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN,
ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN,
LOGGER_TOKEN,
} from './SponsorProviders';
@Injectable()
export class SponsorService {
constructor(
@Inject(GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN) private readonly getSponsorshipPricingUseCase: GetSponsorshipPricingUseCase,
@Inject(GET_SPONSORS_USE_CASE_TOKEN) private readonly getSponsorsUseCase: GetSponsorsUseCase,
@Inject(CREATE_SPONSOR_USE_CASE_TOKEN) private readonly createSponsorUseCase: CreateSponsorUseCase,
@Inject(GET_SPONSOR_DASHBOARD_USE_CASE_TOKEN) private readonly getSponsorDashboardUseCase: GetSponsorDashboardUseCase,
@Inject(GET_SPONSOR_SPONSORSHIPS_USE_CASE_TOKEN) private readonly getSponsorSponsorshipsUseCase: GetSponsorSponsorshipsUseCase,
@Inject(GET_SPONSOR_USE_CASE_TOKEN) private readonly getSponsorUseCase: GetSponsorUseCase,
@Inject(GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN) private readonly getPendingSponsorshipRequestsUseCase: GetPendingSponsorshipRequestsUseCase,
@Inject(ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN) private readonly acceptSponsorshipRequestUseCase: AcceptSponsorshipRequestUseCase,
@Inject(REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN) private readonly rejectSponsorshipRequestUseCase: RejectSponsorshipRequestUseCase,
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
@Inject(GET_SPONSORSHIP_PRICING_USE_CASE_TOKEN)
private readonly getSponsorshipPricingUseCase: GetSponsorshipPricingUseCase,
@Inject(GET_SPONSORS_USE_CASE_TOKEN)
private readonly getSponsorsUseCase: GetSponsorsUseCase,
@Inject(CREATE_SPONSOR_USE_CASE_TOKEN)
private readonly createSponsorUseCase: CreateSponsorUseCase,
@Inject(GET_SPONSOR_DASHBOARD_USE_CASE_TOKEN)
private readonly getSponsorDashboardUseCase: GetSponsorDashboardUseCase,
@Inject(GET_SPONSOR_SPONSORSHIPS_USE_CASE_TOKEN)
private readonly getSponsorSponsorshipsUseCase: GetSponsorSponsorshipsUseCase,
@Inject(GET_SPONSOR_USE_CASE_TOKEN)
private readonly getSponsorUseCase: GetSponsorUseCase,
@Inject(GET_PENDING_SPONSORSHIP_REQUESTS_USE_CASE_TOKEN)
private readonly getPendingSponsorshipRequestsUseCase: GetPendingSponsorshipRequestsUseCase,
@Inject(ACCEPT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN)
private readonly acceptSponsorshipRequestUseCase: AcceptSponsorshipRequestUseCase,
@Inject(REJECT_SPONSORSHIP_REQUEST_USE_CASE_TOKEN)
private readonly rejectSponsorshipRequestUseCase: RejectSponsorshipRequestUseCase,
@Inject(LOGGER_TOKEN)
private readonly logger: Logger,
) {}
async getEntitySponsorshipPricing(): Promise<GetEntitySponsorshipPricingResultDTO> {
async getEntitySponsorshipPricing(): Promise<GetEntitySponsorshipPricingPresenter> {
this.logger.debug('[SponsorService] Fetching sponsorship pricing.');
const presenter = new GetEntitySponsorshipPricingPresenter();
const result = await this.getSponsorshipPricingUseCase.execute();
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsorship pricing.', result.error);
return { entityType: 'season', entityId: '', pricing: [] };
presenter.present(null);
return presenter;
}
return result.value as GetEntitySponsorshipPricingResultDTO;
presenter.present(result.value);
return presenter;
}
async getSponsors(): Promise<GetSponsorsOutputDTO> {
async getSponsors(): Promise<GetSponsorsPresenter> {
this.logger.debug('[SponsorService] Fetching sponsors.');
const presenter = new GetSponsorsPresenter();
const result = await this.getSponsorsUseCase.execute();
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsors.', result.error);
return { sponsors: [] };
presenter.present({ sponsors: [] });
return presenter;
}
return result.value as GetSponsorsOutputDTO;
presenter.present(result.value);
return presenter;
}
async createSponsor(input: CreateSponsorInputDTO): Promise<CreateSponsorOutputDTO> {
async createSponsor(input: CreateSponsorInputDTO): Promise<CreateSponsorPresenter> {
this.logger.debug('[SponsorService] Creating sponsor.', { input });
const presenter = new CreateSponsorPresenter();
const result = await this.createSponsorUseCase.execute(input);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to create sponsor.', result.error);
throw new Error(result.error.details?.message || 'Failed to create sponsor');
}
return result.value as CreateSponsorOutputDTO;
presenter.present(result.value);
return presenter;
}
async getSponsorDashboard(params: GetSponsorDashboardQueryParamsDTO): Promise<SponsorDashboardDTO | null> {
async getSponsorDashboard(
params: GetSponsorDashboardQueryParamsDTO,
): Promise<GetSponsorDashboardPresenter> {
this.logger.debug('[SponsorService] Fetching sponsor dashboard.', { params });
const presenter = new GetSponsorDashboardPresenter();
const result = await this.getSponsorDashboardUseCase.execute(params);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsor dashboard.', result.error);
return null;
presenter.present(null);
return presenter;
}
return result.value as SponsorDashboardDTO | null;
presenter.present(result.value);
return presenter;
}
async getSponsorSponsorships(params: GetSponsorSponsorshipsQueryParamsDTO): Promise<SponsorSponsorshipsDTO | null> {
async getSponsorSponsorships(
params: GetSponsorSponsorshipsQueryParamsDTO,
): Promise<GetSponsorSponsorshipsPresenter> {
this.logger.debug('[SponsorService] Fetching sponsor sponsorships.', { params });
const presenter = new GetSponsorSponsorshipsPresenter();
const result = await this.getSponsorSponsorshipsUseCase.execute(params);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsor sponsorships.', result.error);
return null;
presenter.present(null);
return presenter;
}
return result.value as SponsorSponsorshipsDTO | null;
presenter.present(result.value);
return presenter;
}
async getSponsor(sponsorId: string): Promise<GetSponsorOutputDTO | null> {
async getSponsor(sponsorId: string): Promise<GetSponsorPresenter> {
this.logger.debug('[SponsorService] Fetching sponsor.', { sponsorId });
const presenter = new GetSponsorPresenter();
const result = await this.getSponsorUseCase.execute({ sponsorId });
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch sponsor.', result.error);
return null;
presenter.present(null);
return presenter;
}
return result.value as GetSponsorOutputDTO | null;
presenter.present(result.value);
return presenter;
}
async getPendingSponsorshipRequests(params: { entityType: SponsorableEntityType; entityId: string }): Promise<GetPendingSponsorshipRequestsOutputDTO> {
async getPendingSponsorshipRequests(params: {
entityType: SponsorableEntityType;
entityId: string;
}): Promise<GetPendingSponsorshipRequestsPresenter> {
this.logger.debug('[SponsorService] Fetching pending sponsorship requests.', { params });
const result = await this.getPendingSponsorshipRequestsUseCase.execute(params as GetPendingSponsorshipRequestsDTO);
const presenter = new GetPendingSponsorshipRequestsPresenter();
const result = await this.getPendingSponsorshipRequestsUseCase.execute(
params as GetPendingSponsorshipRequestsDTO,
);
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to fetch pending sponsorship requests.', result.error);
return { entityType: params.entityType, entityId: params.entityId, requests: [], totalCount: 0 };
presenter.present({
entityType: params.entityType,
entityId: params.entityId,
requests: [],
totalCount: 0,
});
return presenter;
}
return result.value as GetPendingSponsorshipRequestsOutputDTO;
presenter.present(result.value);
return presenter;
}
async acceptSponsorshipRequest(requestId: string, respondedBy: string): Promise<AcceptSponsorshipRequestResultPort | null> {
this.logger.debug('[SponsorService] Accepting sponsorship request.', { requestId, respondedBy });
async acceptSponsorshipRequest(
requestId: string,
respondedBy: string,
): Promise<AcceptSponsorshipRequestPresenter> {
this.logger.debug('[SponsorService] Accepting sponsorship request.', {
requestId,
respondedBy,
});
const presenter = new AcceptSponsorshipRequestPresenter();
const result = await this.acceptSponsorshipRequestUseCase.execute({
requestId,
respondedBy,
} as AcceptSponsorshipRequestInputDTO);
const result = await this.acceptSponsorshipRequestUseCase.execute({ requestId, respondedBy });
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to accept sponsorship request.', result.error);
return null;
presenter.present(null);
return presenter;
}
return result.value;
presenter.present(result.value);
return presenter;
}
async rejectSponsorshipRequest(requestId: string, respondedBy: string, reason?: string): Promise<RejectSponsorshipRequestResultDTO | null> {
this.logger.debug('[SponsorService] Rejecting sponsorship request.', { requestId, respondedBy, reason });
async rejectSponsorshipRequest(
requestId: string,
respondedBy: string,
reason?: string,
): Promise<RejectSponsorshipRequestPresenter> {
this.logger.debug('[SponsorService] Rejecting sponsorship request.', {
requestId,
respondedBy,
reason,
});
const presenter = new RejectSponsorshipRequestPresenter();
const result = await this.rejectSponsorshipRequestUseCase.execute({
requestId,
respondedBy,
reason,
} as RejectSponsorshipRequestInputDTO);
const result = await this.rejectSponsorshipRequestUseCase.execute({ requestId, respondedBy, reason });
if (result.isErr()) {
this.logger.error('[SponsorService] Failed to reject sponsorship request.', result.error);
return null;
presenter.present(null);
return presenter;
}
return result.value;
presenter.present(result.value);
return presenter;
}
async getSponsorBilling(sponsorId: string): Promise<{
paymentMethods: PaymentMethodDTO[];
invoices: InvoiceDTO[];
stats: BillingStatsDTO;
}> {
async getSponsorBilling(sponsorId: string): Promise<SponsorBillingPresenter> {
this.logger.debug('[SponsorService] Fetching sponsor billing.', { sponsorId });
const presenter = new SponsorBillingPresenter();
// Mock data - in real implementation, this would come from repositories
const paymentMethods: PaymentMethodDTO[] = [
{
@@ -242,14 +349,16 @@ export class SponsorService {
averageMonthlySpend: 2075,
};
return { paymentMethods, invoices, stats };
presenter.present({ paymentMethods, invoices, stats });
return presenter;
}
async getAvailableLeagues(): Promise<AvailableLeagueDTO[]> {
async getAvailableLeagues(): Promise<AvailableLeaguesPresenter> {
this.logger.debug('[SponsorService] Fetching available leagues.');
// Mock data
return [
const presenter = new AvailableLeaguesPresenter();
const leagues: AvailableLeagueDTO[] = [
{
id: 'league-1',
name: 'GT3 Masters Championship',
@@ -262,7 +371,8 @@ export class SponsorService {
tier: 'premium',
nextRace: '2025-12-20',
seasonStatus: 'active',
description: 'Premier GT3 racing with top-tier drivers. Weekly broadcasts and active community.',
description:
'Premier GT3 racing with top-tier drivers. Weekly broadcasts and active community.',
},
{
id: 'league-2',
@@ -276,18 +386,20 @@ export class SponsorService {
tier: 'premium',
nextRace: '2026-01-05',
seasonStatus: 'active',
description: 'Multi-class endurance racing. High engagement from dedicated endurance fans.',
description:
'Multi-class endurance racing. High engagement from dedicated endurance fans.',
},
];
presenter.present(leagues);
return presenter;
}
async getLeagueDetail(leagueId: string): Promise<{
league: LeagueDetailDTO;
drivers: DriverDTO[];
races: RaceDTO[];
}> {
async getLeagueDetail(leagueId: string): Promise<LeagueDetailPresenter> {
this.logger.debug('[SponsorService] Fetching league detail.', { leagueId });
const presenter = new LeagueDetailPresenter();
// Mock data
const league: LeagueDetailDTO = {
id: leagueId,
@@ -295,7 +407,8 @@ export class SponsorService {
game: 'iRacing',
tier: 'premium',
season: 'Season 3',
description: 'Premier GT3 racing with top-tier drivers competing across the world\'s most iconic circuits.',
description:
"Premier GT3 racing with top-tier drivers competing across the world's most iconic circuits.",
drivers: 48,
races: 12,
completedRaces: 8,
@@ -316,7 +429,7 @@ export class SponsorService {
'Race results page branding',
'Social media feature posts',
'Newsletter sponsor spot',
]
],
},
secondary: {
available: 1,
@@ -327,31 +440,58 @@ export class SponsorService {
'League page sidebar placement',
'Race results mention',
'Social media mentions',
]
],
},
},
};
const drivers: DriverDTO[] = [
{ id: 'd1', name: 'Max Verstappen', country: 'NL', position: 1, races: 8, impressions: 4200, team: 'Red Bull Racing' },
{ id: 'd2', name: 'Lewis Hamilton', country: 'GB', position: 2, races: 8, impressions: 3980, team: 'Mercedes AMG' },
{
id: 'd1',
name: 'Max Verstappen',
country: 'NL',
position: 1,
races: 8,
impressions: 4200,
team: 'Red Bull Racing',
},
{
id: 'd2',
name: 'Lewis Hamilton',
country: 'GB',
position: 2,
races: 8,
impressions: 3980,
team: 'Mercedes AMG',
},
];
const races: RaceDTO[] = [
{ id: 'r1', name: 'Spa-Francorchamps', date: '2025-12-20', views: 0, status: 'upcoming' },
{ id: 'r2', name: 'Monza', date: '2025-12-08', views: 5800, status: 'completed' },
{
id: 'r1',
name: 'Spa-Francorchamps',
date: '2025-12-20',
views: 0,
status: 'upcoming',
},
{
id: 'r2',
name: 'Monza',
date: '2025-12-08',
views: 5800,
status: 'completed',
},
];
return { league, drivers, races };
presenter.present({ league, drivers, races });
return presenter;
}
async getSponsorSettings(sponsorId: string): Promise<{
profile: SponsorProfileDTO;
notifications: NotificationSettingsDTO;
privacy: PrivacySettingsDTO;
}> {
async getSponsorSettings(sponsorId: string): Promise<SponsorSettingsPresenter> {
this.logger.debug('[SponsorService] Fetching sponsor settings.', { sponsorId });
const presenter = new SponsorSettingsPresenter();
// Mock data
const profile: SponsorProfileDTO = {
companyName: 'Acme Racing Co.',
@@ -359,7 +499,8 @@ export class SponsorService {
contactEmail: 'sponsor@acme-racing.com',
contactPhone: '+1 (555) 123-4567',
website: 'https://acme-racing.com',
description: 'Premium sim racing equipment and accessories for competitive drivers.',
description:
'Premium sim racing equipment and accessories for competitive drivers.',
logoUrl: null,
industry: 'Racing Equipment',
address: {
@@ -392,7 +533,8 @@ export class SponsorService {
allowDirectContact: true,
};
return { profile, notifications, privacy };
presenter.present({ profile, notifications, privacy });
return presenter;
}
async updateSponsorSettings(
@@ -401,12 +543,15 @@ export class SponsorService {
profile?: Partial<SponsorProfileDTO>;
notifications?: Partial<NotificationSettingsDTO>;
privacy?: Partial<PrivacySettingsDTO>;
}
): Promise<void> {
},
): Promise<SponsorSettingsUpdatePresenter> {
this.logger.debug('[SponsorService] Updating sponsor settings.', { sponsorId, input });
// Mock implementation - in real app, this would persist to database
// For now, just log the update
this.logger.info('[SponsorService] Settings updated successfully.', { sponsorId });
const presenter = new SponsorSettingsUpdatePresenter();
presenter.present({ success: true });
return presenter;
}
}

View File

@@ -0,0 +1,42 @@
import type { AcceptSponsorshipOutputPort } from '@core/racing/application/ports/output/AcceptSponsorshipOutputPort';
export interface AcceptSponsorshipRequestResultViewModel {
requestId: string;
sponsorshipId: string;
status: 'accepted';
acceptedAt: Date;
platformFee: number;
netAmount: number;
}
export class AcceptSponsorshipRequestPresenter {
private result: AcceptSponsorshipRequestResultViewModel | null = null;
reset() {
this.result = null;
}
present(output: AcceptSponsorshipOutputPort | null) {
if (!output) {
this.result = null;
return;
}
this.result = {
requestId: output.requestId,
sponsorshipId: output.sponsorshipId,
status: output.status,
acceptedAt: output.acceptedAt,
platformFee: output.platformFee,
netAmount: output.netAmount,
};
}
getViewModel(): AcceptSponsorshipRequestResultViewModel | null {
return this.result;
}
get viewModel(): AcceptSponsorshipRequestResultViewModel | null {
return this.result;
}
}

View File

@@ -0,0 +1,21 @@
import { AvailableLeagueDTO } from '../dtos/AvailableLeagueDTO';
export class AvailableLeaguesPresenter {
private result: AvailableLeagueDTO[] | null = null;
reset() {
this.result = null;
}
present(leagues: AvailableLeagueDTO[]) {
this.result = leagues;
}
getViewModel(): AvailableLeagueDTO[] | null {
return this.result;
}
get viewModel(): AvailableLeagueDTO[] | null {
return this.result;
}
}

View File

@@ -1,4 +1,4 @@
import type { GetEntitySponsorshipPricingOutputPort } from '@core/racing/application/ports/output/GetEntitySponsorshipPricingOutputPort';
import type { GetSponsorshipPricingOutputPort } from '@core/racing/application/ports/output/GetSponsorshipPricingOutputPort';
import { GetEntitySponsorshipPricingResultDTO } from '../dtos/GetEntitySponsorshipPricingResultDTO';
export class GetEntitySponsorshipPricingPresenter {
@@ -8,34 +8,34 @@ export class GetEntitySponsorshipPricingPresenter {
this.result = null;
}
async present(output: GetEntitySponsorshipPricingOutputPort | null) {
present(output: GetSponsorshipPricingOutputPort | null) {
if (!output) {
this.result = { pricing: [] };
this.result = {
entityType: 'season',
entityId: '',
pricing: [],
};
return;
}
const pricing = [];
if (output.mainSlot) {
pricing.push({
id: `${output.entityType}-${output.entityId}-main`,
level: 'main',
price: output.mainSlot.price,
currency: output.mainSlot.currency,
});
}
if (output.secondarySlot) {
pricing.push({
id: `${output.entityType}-${output.entityId}-secondary`,
level: 'secondary',
price: output.secondarySlot.price,
currency: output.secondarySlot.currency,
});
}
this.result = { pricing };
this.result = {
entityType: output.entityType,
entityId: output.entityId,
pricing: output.pricing.map(item => ({
id: item.id,
level: item.level,
price: item.price,
currency: item.currency,
})),
};
}
getViewModel(): GetEntitySponsorshipPricingResultDTO | null {
return this.result;
}
}
get viewModel(): GetEntitySponsorshipPricingResultDTO {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -2,12 +2,31 @@ import type { PendingSponsorshipRequestsOutputPort } from '@core/racing/applicat
import { GetPendingSponsorshipRequestsOutputDTO } from '../dtos/GetPendingSponsorshipRequestsOutputDTO';
export class GetPendingSponsorshipRequestsPresenter {
present(outputPort: PendingSponsorshipRequestsOutputPort): GetPendingSponsorshipRequestsOutputDTO {
return {
private result: GetPendingSponsorshipRequestsOutputDTO | null = null;
reset() {
this.result = null;
}
present(outputPort: PendingSponsorshipRequestsOutputPort | null) {
if (!outputPort) {
this.result = null;
return;
}
this.result = {
entityType: outputPort.entityType,
entityId: outputPort.entityId,
requests: outputPort.requests,
totalCount: outputPort.totalCount,
};
}
}
getViewModel(): GetPendingSponsorshipRequestsOutputDTO | null {
return this.result;
}
get viewModel(): GetPendingSponsorshipRequestsOutputDTO | null {
return this.result;
}
}

View File

@@ -2,7 +2,21 @@ import type { SponsorDashboardOutputPort } from '@core/racing/application/ports/
import { SponsorDashboardDTO } from '../dtos/SponsorDashboardDTO';
export class GetSponsorDashboardPresenter {
present(outputPort: SponsorDashboardOutputPort | null): SponsorDashboardDTO | null {
return outputPort;
private result: SponsorDashboardDTO | null = null;
reset() {
this.result = null;
}
}
present(outputPort: SponsorDashboardOutputPort | null) {
this.result = outputPort ?? null;
}
getViewModel(): SponsorDashboardDTO | null {
return this.result;
}
get viewModel(): SponsorDashboardDTO | null {
return this.result;
}
}

View File

@@ -0,0 +1,42 @@
import { GetSponsorOutputDTO } from '../dtos/GetSponsorOutputDTO';
interface GetSponsorOutputPort {
sponsor: {
id: string;
name: string;
logoUrl?: string;
websiteUrl?: string;
};
}
export class GetSponsorPresenter {
private result: GetSponsorOutputDTO | null = null;
reset() {
this.result = null;
}
present(output: GetSponsorOutputPort | null) {
if (!output) {
this.result = null;
return;
}
this.result = {
sponsor: {
id: output.sponsor.id,
name: output.sponsor.name,
...(output.sponsor.logoUrl !== undefined ? { logoUrl: output.sponsor.logoUrl } : {}),
...(output.sponsor.websiteUrl !== undefined ? { websiteUrl: output.sponsor.websiteUrl } : {}),
},
} as GetSponsorOutputDTO;
}
getViewModel(): GetSponsorOutputDTO | null {
return this.result;
}
get viewModel(): GetSponsorOutputDTO | null {
return this.result;
}
}

View File

@@ -2,7 +2,21 @@ import type { SponsorSponsorshipsOutputPort } from '@core/racing/application/por
import { SponsorSponsorshipsDTO } from '../dtos/SponsorSponsorshipsDTO';
export class GetSponsorSponsorshipsPresenter {
present(outputPort: SponsorSponsorshipsOutputPort | null): SponsorSponsorshipsDTO | null {
return outputPort;
private result: SponsorSponsorshipsDTO | null = null;
reset() {
this.result = null;
}
}
present(outputPort: SponsorSponsorshipsOutputPort | null) {
this.result = outputPort ?? null;
}
getViewModel(): SponsorSponsorshipsDTO | null {
return this.result;
}
get viewModel(): SponsorSponsorshipsDTO | null {
return this.result;
}
}

View File

@@ -2,9 +2,24 @@ import type { GetSponsorsOutputPort } from '@core/racing/application/ports/outpu
import { GetSponsorsOutputDTO } from '../dtos/GetSponsorsOutputDTO';
export class GetSponsorsPresenter {
present(outputPort: GetSponsorsOutputPort): GetSponsorsOutputDTO {
return {
private result: GetSponsorsOutputDTO | null = null;
reset() {
this.result = null;
}
present(outputPort: GetSponsorsOutputPort) {
this.result = {
sponsors: outputPort.sponsors,
};
}
}
getViewModel(): GetSponsorsOutputDTO | null {
return this.result;
}
get viewModel(): GetSponsorsOutputDTO {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,29 @@
import { LeagueDetailDTO } from '../dtos/LeagueDetailDTO';
import { DriverDTO } from '../dtos/DriverDTO';
import { RaceDTO } from '../dtos/RaceDTO';
export interface LeagueDetailViewModel {
league: LeagueDetailDTO;
drivers: DriverDTO[];
races: RaceDTO[];
}
export class LeagueDetailPresenter {
private result: LeagueDetailViewModel | null = null;
reset() {
this.result = null;
}
present(viewModel: LeagueDetailViewModel) {
this.result = viewModel;
}
getViewModel(): LeagueDetailViewModel | null {
return this.result;
}
get viewModel(): LeagueDetailViewModel | null {
return this.result;
}
}

View File

@@ -0,0 +1,21 @@
import type { RejectSponsorshipRequestResultDTO } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';
export class RejectSponsorshipRequestPresenter {
private result: RejectSponsorshipRequestResultDTO | null = null;
reset() {
this.result = null;
}
present(output: RejectSponsorshipRequestResultDTO | null) {
this.result = output ?? null;
}
getViewModel(): RejectSponsorshipRequestResultDTO | null {
return this.result;
}
get viewModel(): RejectSponsorshipRequestResultDTO | null {
return this.result;
}
}

View File

@@ -0,0 +1,30 @@
import { PaymentMethodDTO } from '../dtos/PaymentMethodDTO';
import { InvoiceDTO } from '../dtos/InvoiceDTO';
import { BillingStatsDTO } from '../dtos/BillingStatsDTO';
export interface SponsorBillingViewModel {
paymentMethods: PaymentMethodDTO[];
invoices: InvoiceDTO[];
stats: BillingStatsDTO;
}
export class SponsorBillingPresenter {
private result: SponsorBillingViewModel | null = null;
reset() {
this.result = null;
}
present(viewModel: SponsorBillingViewModel) {
this.result = viewModel;
}
getViewModel(): SponsorBillingViewModel | null {
return this.result;
}
get viewModel(): SponsorBillingViewModel {
if (!this.result) throw new Error('Presenter not presented');
return this.result;
}
}

View File

@@ -0,0 +1,29 @@
import { SponsorProfileDTO } from '../dtos/SponsorProfileDTO';
import { NotificationSettingsDTO } from '../dtos/NotificationSettingsDTO';
import { PrivacySettingsDTO } from '../dtos/PrivacySettingsDTO';
export interface SponsorSettingsViewModel {
profile: SponsorProfileDTO;
notifications: NotificationSettingsDTO;
privacy: PrivacySettingsDTO;
}
export class SponsorSettingsPresenter {
private result: SponsorSettingsViewModel | null = null;
reset() {
this.result = null;
}
present(viewModel: SponsorSettingsViewModel) {
this.result = viewModel;
}
getViewModel(): SponsorSettingsViewModel | null {
return this.result;
}
get viewModel(): SponsorSettingsViewModel | null {
return this.result;
}
}

View File

@@ -0,0 +1,25 @@
export interface SponsorSettingsUpdateViewModel {
success: boolean;
errorCode?: string;
message?: string;
}
export class SponsorSettingsUpdatePresenter {
private result: SponsorSettingsUpdateViewModel | null = null;
reset() {
this.result = null;
}
present(viewModel: SponsorSettingsUpdateViewModel) {
this.result = viewModel;
}
getViewModel(): SponsorSettingsUpdateViewModel | null {
return this.result;
}
get viewModel(): SponsorSettingsUpdateViewModel | null {
return this.result;
}
}