code quality
Some checks failed
CI / lint-typecheck (pull_request) Failing after 12s
CI / tests (pull_request) Has been skipped
CI / contract-tests (pull_request) Has been skipped
CI / e2e-tests (pull_request) Has been skipped
CI / comment-pr (pull_request) Has been skipped
CI / commit-types (pull_request) Has been skipped
Some checks failed
CI / lint-typecheck (pull_request) Failing after 12s
CI / tests (pull_request) Has been skipped
CI / contract-tests (pull_request) Has been skipped
CI / e2e-tests (pull_request) Has been skipped
CI / comment-pr (pull_request) Has been skipped
CI / commit-types (pull_request) Has been skipped
This commit is contained in:
@@ -61,9 +61,7 @@ export class MediaResolverAdapter implements MediaResolverPort {
|
|||||||
basePath: config.defaultPath
|
basePath: config.defaultPath
|
||||||
});
|
});
|
||||||
|
|
||||||
this.generatedResolver = new GeneratedMediaResolverAdapter({
|
this.generatedResolver = new GeneratedMediaResolverAdapter();
|
||||||
basePath: config.generatedPath
|
|
||||||
});
|
|
||||||
|
|
||||||
this.uploadedResolver = new UploadedMediaResolverAdapter({
|
this.uploadedResolver = new UploadedMediaResolverAdapter({
|
||||||
basePath: config.uploadedPath
|
basePath: config.uploadedPath
|
||||||
|
|||||||
@@ -4,36 +4,33 @@
|
|||||||
|
|
||||||
import type { WalletRepository, TransactionRepository } from '@core/payments/domain/repositories/WalletRepository';
|
import type { WalletRepository, TransactionRepository } from '@core/payments/domain/repositories/WalletRepository';
|
||||||
import type { Logger } from '@core/shared/domain/Logger';
|
import type { Logger } from '@core/shared/domain/Logger';
|
||||||
import type { LeagueWalletRepository } from '@core/racing/domain/repositories/LeagueWalletRepository';
|
import type { Wallet, Transaction } from '@core/payments/domain/entities/Wallet';
|
||||||
import type { Wallet } from '@core/payments/domain/entities/Wallet';
|
|
||||||
import type { LeagueWallet } from '@core/racing/domain/entities/league-wallet/LeagueWallet';
|
|
||||||
import type { Transaction } from '@core/payments/domain/entities/league-wallet/Transaction';
|
|
||||||
|
|
||||||
const wallets: Map<string, Wallet | LeagueWallet> = new Map();
|
const wallets: Map<string, Wallet> = new Map();
|
||||||
const transactions: Map<string, Transaction> = new Map();
|
const transactions: Map<string, Transaction> = new Map();
|
||||||
|
|
||||||
export class InMemoryWalletRepository implements WalletRepository, LeagueWalletRepository {
|
export class InMemoryWalletRepository implements WalletRepository {
|
||||||
constructor(private readonly logger: Logger) {}
|
constructor(private readonly logger: Logger) {}
|
||||||
|
|
||||||
async findById(id: string): Promise<Wallet | LeagueWallet | null> {
|
async findById(id: string): Promise<Wallet | null> {
|
||||||
this.logger.debug('[InMemoryWalletRepository] findById', { id });
|
this.logger.debug('[InMemoryWalletRepository] findById', { id });
|
||||||
return wallets.get(id) || null;
|
return wallets.get(id) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findByLeagueId(leagueId: string): Promise<LeagueWallet | null> {
|
async findByLeagueId(leagueId: string): Promise<Wallet | null> {
|
||||||
this.logger.debug('[InMemoryWalletRepository] findByLeagueId', { leagueId });
|
this.logger.debug('[InMemoryWalletRepository] findByLeagueId', { leagueId });
|
||||||
return (Array.from(wallets.values()).find(w => (w as LeagueWallet).leagueId.toString() === leagueId) as LeagueWallet) || null;
|
return Array.from(wallets.values()).find(w => w.leagueId === leagueId) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(wallet: Wallet | LeagueWallet): Promise<Wallet | LeagueWallet> {
|
async create(wallet: Wallet): Promise<Wallet> {
|
||||||
this.logger.debug('[InMemoryWalletRepository] create', { wallet });
|
this.logger.debug('[InMemoryWalletRepository] create', { wallet });
|
||||||
wallets.set(wallet.id.toString(), wallet);
|
wallets.set(wallet.id, wallet);
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(wallet: Wallet | LeagueWallet): Promise<Wallet | LeagueWallet> {
|
async update(wallet: Wallet): Promise<Wallet> {
|
||||||
this.logger.debug('[InMemoryWalletRepository] update', { wallet });
|
this.logger.debug('[InMemoryWalletRepository] update', { wallet });
|
||||||
wallets.set(wallet.id.toString(), wallet);
|
wallets.set(wallet.id, wallet);
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,24 +50,24 @@ export class InMemoryWalletRepository implements WalletRepository, LeagueWalletR
|
|||||||
export class InMemoryTransactionRepository implements TransactionRepository {
|
export class InMemoryTransactionRepository implements TransactionRepository {
|
||||||
constructor(private readonly logger: Logger) {}
|
constructor(private readonly logger: Logger) {}
|
||||||
|
|
||||||
async findById(id: string): Promise<any | null> {
|
async findById(id: string): Promise<Transaction | null> {
|
||||||
this.logger.debug('[InMemoryTransactionRepository] findById', { id });
|
this.logger.debug('[InMemoryTransactionRepository] findById', { id });
|
||||||
return transactions.get(id) || null;
|
return transactions.get(id) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findByWalletId(walletId: string): Promise<any[]> {
|
async findByWalletId(walletId: string): Promise<Transaction[]> {
|
||||||
this.logger.debug('[InMemoryTransactionRepository] findByWalletId', { walletId });
|
this.logger.debug('[InMemoryTransactionRepository] findByWalletId', { walletId });
|
||||||
return Array.from(transactions.values()).filter(t => t.walletId.toString() === walletId);
|
return Array.from(transactions.values()).filter(t => t.walletId === walletId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(transaction: any): Promise<any> {
|
async create(transaction: Transaction): Promise<Transaction> {
|
||||||
this.logger.debug('[InMemoryTransactionRepository] create', { transaction });
|
this.logger.debug('[InMemoryTransactionRepository] create', { transaction });
|
||||||
transactions.set(transaction.id.toString(), transaction);
|
transactions.set(transaction.id, transaction);
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(transaction: any): Promise<any> {
|
async update(transaction: Transaction): Promise<Transaction> {
|
||||||
transactions.set(transaction.id.toString(), transaction);
|
transactions.set(transaction.id, transaction);
|
||||||
return transaction;
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +79,7 @@ export class InMemoryTransactionRepository implements TransactionRepository {
|
|||||||
return transactions.has(id);
|
return transactions.has(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
findByType(type: any): Promise<any[]> {
|
findByType(type: any): Promise<Transaction[]> {
|
||||||
return Promise.resolve(Array.from(transactions.values()).filter(t => t.type === type));
|
return Promise.resolve(Array.from(transactions.values()).filter(t => t.type === type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ describe('ResultOrmMapper', () => {
|
|||||||
entity.fastestLap = 0;
|
entity.fastestLap = 0;
|
||||||
entity.incidents = 0;
|
entity.incidents = 0;
|
||||||
entity.startPosition = 1;
|
entity.startPosition = 1;
|
||||||
|
entity.points = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mapper.toDomain(entity);
|
mapper.toDomain(entity);
|
||||||
|
|||||||
@@ -2216,6 +2216,96 @@
|
|||||||
"incidents"
|
"incidents"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"DashboardStatsResponseDTO": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"totalUsers": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"activeUsers": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"suspendedUsers": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"deletedUsers": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"systemAdmins": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"recentLogins": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"newUsersToday": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"userGrowth": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"roleDistribution": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"statusDistribution": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"suspended": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"deleted": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"activityTimeline": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"newUsers": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"logins": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"totalUsers",
|
||||||
|
"activeUsers",
|
||||||
|
"suspendedUsers",
|
||||||
|
"deletedUsers",
|
||||||
|
"systemAdmins",
|
||||||
|
"recentLogins",
|
||||||
|
"newUsersToday",
|
||||||
|
"userGrowth",
|
||||||
|
"label",
|
||||||
|
"value",
|
||||||
|
"color",
|
||||||
|
"roleDistribution",
|
||||||
|
"label",
|
||||||
|
"value",
|
||||||
|
"color",
|
||||||
|
"statusDistribution",
|
||||||
|
"active",
|
||||||
|
"suspended",
|
||||||
|
"deleted",
|
||||||
|
"activityTimeline",
|
||||||
|
"date",
|
||||||
|
"newUsers",
|
||||||
|
"logins"
|
||||||
|
]
|
||||||
|
},
|
||||||
"DeleteMediaOutputDTO": {
|
"DeleteMediaOutputDTO": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it, vi } from 'vitest';
|
import { describe, expect, it, vi } from 'vitest';
|
||||||
import { AdminController } from './AdminController';
|
import { AdminController } from './AdminController';
|
||||||
import { DashboardStatsResponseDto } from './dto/DashboardStatsResponseDto';
|
import { DashboardStatsResponseDto } from './dtos/DashboardStatsResponseDto';
|
||||||
import { ListUsersRequestDto } from './dtos/ListUsersRequestDto';
|
import { ListUsersRequestDto } from './dtos/ListUsersRequestDto';
|
||||||
import { UserListResponseDto, UserResponseDto } from './dtos/UserResponseDto';
|
import { UserListResponseDto, UserResponseDto } from './dtos/UserResponseDto';
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { RequireSystemAdmin, REQUIRE_SYSTEM_ADMIN_METADATA_KEY } from './Require
|
|||||||
|
|
||||||
// Mock SetMetadata
|
// Mock SetMetadata
|
||||||
vi.mock('@nestjs/common', () => ({
|
vi.mock('@nestjs/common', () => ({
|
||||||
SetMetadata: vi.fn(() => (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) => descriptor),
|
SetMetadata: vi.fn(() => (_target: unknown, _propertyKey: string, descriptor: PropertyDescriptor) => descriptor),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('RequireSystemAdmin', () => {
|
describe('RequireSystemAdmin', () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { beforeEach, describe, expect, it } from 'vitest';
|
||||||
import { AuthorizationService } from './AuthorizationService';
|
import { AuthorizationService } from './AuthorizationService';
|
||||||
|
|
||||||
describe('AuthorizationService', () => {
|
describe('AuthorizationService', () => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Public, PUBLIC_ROUTE_METADATA_KEY } from './Public';
|
|||||||
|
|
||||||
// Mock SetMetadata
|
// Mock SetMetadata
|
||||||
vi.mock('@nestjs/common', () => ({
|
vi.mock('@nestjs/common', () => ({
|
||||||
SetMetadata: vi.fn(() => (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) => descriptor),
|
SetMetadata: vi.fn(() => (_target: unknown, _propertyKey: string, descriptor: PropertyDescriptor) => descriptor),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('Public', () => {
|
describe('Public', () => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { RequireAuthenticatedUser, REQUIRE_AUTHENTICATED_USER_METADATA_KEY } fro
|
|||||||
|
|
||||||
// Mock SetMetadata
|
// Mock SetMetadata
|
||||||
vi.mock('@nestjs/common', () => ({
|
vi.mock('@nestjs/common', () => ({
|
||||||
SetMetadata: vi.fn(() => (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) => descriptor),
|
SetMetadata: vi.fn(() => (_target: unknown, _propertyKey: string, descriptor: PropertyDescriptor) => descriptor),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('RequireAuthenticatedUser', () => {
|
describe('RequireAuthenticatedUser', () => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { RequireRoles, REQUIRE_ROLES_METADATA_KEY } from './RequireRoles';
|
|||||||
|
|
||||||
// Mock SetMetadata
|
// Mock SetMetadata
|
||||||
vi.mock('@nestjs/common', () => ({
|
vi.mock('@nestjs/common', () => ({
|
||||||
SetMetadata: vi.fn(() => (target: unknown, propertyKey: string, descriptor: PropertyDescriptor) => descriptor),
|
SetMetadata: vi.fn(() => (_target: unknown, _propertyKey: string, descriptor: PropertyDescriptor) => descriptor),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('RequireRoles', () => {
|
describe('RequireRoles', () => {
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ const createOutput = (): DashboardOverviewResult => {
|
|||||||
fastestLap: 120,
|
fastestLap: 120,
|
||||||
incidents: 0,
|
incidents: 0,
|
||||||
startPosition: 1,
|
startPosition: 1,
|
||||||
|
points: 25,
|
||||||
});
|
});
|
||||||
|
|
||||||
const feedItem: FeedItem = {
|
const feedItem: FeedItem = {
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ describe('requireLeagueAdminOrOwner', () => {
|
|||||||
try {
|
try {
|
||||||
await requireLeagueAdminOrOwner('league-123', mockGetLeagueAdminPermissionsUseCase);
|
await requireLeagueAdminOrOwner('league-123', mockGetLeagueAdminPermissionsUseCase);
|
||||||
expect(true).toBe(false); // Should not reach here
|
expect(true).toBe(false); // Should not reach here
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
expect(error).toBeInstanceOf(ForbiddenException);
|
expect(error).toBeInstanceOf(ForbiddenException);
|
||||||
expect(error.message).toBe('Forbidden');
|
expect(error.message).toBe('Forbidden');
|
||||||
}
|
}
|
||||||
@@ -192,7 +192,7 @@ describe('requireLeagueAdminOrOwner', () => {
|
|||||||
mockGetActorFromRequestContext.mockReturnValue({
|
mockGetActorFromRequestContext.mockReturnValue({
|
||||||
userId: 'user-123',
|
userId: 'user-123',
|
||||||
driverId: 'driver-123',
|
driverId: 'driver-123',
|
||||||
role: null,
|
role: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mockResult = {
|
const mockResult = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, it, vi } from 'vitest';
|
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
||||||
import { LeagueController } from './LeagueController';
|
import { LeagueController } from './LeagueController';
|
||||||
import { LeagueService } from './LeagueService';
|
import { LeagueService } from './LeagueService';
|
||||||
|
|
||||||
@@ -25,9 +25,9 @@ describe('LeagueController - Discovery Endpoints', () => {
|
|||||||
name: 'GT3 Masters',
|
name: 'GT3 Masters',
|
||||||
description: 'A GT3 racing league',
|
description: 'A GT3 racing league',
|
||||||
ownerId: 'owner-1',
|
ownerId: 'owner-1',
|
||||||
maxDrivers: 32,
|
settings: { maxDrivers: 32 },
|
||||||
currentDrivers: 25,
|
usedSlots: 25,
|
||||||
isPublic: true,
|
createdAt: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
totalCount: 1,
|
totalCount: 1,
|
||||||
@@ -59,18 +59,18 @@ describe('LeagueController - Discovery Endpoints', () => {
|
|||||||
name: 'Small League',
|
name: 'Small League',
|
||||||
description: 'Small league',
|
description: 'Small league',
|
||||||
ownerId: 'owner-1',
|
ownerId: 'owner-1',
|
||||||
maxDrivers: 10,
|
settings: { maxDrivers: 10 },
|
||||||
currentDrivers: 8,
|
usedSlots: 8,
|
||||||
isPublic: true,
|
createdAt: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'league-2',
|
id: 'league-2',
|
||||||
name: 'Large League',
|
name: 'Large League',
|
||||||
description: 'Large league',
|
description: 'Large league',
|
||||||
ownerId: 'owner-2',
|
ownerId: 'owner-2',
|
||||||
maxDrivers: 50,
|
settings: { maxDrivers: 50 },
|
||||||
currentDrivers: 45,
|
usedSlots: 45,
|
||||||
isPublic: true,
|
createdAt: new Date().toISOString(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
totalCount: 2,
|
totalCount: 2,
|
||||||
@@ -81,8 +81,8 @@ describe('LeagueController - Discovery Endpoints', () => {
|
|||||||
|
|
||||||
expect(result).toEqual(mockResult);
|
expect(result).toEqual(mockResult);
|
||||||
expect(result.leagues).toHaveLength(2);
|
expect(result.leagues).toHaveLength(2);
|
||||||
expect(result.leagues[0]?.maxDrivers).toBe(10);
|
expect(result.leagues[0]?.settings.maxDrivers).toBe(10);
|
||||||
expect(result.leagues[1]?.maxDrivers).toBe(50);
|
expect(result.leagues[1]?.settings.maxDrivers).toBe(50);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -95,13 +95,17 @@ describe('LeagueController - Discovery Endpoints', () => {
|
|||||||
name: 'GT3 Masters',
|
name: 'GT3 Masters',
|
||||||
description: 'A GT3 racing league',
|
description: 'A GT3 racing league',
|
||||||
ownerId: 'owner-1',
|
ownerId: 'owner-1',
|
||||||
maxDrivers: 32,
|
settings: { maxDrivers: 32 },
|
||||||
currentDrivers: 25,
|
usedSlots: 25,
|
||||||
isPublic: true,
|
createdAt: new Date().toISOString(),
|
||||||
scoringConfig: {
|
scoring: {
|
||||||
pointsSystem: 'standard',
|
gameId: 'iracing',
|
||||||
pointsPerRace: 25,
|
gameName: 'iRacing',
|
||||||
bonusPoints: true,
|
primaryChampionshipType: 'driver',
|
||||||
|
scoringPresetId: 'standard',
|
||||||
|
scoringPresetName: 'Standard',
|
||||||
|
dropPolicySummary: 'None',
|
||||||
|
scoringPatternSummary: '25-18-15...',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -134,13 +138,17 @@ describe('LeagueController - Discovery Endpoints', () => {
|
|||||||
name: 'Standard League',
|
name: 'Standard League',
|
||||||
description: 'Standard scoring',
|
description: 'Standard scoring',
|
||||||
ownerId: 'owner-1',
|
ownerId: 'owner-1',
|
||||||
maxDrivers: 32,
|
settings: { maxDrivers: 32 },
|
||||||
currentDrivers: 20,
|
usedSlots: 20,
|
||||||
isPublic: true,
|
createdAt: new Date().toISOString(),
|
||||||
scoringConfig: {
|
scoring: {
|
||||||
pointsSystem: 'standard',
|
gameId: 'iracing',
|
||||||
pointsPerRace: 25,
|
gameName: 'iRacing',
|
||||||
bonusPoints: true,
|
primaryChampionshipType: 'driver',
|
||||||
|
scoringPresetId: 'standard',
|
||||||
|
scoringPresetName: 'Standard',
|
||||||
|
dropPolicySummary: 'None',
|
||||||
|
scoringPatternSummary: '25-18-15...',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -148,13 +156,17 @@ describe('LeagueController - Discovery Endpoints', () => {
|
|||||||
name: 'Custom League',
|
name: 'Custom League',
|
||||||
description: 'Custom scoring',
|
description: 'Custom scoring',
|
||||||
ownerId: 'owner-2',
|
ownerId: 'owner-2',
|
||||||
maxDrivers: 20,
|
settings: { maxDrivers: 20 },
|
||||||
currentDrivers: 15,
|
usedSlots: 15,
|
||||||
isPublic: true,
|
createdAt: new Date().toISOString(),
|
||||||
scoringConfig: {
|
scoring: {
|
||||||
pointsSystem: 'custom',
|
gameId: 'iracing',
|
||||||
pointsPerRace: 50,
|
gameName: 'iRacing',
|
||||||
bonusPoints: false,
|
primaryChampionshipType: 'driver',
|
||||||
|
scoringPresetId: 'custom',
|
||||||
|
scoringPresetName: 'Custom',
|
||||||
|
dropPolicySummary: 'None',
|
||||||
|
scoringPatternSummary: '50-40-30...',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -166,8 +178,8 @@ describe('LeagueController - Discovery Endpoints', () => {
|
|||||||
|
|
||||||
expect(result).toEqual(mockResult);
|
expect(result).toEqual(mockResult);
|
||||||
expect(result.leagues).toHaveLength(2);
|
expect(result.leagues).toHaveLength(2);
|
||||||
expect(result.leagues[0]?.scoringConfig.pointsSystem).toBe('standard');
|
expect(result.leagues[0]?.scoring?.scoringPresetId).toBe('standard');
|
||||||
expect(result.leagues[1]?.scoringConfig.pointsSystem).toBe('custom');
|
expect(result.leagues[1]?.scoring?.scoringPresetId).toBe('custom');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,7 @@
|
|||||||
import { requestContextMiddleware } from '@adapters/http/RequestContext';
|
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import { describe, expect, it, vi } from 'vitest';
|
import { describe, expect, it, vi } from 'vitest';
|
||||||
import { LeagueService } from './LeagueService';
|
import { LeagueService } from './LeagueService';
|
||||||
|
|
||||||
async function withUserId<T>(userId: string, fn: () => Promise<T>): Promise<T> {
|
|
||||||
const req = { user: { userId } };
|
|
||||||
const res = {};
|
|
||||||
|
|
||||||
return await new Promise<T>((resolve, reject) => {
|
|
||||||
requestContextMiddleware(req as never, res as never, () => {
|
|
||||||
fn().then(resolve, reject);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('LeagueService - All Endpoints', () => {
|
describe('LeagueService - All Endpoints', () => {
|
||||||
it('covers all league endpoint happy paths and error branches', async () => {
|
it('covers all league endpoint happy paths and error branches', async () => {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { NotificationsController } from './NotificationsController';
|
import { NotificationsController } from './NotificationsController';
|
||||||
import { NotificationsService } from './NotificationsService';
|
import { NotificationsService } from './NotificationsService';
|
||||||
import { vi } from 'vitest';
|
import { vi, describe, beforeEach, it, expect } from 'vitest';
|
||||||
import type { Request, Response } from 'express';
|
import type { Response } from 'express';
|
||||||
|
|
||||||
describe('NotificationsController', () => {
|
describe('NotificationsController', () => {
|
||||||
let controller: NotificationsController;
|
let controller: NotificationsController;
|
||||||
@@ -38,7 +38,7 @@ describe('NotificationsController', () => {
|
|||||||
|
|
||||||
const mockReq = {
|
const mockReq = {
|
||||||
user: { userId: 'user-123' },
|
user: { userId: 'user-123' },
|
||||||
} as unknown as Request;
|
} as any;
|
||||||
|
|
||||||
const mockRes = {
|
const mockRes = {
|
||||||
status: vi.fn().mockReturnThis(),
|
status: vi.fn().mockReturnThis(),
|
||||||
@@ -53,7 +53,7 @@ describe('NotificationsController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return 401 when user is not authenticated', async () => {
|
it('should return 401 when user is not authenticated', async () => {
|
||||||
const mockReq = {} as unknown as Request;
|
const mockReq = {} as any;
|
||||||
const mockRes = {
|
const mockRes = {
|
||||||
status: vi.fn().mockReturnThis(),
|
status: vi.fn().mockReturnThis(),
|
||||||
json: vi.fn(),
|
json: vi.fn(),
|
||||||
@@ -69,7 +69,7 @@ describe('NotificationsController', () => {
|
|||||||
it('should return 401 when userId is missing', async () => {
|
it('should return 401 when userId is missing', async () => {
|
||||||
const mockReq = {
|
const mockReq = {
|
||||||
user: {},
|
user: {},
|
||||||
} as unknown as Request;
|
} as any;
|
||||||
|
|
||||||
const mockRes = {
|
const mockRes = {
|
||||||
status: vi.fn().mockReturnThis(),
|
status: vi.fn().mockReturnThis(),
|
||||||
@@ -90,7 +90,7 @@ describe('NotificationsController', () => {
|
|||||||
|
|
||||||
const mockReq = {
|
const mockReq = {
|
||||||
user: { userId: 'user-123' },
|
user: { userId: 'user-123' },
|
||||||
} as unknown as Request;
|
} as any;
|
||||||
|
|
||||||
const mockRes = {
|
const mockRes = {
|
||||||
status: vi.fn().mockReturnThis(),
|
status: vi.fn().mockReturnThis(),
|
||||||
@@ -105,7 +105,7 @@ describe('NotificationsController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return 401 when user is not authenticated', async () => {
|
it('should return 401 when user is not authenticated', async () => {
|
||||||
const mockReq = {} as unknown as Request;
|
const mockReq = {} as any;
|
||||||
const mockRes = {
|
const mockRes = {
|
||||||
status: vi.fn().mockReturnThis(),
|
status: vi.fn().mockReturnThis(),
|
||||||
json: vi.fn(),
|
json: vi.fn(),
|
||||||
@@ -121,7 +121,7 @@ describe('NotificationsController', () => {
|
|||||||
it('should return 401 when userId is missing', async () => {
|
it('should return 401 when userId is missing', async () => {
|
||||||
const mockReq = {
|
const mockReq = {
|
||||||
user: {},
|
user: {},
|
||||||
} as unknown as Request;
|
} as any;
|
||||||
|
|
||||||
const mockRes = {
|
const mockRes = {
|
||||||
status: vi.fn().mockReturnThis(),
|
status: vi.fn().mockReturnThis(),
|
||||||
@@ -148,7 +148,7 @@ describe('NotificationsController', () => {
|
|||||||
|
|
||||||
const mockReq = {
|
const mockReq = {
|
||||||
user: { userId: 'user-123' },
|
user: { userId: 'user-123' },
|
||||||
} as unknown as Request;
|
} as any;
|
||||||
|
|
||||||
const mockRes = {
|
const mockRes = {
|
||||||
status: vi.fn().mockReturnThis(),
|
status: vi.fn().mockReturnThis(),
|
||||||
@@ -163,7 +163,7 @@ describe('NotificationsController', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return 401 when user is not authenticated', async () => {
|
it('should return 401 when user is not authenticated', async () => {
|
||||||
const mockReq = {} as unknown as Request;
|
const mockReq = {} as any;
|
||||||
const mockRes = {
|
const mockRes = {
|
||||||
status: vi.fn().mockReturnThis(),
|
status: vi.fn().mockReturnThis(),
|
||||||
json: vi.fn(),
|
json: vi.fn(),
|
||||||
@@ -179,7 +179,7 @@ describe('NotificationsController', () => {
|
|||||||
it('should return 401 when userId is missing', async () => {
|
it('should return 401 when userId is missing', async () => {
|
||||||
const mockReq = {
|
const mockReq = {
|
||||||
user: {},
|
user: {},
|
||||||
} as unknown as Request;
|
} as any;
|
||||||
|
|
||||||
const mockRes = {
|
const mockRes = {
|
||||||
status: vi.fn().mockReturnThis(),
|
status: vi.fn().mockReturnThis(),
|
||||||
@@ -198,7 +198,7 @@ describe('NotificationsController', () => {
|
|||||||
|
|
||||||
const mockReq = {
|
const mockReq = {
|
||||||
user: { userId: 'user-123' },
|
user: { userId: 'user-123' },
|
||||||
} as unknown as Request;
|
} as any;
|
||||||
|
|
||||||
const mockRes = {
|
const mockRes = {
|
||||||
status: vi.fn().mockReturnThis(),
|
status: vi.fn().mockReturnThis(),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
import { describe, expect, it, beforeEach } from 'vitest';
|
||||||
import { CreatePaymentPresenter } from './CreatePaymentPresenter';
|
import { CreatePaymentPresenter } from './CreatePaymentPresenter';
|
||||||
import { CreatePaymentOutput } from '../dtos/PaymentsDto';
|
import { PaymentType, PayerType, PaymentStatus } from '@core/payments/domain/entities/Payment';
|
||||||
|
|
||||||
describe('CreatePaymentPresenter', () => {
|
describe('CreatePaymentPresenter', () => {
|
||||||
let presenter: CreatePaymentPresenter;
|
let presenter: CreatePaymentPresenter;
|
||||||
@@ -13,14 +14,14 @@ describe('CreatePaymentPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -31,14 +32,14 @@ describe('CreatePaymentPresenter', () => {
|
|||||||
expect(responseModel).toEqual({
|
expect(responseModel).toEqual({
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -48,15 +49,15 @@ describe('CreatePaymentPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
seasonId: 'season-123',
|
seasonId: 'season-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -71,14 +72,14 @@ describe('CreatePaymentPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'completed',
|
status: PaymentStatus.COMPLETED,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
completedAt: new Date('2024-01-02'),
|
completedAt: new Date('2024-01-02'),
|
||||||
},
|
},
|
||||||
@@ -94,14 +95,14 @@ describe('CreatePaymentPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -116,14 +117,14 @@ describe('CreatePaymentPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -144,14 +145,14 @@ describe('CreatePaymentPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -169,14 +170,14 @@ describe('CreatePaymentPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -191,14 +192,14 @@ describe('CreatePaymentPresenter', () => {
|
|||||||
const firstResult = {
|
const firstResult = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -206,14 +207,14 @@ describe('CreatePaymentPresenter', () => {
|
|||||||
const secondResult = {
|
const secondResult = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-456',
|
id: 'payment-456',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 200,
|
amount: 200,
|
||||||
platformFee: 10,
|
platformFee: 10,
|
||||||
netAmount: 190,
|
netAmount: 190,
|
||||||
payerId: 'user-456',
|
payerId: 'user-456',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-456',
|
leagueId: 'league-456',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-02'),
|
createdAt: new Date('2024-01-02'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { GetMembershipFeesPresenter } from './GetMembershipFeesPresenter';
|
import { GetMembershipFeesPresenter } from './GetMembershipFeesPresenter';
|
||||||
import { GetMembershipFeesResultDTO } from '../dtos/GetMembershipFeesDTO';
|
import { GetMembershipFeesResultDTO } from '../dtos/GetMembershipFeesDTO';
|
||||||
import { MembershipFeeType, MemberPaymentStatus } from '../dtos/PaymentsDto';
|
import { MembershipFeeType } from '../dtos/PaymentsDto';
|
||||||
|
|
||||||
describe('GetMembershipFeesPresenter', () => {
|
describe('GetMembershipFeesPresenter', () => {
|
||||||
let presenter: GetMembershipFeesPresenter;
|
let presenter: GetMembershipFeesPresenter;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
import { describe, expect, it, beforeEach } from 'vitest';
|
||||||
import { GetPaymentsPresenter } from './GetPaymentsPresenter';
|
import { GetPaymentsPresenter } from './GetPaymentsPresenter';
|
||||||
import { GetPaymentsOutput } from '../dtos/PaymentsDto';
|
import { PaymentType, PayerType, PaymentStatus } from '@core/payments/domain/entities/Payment';
|
||||||
|
|
||||||
describe('GetPaymentsPresenter', () => {
|
describe('GetPaymentsPresenter', () => {
|
||||||
let presenter: GetPaymentsPresenter;
|
let presenter: GetPaymentsPresenter;
|
||||||
@@ -14,14 +15,14 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
payments: [
|
payments: [
|
||||||
{
|
{
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -34,14 +35,14 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
payments: [
|
payments: [
|
||||||
{
|
{
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -53,15 +54,15 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
payments: [
|
payments: [
|
||||||
{
|
{
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
seasonId: 'season-123',
|
seasonId: 'season-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -70,7 +71,7 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
presenter.present(result);
|
presenter.present(result);
|
||||||
|
|
||||||
const responseModel = presenter.getResponseModel();
|
const responseModel = presenter.getResponseModel();
|
||||||
expect(responseModel.payments[0].seasonId).toBe('season-123');
|
expect(responseModel.payments[0]!.seasonId).toBe('season-123');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should include completedAt when provided', () => {
|
it('should include completedAt when provided', () => {
|
||||||
@@ -78,14 +79,14 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
payments: [
|
payments: [
|
||||||
{
|
{
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'completed',
|
status: PaymentStatus.COMPLETED,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
completedAt: new Date('2024-01-02'),
|
completedAt: new Date('2024-01-02'),
|
||||||
},
|
},
|
||||||
@@ -95,7 +96,7 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
presenter.present(result);
|
presenter.present(result);
|
||||||
|
|
||||||
const responseModel = presenter.getResponseModel();
|
const responseModel = presenter.getResponseModel();
|
||||||
expect(responseModel.payments[0].completedAt).toEqual(new Date('2024-01-02'));
|
expect(responseModel.payments[0]!.completedAt).toEqual(new Date('2024-01-02'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not include seasonId when not provided', () => {
|
it('should not include seasonId when not provided', () => {
|
||||||
@@ -103,14 +104,14 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
payments: [
|
payments: [
|
||||||
{
|
{
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -119,7 +120,7 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
presenter.present(result);
|
presenter.present(result);
|
||||||
|
|
||||||
const responseModel = presenter.getResponseModel();
|
const responseModel = presenter.getResponseModel();
|
||||||
expect(responseModel.payments[0].seasonId).toBeUndefined();
|
expect(responseModel.payments[0]!.seasonId).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not include completedAt when not provided', () => {
|
it('should not include completedAt when not provided', () => {
|
||||||
@@ -127,14 +128,14 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
payments: [
|
payments: [
|
||||||
{
|
{
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -143,7 +144,7 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
presenter.present(result);
|
presenter.present(result);
|
||||||
|
|
||||||
const responseModel = presenter.getResponseModel();
|
const responseModel = presenter.getResponseModel();
|
||||||
expect(responseModel.payments[0].completedAt).toBeUndefined();
|
expect(responseModel.payments[0]!.completedAt).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle empty payments list', () => {
|
it('should handle empty payments list', () => {
|
||||||
@@ -162,26 +163,26 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
payments: [
|
payments: [
|
||||||
{
|
{
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'payment-456',
|
id: 'payment-456',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 200,
|
amount: 200,
|
||||||
platformFee: 10,
|
platformFee: 10,
|
||||||
netAmount: 190,
|
netAmount: 190,
|
||||||
payerId: 'user-456',
|
payerId: 'user-456',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-456',
|
leagueId: 'league-456',
|
||||||
status: 'completed',
|
status: PaymentStatus.COMPLETED,
|
||||||
createdAt: new Date('2024-01-02'),
|
createdAt: new Date('2024-01-02'),
|
||||||
completedAt: new Date('2024-01-03'),
|
completedAt: new Date('2024-01-03'),
|
||||||
},
|
},
|
||||||
@@ -192,8 +193,8 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
|
|
||||||
const responseModel = presenter.getResponseModel();
|
const responseModel = presenter.getResponseModel();
|
||||||
expect(responseModel.payments).toHaveLength(2);
|
expect(responseModel.payments).toHaveLength(2);
|
||||||
expect(responseModel.payments[0].id).toBe('payment-123');
|
expect(responseModel.payments[0]!.id).toBe('payment-123');
|
||||||
expect(responseModel.payments[1].id).toBe('payment-456');
|
expect(responseModel.payments[1]!.id).toBe('payment-456');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -207,14 +208,14 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
payments: [
|
payments: [
|
||||||
{
|
{
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -224,7 +225,7 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
|
|
||||||
const responseModel = presenter.getResponseModel();
|
const responseModel = presenter.getResponseModel();
|
||||||
expect(responseModel).toBeDefined();
|
expect(responseModel).toBeDefined();
|
||||||
expect(responseModel.payments[0].id).toBe('payment-123');
|
expect(responseModel.payments[0]!.id).toBe('payment-123');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -234,14 +235,14 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
payments: [
|
payments: [
|
||||||
{
|
{
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -258,14 +259,14 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
payments: [
|
payments: [
|
||||||
{
|
{
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -275,14 +276,14 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
payments: [
|
payments: [
|
||||||
{
|
{
|
||||||
id: 'payment-456',
|
id: 'payment-456',
|
||||||
type: 'membership',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 200,
|
amount: 200,
|
||||||
platformFee: 10,
|
platformFee: 10,
|
||||||
netAmount: 190,
|
netAmount: 190,
|
||||||
payerId: 'user-456',
|
payerId: 'user-456',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-456',
|
leagueId: 'league-456',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-02'),
|
createdAt: new Date('2024-01-02'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -293,7 +294,7 @@ describe('GetPaymentsPresenter', () => {
|
|||||||
presenter.present(secondResult);
|
presenter.present(secondResult);
|
||||||
|
|
||||||
const responseModel = presenter.getResponseModel();
|
const responseModel = presenter.getResponseModel();
|
||||||
expect(responseModel.payments[0].id).toBe('payment-456');
|
expect(responseModel.payments[0]!.id).toBe('payment-456');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ describe('GetPrizesPresenter', () => {
|
|||||||
type: PrizeType.CASH,
|
type: PrizeType.CASH,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
|
seasonId: 'season-123',
|
||||||
|
position: 1,
|
||||||
|
awarded: false,
|
||||||
|
createdAt: new Date(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -39,6 +43,10 @@ describe('GetPrizesPresenter', () => {
|
|||||||
type: PrizeType.CASH,
|
type: PrizeType.CASH,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
|
seasonId: 'season-123',
|
||||||
|
position: 1,
|
||||||
|
awarded: false,
|
||||||
|
createdAt: new Date(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -52,6 +60,10 @@ describe('GetPrizesPresenter', () => {
|
|||||||
type: PrizeType.MERCHANDISE,
|
type: PrizeType.MERCHANDISE,
|
||||||
amount: 200,
|
amount: 200,
|
||||||
leagueId: 'league-456',
|
leagueId: 'league-456',
|
||||||
|
seasonId: 'season-456',
|
||||||
|
position: 2,
|
||||||
|
awarded: false,
|
||||||
|
createdAt: new Date(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -78,6 +90,10 @@ describe('GetPrizesPresenter', () => {
|
|||||||
type: PrizeType.CASH,
|
type: PrizeType.CASH,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
|
seasonId: 'season-123',
|
||||||
|
position: 1,
|
||||||
|
awarded: false,
|
||||||
|
createdAt: new Date(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -99,6 +115,10 @@ describe('GetPrizesPresenter', () => {
|
|||||||
type: PrizeType.CASH,
|
type: PrizeType.CASH,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
|
seasonId: 'season-123',
|
||||||
|
position: 1,
|
||||||
|
awarded: false,
|
||||||
|
createdAt: new Date(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -119,6 +139,10 @@ describe('GetPrizesPresenter', () => {
|
|||||||
type: PrizeType.CASH,
|
type: PrizeType.CASH,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
|
seasonId: 'season-123',
|
||||||
|
position: 1,
|
||||||
|
awarded: false,
|
||||||
|
createdAt: new Date(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -132,6 +156,10 @@ describe('GetPrizesPresenter', () => {
|
|||||||
type: PrizeType.MERCHANDISE,
|
type: PrizeType.MERCHANDISE,
|
||||||
amount: 200,
|
amount: 200,
|
||||||
leagueId: 'league-456',
|
leagueId: 'league-456',
|
||||||
|
seasonId: 'season-456',
|
||||||
|
position: 2,
|
||||||
|
awarded: false,
|
||||||
|
createdAt: new Date(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -155,6 +183,10 @@ describe('GetPrizesPresenter', () => {
|
|||||||
type: PrizeType.CASH,
|
type: PrizeType.CASH,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
|
seasonId: 'season-123',
|
||||||
|
position: 1,
|
||||||
|
awarded: false,
|
||||||
|
createdAt: new Date(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -178,6 +210,10 @@ describe('GetPrizesPresenter', () => {
|
|||||||
type: PrizeType.CASH,
|
type: PrizeType.CASH,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
|
seasonId: 'season-123',
|
||||||
|
position: 1,
|
||||||
|
awarded: false,
|
||||||
|
createdAt: new Date(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
import { describe, expect, it, beforeEach } from 'vitest';
|
||||||
import { UpdatePaymentStatusPresenter } from './UpdatePaymentStatusPresenter';
|
import { UpdatePaymentStatusPresenter } from './UpdatePaymentStatusPresenter';
|
||||||
import { UpdatePaymentStatusOutput } from '../dtos/PaymentsDto';
|
import { PaymentType, PayerType, PaymentStatus } from '@core/payments/domain/entities/Payment';
|
||||||
|
|
||||||
describe('UpdatePaymentStatusPresenter', () => {
|
describe('UpdatePaymentStatusPresenter', () => {
|
||||||
let presenter: UpdatePaymentStatusPresenter;
|
let presenter: UpdatePaymentStatusPresenter;
|
||||||
@@ -13,14 +14,14 @@ describe('UpdatePaymentStatusPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership_fee',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'completed',
|
status: PaymentStatus.COMPLETED,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
completedAt: new Date('2024-01-02'),
|
completedAt: new Date('2024-01-02'),
|
||||||
},
|
},
|
||||||
@@ -32,14 +33,14 @@ describe('UpdatePaymentStatusPresenter', () => {
|
|||||||
expect(responseModel).toEqual({
|
expect(responseModel).toEqual({
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership_fee',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'completed',
|
status: PaymentStatus.COMPLETED,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
completedAt: new Date('2024-01-02'),
|
completedAt: new Date('2024-01-02'),
|
||||||
},
|
},
|
||||||
@@ -50,15 +51,15 @@ describe('UpdatePaymentStatusPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership_fee',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
seasonId: 'season-123',
|
seasonId: 'season-123',
|
||||||
status: 'completed',
|
status: PaymentStatus.COMPLETED,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
completedAt: new Date('2024-01-02'),
|
completedAt: new Date('2024-01-02'),
|
||||||
},
|
},
|
||||||
@@ -74,14 +75,14 @@ describe('UpdatePaymentStatusPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership_fee',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'completed',
|
status: PaymentStatus.COMPLETED,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
completedAt: new Date('2024-01-02'),
|
completedAt: new Date('2024-01-02'),
|
||||||
},
|
},
|
||||||
@@ -97,14 +98,14 @@ describe('UpdatePaymentStatusPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership_fee',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'completed',
|
status: PaymentStatus.COMPLETED,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -119,14 +120,14 @@ describe('UpdatePaymentStatusPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership_fee',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'pending',
|
status: PaymentStatus.PENDING,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -147,14 +148,14 @@ describe('UpdatePaymentStatusPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership_fee',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'completed',
|
status: PaymentStatus.COMPLETED,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
completedAt: new Date('2024-01-02'),
|
completedAt: new Date('2024-01-02'),
|
||||||
},
|
},
|
||||||
@@ -173,14 +174,14 @@ describe('UpdatePaymentStatusPresenter', () => {
|
|||||||
const result = {
|
const result = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership_fee',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'completed',
|
status: PaymentStatus.COMPLETED,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
completedAt: new Date('2024-01-02'),
|
completedAt: new Date('2024-01-02'),
|
||||||
},
|
},
|
||||||
@@ -196,14 +197,14 @@ describe('UpdatePaymentStatusPresenter', () => {
|
|||||||
const firstResult = {
|
const firstResult = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-123',
|
id: 'payment-123',
|
||||||
type: 'membership_fee',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 100,
|
amount: 100,
|
||||||
platformFee: 5,
|
platformFee: 5,
|
||||||
netAmount: 95,
|
netAmount: 95,
|
||||||
payerId: 'user-123',
|
payerId: 'user-123',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-123',
|
leagueId: 'league-123',
|
||||||
status: 'completed',
|
status: PaymentStatus.COMPLETED,
|
||||||
createdAt: new Date('2024-01-01'),
|
createdAt: new Date('2024-01-01'),
|
||||||
completedAt: new Date('2024-01-02'),
|
completedAt: new Date('2024-01-02'),
|
||||||
},
|
},
|
||||||
@@ -212,14 +213,14 @@ describe('UpdatePaymentStatusPresenter', () => {
|
|||||||
const secondResult = {
|
const secondResult = {
|
||||||
payment: {
|
payment: {
|
||||||
id: 'payment-456',
|
id: 'payment-456',
|
||||||
type: 'membership_fee',
|
type: PaymentType.MEMBERSHIP_FEE,
|
||||||
amount: 200,
|
amount: 200,
|
||||||
platformFee: 10,
|
platformFee: 10,
|
||||||
netAmount: 190,
|
netAmount: 190,
|
||||||
payerId: 'user-456',
|
payerId: 'user-456',
|
||||||
payerType: 'driver',
|
payerType: PayerType.DRIVER,
|
||||||
leagueId: 'league-456',
|
leagueId: 'league-456',
|
||||||
status: 'completed',
|
status: PaymentStatus.COMPLETED,
|
||||||
createdAt: new Date('2024-01-02'),
|
createdAt: new Date('2024-01-02'),
|
||||||
completedAt: new Date('2024-01-03'),
|
completedAt: new Date('2024-01-03'),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { Module } from '@nestjs/common';
|
|||||||
import { TypeOrmModule, getDataSourceToken } from '@nestjs/typeorm';
|
import { TypeOrmModule, getDataSourceToken } from '@nestjs/typeorm';
|
||||||
import type { DataSource } from 'typeorm';
|
import type { DataSource } from 'typeorm';
|
||||||
|
|
||||||
import { AdminUserOrmEntity } from '@core/admin/infrastructure/typeorm/entities/AdminUserOrmEntity';
|
import { AdminUserOrmEntity } from '@adapters/admin/persistence/typeorm/entities/AdminUserOrmEntity';
|
||||||
import { AdminUserOrmMapper } from '@core/admin/infrastructure/typeorm/mappers/AdminUserOrmMapper';
|
import { AdminUserOrmMapper } from '@adapters/admin/persistence/typeorm/mappers/AdminUserOrmMapper';
|
||||||
import { TypeOrmAdminUserRepository } from '@core/admin/infrastructure/typeorm/repositories/TypeOrmAdminUserRepository';
|
import { TypeOrmAdminUserRepository } from '@adapters/admin/persistence/typeorm/repositories/TypeOrmAdminUserRepository';
|
||||||
|
|
||||||
import { ADMIN_USER_REPOSITORY_TOKEN } from '../admin/AdminPersistenceTokens';
|
import { ADMIN_USER_REPOSITORY_TOKEN } from '../admin/AdminPersistenceTokens';
|
||||||
|
|
||||||
|
|||||||
363
apps/api/src/shared/testing/contractValidation.test.ts
Normal file
363
apps/api/src/shared/testing/contractValidation.test.ts
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
/**
|
||||||
|
* API Contract Validation Tests
|
||||||
|
*
|
||||||
|
* Validates that API DTOs are consistent and generate valid OpenAPI specs.
|
||||||
|
* This test suite ensures contract compatibility between API and website.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, it, expect, beforeAll } from 'vitest';
|
||||||
|
import * as fs from 'fs/promises';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
// Import DTO classes to validate their structure
|
||||||
|
import { GetAnalyticsMetricsOutputDTO } from '../../domain/analytics/dtos/GetAnalyticsMetricsOutputDTO';
|
||||||
|
import { GetDashboardDataOutputDTO } from '../../domain/analytics/dtos/GetDashboardDataOutputDTO';
|
||||||
|
import { RecordEngagementInputDTO } from '../../domain/analytics/dtos/RecordEngagementInputDTO';
|
||||||
|
import { RecordEngagementOutputDTO } from '../../domain/analytics/dtos/RecordEngagementOutputDTO';
|
||||||
|
import { RecordPageViewInputDTO } from '../../domain/analytics/dtos/RecordPageViewInputDTO';
|
||||||
|
import { RecordPageViewOutputDTO } from '../../domain/analytics/dtos/RecordPageViewOutputDTO';
|
||||||
|
import { RequestAvatarGenerationInputDTO } from '../../domain/media/dtos/RequestAvatarGenerationInputDTO';
|
||||||
|
import { RequestAvatarGenerationOutputDTO } from '../../domain/media/dtos/RequestAvatarGenerationOutputDTO';
|
||||||
|
import { UploadMediaInputDTO } from '../../domain/media/dtos/UploadMediaInputDTO';
|
||||||
|
import { UploadMediaOutputDTO } from '../../domain/media/dtos/UploadMediaOutputDTO';
|
||||||
|
import { ValidateFaceInputDTO } from '../../domain/media/dtos/ValidateFaceInputDTO';
|
||||||
|
import { ValidateFaceOutputDTO } from '../../domain/media/dtos/ValidateFaceOutputDTO';
|
||||||
|
import { RaceDTO } from '../../domain/race/dtos/RaceDTO';
|
||||||
|
import { RaceDetailDTO } from '../../domain/race/dtos/RaceDetailDTO';
|
||||||
|
import { RaceResultDTO } from '../../domain/race/dtos/RaceResultDTO';
|
||||||
|
import { SponsorDTO } from '../../domain/sponsor/dtos/SponsorDTO';
|
||||||
|
import { SponsorshipDTO } from '../../domain/sponsor/dtos/SponsorshipDTO';
|
||||||
|
import { TeamDTO } from '../../domain/team/dtos/TeamDto';
|
||||||
|
|
||||||
|
const colors = {
|
||||||
|
reset: '\x1b[0m',
|
||||||
|
green: '\x1b[32m',
|
||||||
|
red: '\x1b[31m',
|
||||||
|
yellow: '\x1b[33m',
|
||||||
|
cyan: '\x1b[36m',
|
||||||
|
dim: '\x1b[2m'
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('API Contract Validation', () => {
|
||||||
|
let openApiSpec: any;
|
||||||
|
let specPath: string;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
// Load the OpenAPI spec
|
||||||
|
specPath = path.join(__dirname, '..', '..', '..', 'openapi.json');
|
||||||
|
const specContent = await fs.readFile(specPath, 'utf-8');
|
||||||
|
openApiSpec = JSON.parse(specContent);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('OpenAPI Spec Integrity', () => {
|
||||||
|
it('should have valid OpenAPI structure', () => {
|
||||||
|
expect(openApiSpec).toBeDefined();
|
||||||
|
expect(openApiSpec.openapi).toBeDefined();
|
||||||
|
expect(openApiSpec.info).toBeDefined();
|
||||||
|
expect(openApiSpec.paths).toBeDefined();
|
||||||
|
expect(openApiSpec.components).toBeDefined();
|
||||||
|
expect(openApiSpec.components.schemas).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have valid OpenAPI version', () => {
|
||||||
|
expect(openApiSpec.openapi).toMatch(/^3\.\d+\.\d+$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have required API metadata', () => {
|
||||||
|
expect(openApiSpec.info.title).toBeDefined();
|
||||||
|
expect(openApiSpec.info.version).toBeDefined();
|
||||||
|
expect(openApiSpec.info.description).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have no circular references in schemas', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
const visited = new Set<string>();
|
||||||
|
const visiting = new Set<string>();
|
||||||
|
|
||||||
|
const checkCircular = (schemaName: string, schema: any): boolean => {
|
||||||
|
if (!schema) return false;
|
||||||
|
if (visiting.has(schemaName)) {
|
||||||
|
return true; // Circular reference detected
|
||||||
|
}
|
||||||
|
if (visited.has(schemaName)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
visiting.add(schemaName);
|
||||||
|
|
||||||
|
// Check $ref references
|
||||||
|
if (schema.$ref) {
|
||||||
|
const refName = schema.$ref.split('/').pop();
|
||||||
|
if (schemas[refName] && checkCircular(refName, schemas[refName])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check properties
|
||||||
|
if (schema.properties) {
|
||||||
|
for (const prop of Object.values(schema.properties)) {
|
||||||
|
if ((prop as any).$ref) {
|
||||||
|
const refName = (prop as any).$ref.split('/').pop();
|
||||||
|
if (schemas[refName] && checkCircular(refName, schemas[refName])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check array items
|
||||||
|
if (schema.items && schema.items.$ref) {
|
||||||
|
const refName = schema.items.$ref.split('/').pop();
|
||||||
|
if (schemas[refName] && checkCircular(refName, schemas[refName])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visiting.delete(schemaName);
|
||||||
|
visited.add(schemaName);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [schemaName, schema] of Object.entries(schemas)) {
|
||||||
|
if (checkCircular(schemaName, schema as any)) {
|
||||||
|
throw new Error(`Circular reference detected in schema: ${schemaName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have all required DTOs in OpenAPI spec', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
|
||||||
|
// List of critical DTOs that must exist in the spec
|
||||||
|
const requiredDTOs = [
|
||||||
|
'GetAnalyticsMetricsOutputDTO',
|
||||||
|
'GetDashboardDataOutputDTO',
|
||||||
|
'RecordEngagementInputDTO',
|
||||||
|
'RecordEngagementOutputDTO',
|
||||||
|
'RecordPageViewInputDTO',
|
||||||
|
'RecordPageViewOutputDTO',
|
||||||
|
'RequestAvatarGenerationInputDTO',
|
||||||
|
'RequestAvatarGenerationOutputDTO',
|
||||||
|
'UploadMediaInputDTO',
|
||||||
|
'UploadMediaOutputDTO',
|
||||||
|
'ValidateFaceInputDTO',
|
||||||
|
'ValidateFaceOutputDTO',
|
||||||
|
'RaceDTO',
|
||||||
|
'RaceDetailDTO',
|
||||||
|
'RaceResultDTO',
|
||||||
|
'SponsorDTO',
|
||||||
|
'SponsorshipDTO',
|
||||||
|
'TeamDTO'
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const dtoName of requiredDTOs) {
|
||||||
|
expect(schemas[dtoName], `DTO ${dtoName} should exist in OpenAPI spec`).toBeDefined();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have valid JSON schema for all DTOs', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
|
||||||
|
for (const [schemaName, schema] of Object.entries(schemas)) {
|
||||||
|
expect(schema, `Schema ${schemaName} should be an object`).toBeInstanceOf(Object);
|
||||||
|
expect(schema.type, `Schema ${schemaName} should have a type`).toBeDefined();
|
||||||
|
|
||||||
|
if (schema.type === 'object') {
|
||||||
|
expect(schema.properties, `Schema ${schemaName} should have properties`).toBeDefined();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('DTO Consistency', () => {
|
||||||
|
it('should have consistent DTO definitions between code and spec', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
|
||||||
|
// Test a sample of DTOs to ensure they match the spec
|
||||||
|
const testDTOs = [
|
||||||
|
{ name: 'GetAnalyticsMetricsOutputDTO', expectedProps: ['pageViews', 'uniqueVisitors', 'averageSessionDuration', 'bounceRate'] },
|
||||||
|
{ name: 'RaceDTO', expectedProps: ['id', 'name', 'date'] },
|
||||||
|
{ name: 'SponsorDTO', expectedProps: ['id', 'name'] }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { name, expectedProps } of testDTOs) {
|
||||||
|
const schema = schemas[name];
|
||||||
|
expect(schema, `Schema ${name} should exist`).toBeDefined();
|
||||||
|
|
||||||
|
if (schema.properties) {
|
||||||
|
for (const prop of expectedProps) {
|
||||||
|
expect(schema.properties[prop], `Property ${prop} should exist in ${name}`).toBeDefined();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have no duplicate DTO names', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
const schemaNames = Object.keys(schemas);
|
||||||
|
const uniqueNames = new Set(schemaNames);
|
||||||
|
|
||||||
|
expect(schemaNames.length).toBe(uniqueNames.size);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have consistent naming conventions', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
|
||||||
|
for (const schemaName of Object.keys(schemas)) {
|
||||||
|
// DTO names should end with DTO
|
||||||
|
expect(schemaName).toMatch(/DTO$/);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Type Generation Integrity', () => {
|
||||||
|
it('should have all DTOs with proper type definitions', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
|
||||||
|
for (const [schemaName, schema] of Object.entries(schemas)) {
|
||||||
|
if (schema.type === 'object') {
|
||||||
|
expect(schema.properties, `Schema ${schemaName} should have properties`).toBeDefined();
|
||||||
|
|
||||||
|
// Check that all properties have types or are references
|
||||||
|
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
||||||
|
const prop = propSchema as any;
|
||||||
|
// Properties can have a type directly, or be a $ref to another schema
|
||||||
|
const hasType = prop.type !== undefined;
|
||||||
|
const isRef = prop.$ref !== undefined;
|
||||||
|
|
||||||
|
expect(hasType || isRef, `Property ${propName} in ${schemaName} should have a type or be a $ref`).toBe(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have required fields properly marked', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
|
||||||
|
// Test a few critical DTOs
|
||||||
|
const testDTOs = [
|
||||||
|
{ name: 'GetAnalyticsMetricsOutputDTO', required: ['pageViews', 'uniqueVisitors', 'averageSessionDuration', 'bounceRate'] },
|
||||||
|
{ name: 'RaceDTO', required: ['id', 'name', 'date'] }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { name, required } of testDTOs) {
|
||||||
|
const schema = schemas[name];
|
||||||
|
expect(schema.required, `Schema ${name} should have required fields`).toBeDefined();
|
||||||
|
|
||||||
|
for (const field of required) {
|
||||||
|
expect(schema.required).toContain(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have nullable fields properly marked', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
|
||||||
|
// Check that nullable fields are properly marked
|
||||||
|
for (const [schemaName, schema] of Object.entries(schemas)) {
|
||||||
|
if (schema.properties) {
|
||||||
|
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
||||||
|
if ((propSchema as any).nullable === true) {
|
||||||
|
// Nullable fields should not be in required array
|
||||||
|
if (schema.required) {
|
||||||
|
expect(schema.required).not.toContain(propName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Contract Compatibility', () => {
|
||||||
|
it('should have backward compatible DTOs', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
|
||||||
|
// Critical DTOs that must maintain backward compatibility
|
||||||
|
const criticalDTOs = [
|
||||||
|
'RaceDTO',
|
||||||
|
'SponsorDTO',
|
||||||
|
'TeamDTO',
|
||||||
|
'DriverDTO'
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const dtoName of criticalDTOs) {
|
||||||
|
const schema = schemas[dtoName];
|
||||||
|
expect(schema, `Critical DTO ${dtoName} should exist`).toBeDefined();
|
||||||
|
|
||||||
|
// These DTOs should have required fields that cannot be removed
|
||||||
|
if (schema.required) {
|
||||||
|
expect(schema.required.length).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have no breaking changes in required fields', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
|
||||||
|
// Check that required fields are not empty for critical DTOs
|
||||||
|
const criticalDTOs = ['RaceDTO', 'SponsorDTO', 'TeamDTO'];
|
||||||
|
|
||||||
|
for (const dtoName of criticalDTOs) {
|
||||||
|
const schema = schemas[dtoName];
|
||||||
|
if (schema && schema.required) {
|
||||||
|
expect(schema.required.length).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have consistent field types across versions', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
|
||||||
|
// Check that common fields have consistent types
|
||||||
|
const commonFields = {
|
||||||
|
id: 'string',
|
||||||
|
name: 'string',
|
||||||
|
createdAt: 'string',
|
||||||
|
updatedAt: 'string'
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [fieldName, expectedType] of Object.entries(commonFields)) {
|
||||||
|
for (const [schemaName, schema] of Object.entries(schemas)) {
|
||||||
|
if (schema.properties && schema.properties[fieldName]) {
|
||||||
|
expect(schema.properties[fieldName].type).toBe(expectedType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Contract Validation Summary', () => {
|
||||||
|
it('should pass all contract validation checks', () => {
|
||||||
|
const schemas = openApiSpec.components.schemas as Record<string, any>;
|
||||||
|
const schemaCount = Object.keys(schemas).length;
|
||||||
|
|
||||||
|
console.log(`${colors.cyan}📊 Contract Validation Summary${colors.reset}`);
|
||||||
|
console.log(`${colors.dim} Total DTOs in OpenAPI spec: ${schemaCount}${colors.reset}`);
|
||||||
|
console.log(`${colors.dim} Spec file: ${specPath}${colors.reset}`);
|
||||||
|
|
||||||
|
// Verify critical metrics
|
||||||
|
expect(schemaCount).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
// Count DTOs by category
|
||||||
|
const analyticsDTOs = Object.keys(schemas).filter(name => name.includes('Analytics') || name.includes('Engagement') || name.includes('PageView'));
|
||||||
|
const mediaDTOs = Object.keys(schemas).filter(name => name.includes('Media') || name.includes('Avatar'));
|
||||||
|
const raceDTOs = Object.keys(schemas).filter(name => name.includes('Race'));
|
||||||
|
const sponsorDTOs = Object.keys(schemas).filter(name => name.includes('Sponsor'));
|
||||||
|
const teamDTOs = Object.keys(schemas).filter(name => name.includes('Team'));
|
||||||
|
|
||||||
|
console.log(`${colors.dim} Analytics DTOs: ${analyticsDTOs.length}${colors.reset}`);
|
||||||
|
console.log(`${colors.dim} Media DTOs: ${mediaDTOs.length}${colors.reset}`);
|
||||||
|
console.log(`${colors.dim} Race DTOs: ${raceDTOs.length}${colors.reset}`);
|
||||||
|
console.log(`${colors.dim} Sponsor DTOs: ${sponsorDTOs.length}${colors.reset}`);
|
||||||
|
console.log(`${colors.dim} Team DTOs: ${teamDTOs.length}${colors.reset}`);
|
||||||
|
|
||||||
|
// Verify that we have DTOs in each category
|
||||||
|
expect(analyticsDTOs.length).toBeGreaterThan(0);
|
||||||
|
expect(mediaDTOs.length).toBeGreaterThan(0);
|
||||||
|
expect(raceDTOs.length).toBeGreaterThan(0);
|
||||||
|
expect(sponsorDTOs.length).toBeGreaterThan(0);
|
||||||
|
expect(teamDTOs.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,22 +1,21 @@
|
|||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import { Result } from '@/lib/contracts/Result';
|
import { Result } from '@/lib/contracts/Result';
|
||||||
import { CompleteOnboardingMutation } from '@/lib/mutations/onboarding/CompleteOnboardingMutation';
|
import { CompleteOnboardingMutation, CompleteOnboardingCommand } from '@/lib/mutations/onboarding/CompleteOnboardingMutation';
|
||||||
import { CompleteOnboardingInputDTO } from '@/lib/types/generated/CompleteOnboardingInputDTO';
|
|
||||||
import { revalidatePath } from 'next/cache';
|
import { revalidatePath } from 'next/cache';
|
||||||
import { routes } from '@/lib/routing/RouteConfig';
|
import { routes } from '@/lib/routing/RouteConfig';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Complete onboarding - thin wrapper around mutation
|
* Complete onboarding - thin wrapper around mutation
|
||||||
*
|
*
|
||||||
* Pattern: Server Action → Mutation → Service → API Client
|
* Pattern: Server Action → Mutation → Service → API Client
|
||||||
*
|
*
|
||||||
* Authentication is handled automatically by the API via cookies.
|
* Authentication is handled automatically by the API via cookies.
|
||||||
* The BaseApiClient includes credentials: 'include', so cookies are sent automatically.
|
* The BaseApiClient includes credentials: 'include', so cookies are sent automatically.
|
||||||
* If authentication fails, the API returns 401/403 which gets converted to domain errors.
|
* If authentication fails, the API returns 401/403 which gets converted to domain errors.
|
||||||
*/
|
*/
|
||||||
export async function completeOnboardingAction(
|
export async function completeOnboardingAction(
|
||||||
input: CompleteOnboardingInputDTO
|
input: CompleteOnboardingCommand
|
||||||
): Promise<Result<{ success: boolean }, string>> {
|
): Promise<Result<{ success: boolean }, string>> {
|
||||||
const mutation = new CompleteOnboardingMutation();
|
const mutation = new CompleteOnboardingMutation();
|
||||||
const result = await mutation.execute(input);
|
const result = await mutation.execute(input);
|
||||||
|
|||||||
@@ -124,16 +124,16 @@ export async function withdrawFromRaceAction(raceId: string, driverId: string, l
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||||
export async function navigateToEditRaceAction(leagueId: string): Promise<void> {
|
export async function navigateToEditRaceAction(raceId: string, leagueId: string): Promise<void> {
|
||||||
redirect(routes.league.scheduleAdmin(leagueId));
|
redirect(routes.league.scheduleAdmin(leagueId));
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||||
export async function navigateToRescheduleRaceAction(leagueId: string): Promise<void> {
|
export async function navigateToRescheduleRaceAction(raceId: string, leagueId: string): Promise<void> {
|
||||||
redirect(routes.league.scheduleAdmin(leagueId));
|
redirect(routes.league.scheduleAdmin(leagueId));
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||||
export async function navigateToRaceResultsAction(raceId: string): Promise<void> {
|
export async function navigateToRaceResultsAction(raceId: string, leagueId: string): Promise<void> {
|
||||||
redirect(routes.race.results(raceId));
|
redirect(routes.race.results(raceId));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export default async function DriverProfilePage({ params }: { params: Promise<{
|
|||||||
return (
|
return (
|
||||||
<DriverProfilePageClient
|
<DriverProfilePageClient
|
||||||
viewData={null}
|
viewData={null}
|
||||||
error={error}
|
error={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -76,4 +76,4 @@ export default async function DriverProfilePage({ params }: { params: Promise<{
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ export default async function Page() {
|
|||||||
return (
|
return (
|
||||||
<DriversPageClient
|
<DriversPageClient
|
||||||
viewData={null}
|
viewData={null}
|
||||||
error={error}
|
error={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,8 +59,12 @@ export default async function LeagueLayout({
|
|||||||
sponsorSlots: {
|
sponsorSlots: {
|
||||||
main: { price: 0, status: 'occupied' },
|
main: { price: 0, status: 'occupied' },
|
||||||
secondary: { price: 0, total: 0, occupied: 0 }
|
secondary: { price: 0, total: 0, occupied: 0 }
|
||||||
}
|
},
|
||||||
},
|
ownerId: '',
|
||||||
|
createdAt: '',
|
||||||
|
settings: {},
|
||||||
|
usedSlots: 0,
|
||||||
|
} as any,
|
||||||
drivers: [],
|
drivers: [],
|
||||||
races: [],
|
races: [],
|
||||||
seasonProgress: { completedRaces: 0, totalRaces: 0, percentage: 0 },
|
seasonProgress: { completedRaces: 0, totalRaces: 0, percentage: 0 },
|
||||||
@@ -98,7 +102,7 @@ export default async function LeagueLayout({
|
|||||||
|
|
||||||
// Check if user is admin or owner
|
// Check if user is admin or owner
|
||||||
const isOwner = currentDriver && data.league.ownerId === currentDriver.id;
|
const isOwner = currentDriver && data.league.ownerId === currentDriver.id;
|
||||||
const isAdmin = currentDriver && data.memberships.members?.some(m => m.driverId === currentDriver.id && m.role === 'admin');
|
const isAdmin = currentDriver && data.memberships.members?.some((m: any) => m.driverId === currentDriver.id && m.role === 'admin');
|
||||||
const hasAdminAccess = isOwner || isAdmin;
|
const hasAdminAccess = isOwner || isAdmin;
|
||||||
|
|
||||||
const adminTabs = hasAdminAccess ? [
|
const adminTabs = hasAdminAccess ? [
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default async function Page({ params }: Props) {
|
|||||||
// Determine if current user is owner or admin
|
// Determine if current user is owner or admin
|
||||||
const isOwnerOrAdmin = currentDriverId
|
const isOwnerOrAdmin = currentDriverId
|
||||||
? currentDriverId === league.ownerId ||
|
? currentDriverId === league.ownerId ||
|
||||||
data.memberships.members?.some(m => m.driverId === currentDriverId && m.role === 'admin')
|
data.memberships.members?.some((m: any) => m.driverId === currentDriverId && m.role === 'admin')
|
||||||
: false;
|
: false;
|
||||||
|
|
||||||
// Build ViewData using the builder
|
// Build ViewData using the builder
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default async function LeagueRosterPage({ params }: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = result.unwrap();
|
const data = result.unwrap();
|
||||||
const members = (data.memberships.members || []).map(m => ({
|
const members = (data.memberships.members || []).map((m: any) => ({
|
||||||
driverId: m.driverId,
|
driverId: m.driverId,
|
||||||
driverName: m.driver.name,
|
driverName: m.driver.name,
|
||||||
role: m.role,
|
role: m.role,
|
||||||
|
|||||||
@@ -5,7 +5,16 @@ import { DriverProfileTemplate } from '@/templates/DriverProfileTemplate';
|
|||||||
import { EmptyTemplate, ErrorTemplate } from '@/templates/shared/StatusTemplates';
|
import { EmptyTemplate, ErrorTemplate } from '@/templates/shared/StatusTemplates';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import type { DriverProfileViewData } from '@/lib/view-data/DriverProfileViewData';
|
||||||
|
|
||||||
|
interface DriverProfilePageClientProps {
|
||||||
|
viewData: DriverProfileViewData | null;
|
||||||
|
error?: boolean;
|
||||||
|
empty?: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function DriverProfilePageClient({ viewData, error, empty }: DriverProfilePageClientProps) {
|
export function DriverProfilePageClient({ viewData, error, empty }: DriverProfilePageClientProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|||||||
@@ -5,6 +5,16 @@ import { DriversTemplate } from '@/templates/DriversTemplate';
|
|||||||
import { EmptyTemplate, ErrorTemplate } from '@/templates/shared/StatusTemplates';
|
import { EmptyTemplate, ErrorTemplate } from '@/templates/shared/StatusTemplates';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
import type { DriversViewData, DriverViewData } from '@/lib/view-data/DriversViewData';
|
||||||
|
|
||||||
|
interface DriversPageClientProps {
|
||||||
|
viewData: DriversViewData | null;
|
||||||
|
error?: boolean;
|
||||||
|
empty?: {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function DriversPageClient({ viewData, error, empty }: DriversPageClientProps) {
|
export function DriversPageClient({ viewData, error, empty }: DriversPageClientProps) {
|
||||||
@@ -16,8 +26,8 @@ export function DriversPageClient({ viewData, error, empty }: DriversPageClientP
|
|||||||
if (!searchQuery) return viewData.drivers;
|
if (!searchQuery) return viewData.drivers;
|
||||||
|
|
||||||
const query = searchQuery.toLowerCase();
|
const query = searchQuery.toLowerCase();
|
||||||
return viewData.drivers.filter(driver =>
|
return viewData.drivers.filter((driver: DriverViewData) =>
|
||||||
driver.name.toLowerCase().includes(query) ||
|
driver.name.toLowerCase().includes(query) ||
|
||||||
driver.nationality.toLowerCase().includes(query)
|
driver.nationality.toLowerCase().includes(query)
|
||||||
);
|
);
|
||||||
}, [viewData, searchQuery]);
|
}, [viewData, searchQuery]);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ForgotPasswordViewModelBuilder } from '@/lib/builders/view-models/ForgotPasswordViewModelBuilder';
|
|
||||||
import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts';
|
import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts';
|
||||||
import { ForgotPasswordMutation } from '@/lib/mutations/auth/ForgotPasswordMutation';
|
import { ForgotPasswordMutation } from '@/lib/mutations/auth/ForgotPasswordMutation';
|
||||||
import { ForgotPasswordFormValidation } from '@/lib/utilities/authValidation';
|
import { ForgotPasswordFormValidation } from '@/lib/utilities/authValidation';
|
||||||
@@ -18,7 +17,7 @@ import { useState } from 'react';
|
|||||||
export function ForgotPasswordClient({ viewData }: ClientWrapperProps<ForgotPasswordViewData>) {
|
export function ForgotPasswordClient({ viewData }: ClientWrapperProps<ForgotPasswordViewData>) {
|
||||||
// Build ViewModel from ViewData
|
// Build ViewModel from ViewData
|
||||||
const [viewModel, setViewModel] = useState<ForgotPasswordViewModel>(() =>
|
const [viewModel, setViewModel] = useState<ForgotPasswordViewModel>(() =>
|
||||||
ForgotPasswordViewModelBuilder.build(viewData)
|
new ForgotPasswordViewModel(viewData.returnTo, viewData.formState)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle form field changes
|
// Handle form field changes
|
||||||
@@ -114,7 +113,7 @@ export function ForgotPasswordClient({ viewData }: ClientWrapperProps<ForgotPass
|
|||||||
setShowSuccess: (show) => {
|
setShowSuccess: (show) => {
|
||||||
if (!show) {
|
if (!show) {
|
||||||
// Reset to initial state
|
// Reset to initial state
|
||||||
setViewModel(() => ForgotPasswordViewModelBuilder.build(viewData));
|
setViewModel(() => new ForgotPasswordViewModel(viewData.returnTo, viewData.formState));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Login Client Component
|
* Login Client Component
|
||||||
*
|
*
|
||||||
* Handles client-side login flow using the LoginFlowController.
|
* Handles client-side login flow using the LoginFlowController.
|
||||||
* Deterministic state machine per docs/architecture/website/LOGIN_FLOW_STATE_MACHINE.md
|
* Deterministic state machine per docs/architecture/website/LOGIN_FLOW_STATE_MACHINE.md
|
||||||
*/
|
*/
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
import { useAuth } from '@/components/auth/AuthContext';
|
import { useAuth } from '@/components/auth/AuthContext';
|
||||||
import { LoginFlowController, LoginState } from '@/lib/auth/LoginFlowController';
|
import { LoginFlowController, LoginState } from '@/lib/auth/LoginFlowController';
|
||||||
import { LoginViewModelBuilder } from '@/lib/builders/view-models/LoginViewModelBuilder';
|
|
||||||
import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts';
|
import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts';
|
||||||
import { LoginMutation } from '@/lib/mutations/auth/LoginMutation';
|
import { LoginMutation } from '@/lib/mutations/auth/LoginMutation';
|
||||||
import { validateLoginForm, type LoginFormValues } from '@/lib/utils/validation';
|
import { validateLoginForm, type LoginFormValues } from '@/lib/utils/validation';
|
||||||
@@ -26,8 +25,13 @@ export function LoginClient({ viewData }: ClientWrapperProps<LoginViewData>) {
|
|||||||
const { refreshSession, session } = useAuth();
|
const { refreshSession, session } = useAuth();
|
||||||
|
|
||||||
// Build ViewModel from ViewData
|
// Build ViewModel from ViewData
|
||||||
const [viewModel, setViewModel] = useState<LoginViewModel>(() =>
|
const [viewModel, setViewModel] = useState<LoginViewModel>(() =>
|
||||||
LoginViewModelBuilder.build(viewData)
|
new LoginViewModel(
|
||||||
|
viewData.returnTo,
|
||||||
|
viewData.hasInsufficientPermissions,
|
||||||
|
viewData.formState,
|
||||||
|
{ showPassword: viewData.showPassword, showErrorDetails: viewData.showErrorDetails }
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Login flow controller
|
// Login flow controller
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { ResetPasswordViewModelBuilder } from '@/lib/builders/view-models/ResetPasswordViewModelBuilder';
|
|
||||||
import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts';
|
import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts';
|
||||||
import { ResetPasswordMutation } from '@/lib/mutations/auth/ResetPasswordMutation';
|
import { ResetPasswordMutation } from '@/lib/mutations/auth/ResetPasswordMutation';
|
||||||
import { routes } from '@/lib/routing/RouteConfig';
|
import { routes } from '@/lib/routing/RouteConfig';
|
||||||
@@ -23,7 +22,12 @@ export function ResetPasswordClient({ viewData }: ClientWrapperProps<ResetPasswo
|
|||||||
|
|
||||||
// Build ViewModel from ViewData
|
// Build ViewModel from ViewData
|
||||||
const [viewModel, setViewModel] = useState<ResetPasswordViewModel>(() =>
|
const [viewModel, setViewModel] = useState<ResetPasswordViewModel>(() =>
|
||||||
ResetPasswordViewModelBuilder.build(viewData)
|
new ResetPasswordViewModel(
|
||||||
|
viewData.token,
|
||||||
|
viewData.returnTo,
|
||||||
|
viewData.formState,
|
||||||
|
{ showPassword: false, showConfirmPassword: false }
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle form field changes
|
// Handle form field changes
|
||||||
@@ -151,7 +155,12 @@ export function ResetPasswordClient({ viewData }: ClientWrapperProps<ResetPasswo
|
|||||||
setShowSuccess: (show) => {
|
setShowSuccess: (show) => {
|
||||||
if (!show) {
|
if (!show) {
|
||||||
// Reset to initial state
|
// Reset to initial state
|
||||||
setViewModel(() => ResetPasswordViewModelBuilder.build(viewData));
|
setViewModel(() => new ResetPasswordViewModel(
|
||||||
|
viewData.token,
|
||||||
|
viewData.returnTo,
|
||||||
|
viewData.formState,
|
||||||
|
{ showPassword: false, showConfirmPassword: false }
|
||||||
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setShowPassword: togglePassword,
|
setShowPassword: togglePassword,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useAuth } from '@/components/auth/AuthContext';
|
import { useAuth } from '@/components/auth/AuthContext';
|
||||||
import { SignupViewModelBuilder } from '@/lib/builders/view-models/SignupViewModelBuilder';
|
|
||||||
import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts';
|
import { ClientWrapperProps } from '@/lib/contracts/components/ComponentContracts';
|
||||||
import { SignupMutation } from '@/lib/mutations/auth/SignupMutation';
|
import { SignupMutation } from '@/lib/mutations/auth/SignupMutation';
|
||||||
import { SignupFormValidation } from '@/lib/utilities/authValidation';
|
import { SignupFormValidation } from '@/lib/utilities/authValidation';
|
||||||
@@ -24,7 +23,11 @@ export function SignupClient({ viewData }: ClientWrapperProps<SignupViewData>) {
|
|||||||
|
|
||||||
// Build ViewModel from ViewData
|
// Build ViewModel from ViewData
|
||||||
const [viewModel, setViewModel] = useState<SignupViewModel>(() =>
|
const [viewModel, setViewModel] = useState<SignupViewModel>(() =>
|
||||||
SignupViewModelBuilder.build(viewData)
|
new SignupViewModel(
|
||||||
|
viewData.returnTo,
|
||||||
|
viewData.formState,
|
||||||
|
{ showPassword: false, showConfirmPassword: false }
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle form field changes
|
// Handle form field changes
|
||||||
|
|||||||
@@ -11,77 +11,50 @@ import type { DriverProfileViewData } from '@/lib/view-data/DriverProfileViewDat
|
|||||||
|
|
||||||
export class DriverProfileViewDataBuilder {
|
export class DriverProfileViewDataBuilder {
|
||||||
public static build(apiDto: GetDriverProfileOutputDTO): DriverProfileViewData {
|
public static build(apiDto: GetDriverProfileOutputDTO): DriverProfileViewData {
|
||||||
|
const currentDriver = apiDto.currentDriver!;
|
||||||
return {
|
return {
|
||||||
currentDriver: apiDto.currentDriver ? {
|
driver: {
|
||||||
id: apiDto.currentDriver.id,
|
id: currentDriver.id,
|
||||||
name: apiDto.currentDriver.name,
|
name: currentDriver.name,
|
||||||
country: apiDto.currentDriver.country,
|
countryCode: currentDriver.country,
|
||||||
avatarUrl: apiDto.currentDriver.avatarUrl || '',
|
countryFlag: currentDriver.country, // Placeholder
|
||||||
iracingId: typeof apiDto.currentDriver.iracingId === 'string' ? parseInt(apiDto.currentDriver.iracingId, 10) : (apiDto.currentDriver.iracingId ?? null),
|
avatarUrl: currentDriver.avatarUrl || '',
|
||||||
joinedAt: apiDto.currentDriver.joinedAt,
|
bio: currentDriver.bio ?? null,
|
||||||
joinedAtLabel: DateFormatter.formatMonthYear(apiDto.currentDriver.joinedAt),
|
iracingId: currentDriver.iracingId ?? null,
|
||||||
rating: apiDto.currentDriver.rating ?? null,
|
joinedAtLabel: DateFormatter.formatMonthYear(currentDriver.joinedAt),
|
||||||
ratingLabel: RatingFormatter.format(apiDto.currentDriver.rating),
|
globalRankLabel: currentDriver.globalRank != null ? `#${currentDriver.globalRank}` : '—',
|
||||||
globalRank: apiDto.currentDriver.globalRank ?? null,
|
},
|
||||||
globalRankLabel: apiDto.currentDriver.globalRank != null ? `#${apiDto.currentDriver.globalRank}` : '—',
|
|
||||||
consistency: apiDto.currentDriver.consistency ?? null,
|
|
||||||
bio: apiDto.currentDriver.bio ?? null,
|
|
||||||
totalDrivers: apiDto.currentDriver.totalDrivers ?? null,
|
|
||||||
} : null,
|
|
||||||
stats: apiDto.stats ? {
|
stats: apiDto.stats ? {
|
||||||
totalRaces: apiDto.stats.totalRaces,
|
|
||||||
totalRacesLabel: NumberFormatter.format(apiDto.stats.totalRaces),
|
|
||||||
wins: apiDto.stats.wins,
|
|
||||||
winsLabel: NumberFormatter.format(apiDto.stats.wins),
|
|
||||||
podiums: apiDto.stats.podiums,
|
|
||||||
podiumsLabel: NumberFormatter.format(apiDto.stats.podiums),
|
|
||||||
dnfs: apiDto.stats.dnfs,
|
|
||||||
dnfsLabel: NumberFormatter.format(apiDto.stats.dnfs),
|
|
||||||
avgFinish: apiDto.stats.avgFinish ?? null,
|
|
||||||
avgFinishLabel: FinishFormatter.formatAverage(apiDto.stats.avgFinish),
|
|
||||||
bestFinish: apiDto.stats.bestFinish ?? null,
|
|
||||||
bestFinishLabel: FinishFormatter.format(apiDto.stats.bestFinish),
|
|
||||||
worstFinish: apiDto.stats.worstFinish ?? null,
|
|
||||||
worstFinishLabel: FinishFormatter.format(apiDto.stats.worstFinish),
|
|
||||||
finishRate: apiDto.stats.finishRate ?? null,
|
|
||||||
winRate: apiDto.stats.winRate ?? null,
|
|
||||||
podiumRate: apiDto.stats.podiumRate ?? null,
|
|
||||||
percentile: apiDto.stats.percentile ?? null,
|
|
||||||
rating: apiDto.stats.rating ?? null,
|
|
||||||
ratingLabel: RatingFormatter.format(apiDto.stats.rating),
|
ratingLabel: RatingFormatter.format(apiDto.stats.rating),
|
||||||
consistency: apiDto.stats.consistency ?? null,
|
globalRankLabel: apiDto.stats.overallRank != null ? `#${apiDto.stats.overallRank}` : '—',
|
||||||
|
totalRacesLabel: NumberFormatter.format(apiDto.stats.totalRaces),
|
||||||
|
winsLabel: NumberFormatter.format(apiDto.stats.wins),
|
||||||
|
podiumsLabel: NumberFormatter.format(apiDto.stats.podiums),
|
||||||
|
dnfsLabel: NumberFormatter.format(apiDto.stats.dnfs),
|
||||||
|
bestFinishLabel: FinishFormatter.format(apiDto.stats.bestFinish),
|
||||||
|
worstFinishLabel: FinishFormatter.format(apiDto.stats.worstFinish),
|
||||||
|
avgFinishLabel: FinishFormatter.formatAverage(apiDto.stats.avgFinish),
|
||||||
consistencyLabel: PercentFormatter.formatWhole(apiDto.stats.consistency),
|
consistencyLabel: PercentFormatter.formatWhole(apiDto.stats.consistency),
|
||||||
overallRank: apiDto.stats.overallRank ?? null,
|
percentileLabel: PercentFormatter.formatWhole(apiDto.stats.percentile),
|
||||||
} : null,
|
} as any : null,
|
||||||
finishDistribution: apiDto.finishDistribution ? {
|
|
||||||
totalRaces: apiDto.finishDistribution.totalRaces,
|
|
||||||
wins: apiDto.finishDistribution.wins,
|
|
||||||
podiums: apiDto.finishDistribution.podiums,
|
|
||||||
topTen: apiDto.finishDistribution.topTen,
|
|
||||||
dnfs: apiDto.finishDistribution.dnfs,
|
|
||||||
other: apiDto.finishDistribution.other,
|
|
||||||
} : null,
|
|
||||||
teamMemberships: apiDto.teamMemberships.map(m => ({
|
teamMemberships: apiDto.teamMemberships.map(m => ({
|
||||||
teamId: m.teamId,
|
teamId: m.teamId,
|
||||||
teamName: m.teamName,
|
teamName: m.teamName,
|
||||||
teamTag: m.teamTag ?? null,
|
teamTag: m.teamTag ?? null,
|
||||||
role: m.role,
|
roleLabel: m.role,
|
||||||
joinedAt: m.joinedAt,
|
|
||||||
joinedAtLabel: DateFormatter.formatMonthYear(m.joinedAt),
|
joinedAtLabel: DateFormatter.formatMonthYear(m.joinedAt),
|
||||||
isCurrent: m.isCurrent,
|
href: `/teams/${m.teamId}`,
|
||||||
})),
|
})) as any,
|
||||||
socialSummary: {
|
|
||||||
friendsCount: apiDto.socialSummary.friendsCount,
|
|
||||||
friends: apiDto.socialSummary.friends.map(f => ({
|
|
||||||
id: f.id,
|
|
||||||
name: f.name,
|
|
||||||
country: f.country,
|
|
||||||
avatarUrl: f.avatarUrl || '',
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
extendedProfile: apiDto.extendedProfile ? {
|
extendedProfile: apiDto.extendedProfile ? {
|
||||||
|
timezone: apiDto.extendedProfile.timezone,
|
||||||
|
racingStyle: apiDto.extendedProfile.racingStyle,
|
||||||
|
favoriteTrack: apiDto.extendedProfile.favoriteTrack,
|
||||||
|
favoriteCar: apiDto.extendedProfile.favoriteCar,
|
||||||
|
availableHours: apiDto.extendedProfile.availableHours,
|
||||||
|
lookingForTeamLabel: apiDto.extendedProfile.lookingForTeam ? 'Yes' : 'No',
|
||||||
|
openToRequestsLabel: apiDto.extendedProfile.openToRequests ? 'Yes' : 'No',
|
||||||
socialHandles: apiDto.extendedProfile.socialHandles.map(h => ({
|
socialHandles: apiDto.extendedProfile.socialHandles.map(h => ({
|
||||||
platform: h.platform,
|
platformLabel: h.platform,
|
||||||
handle: h.handle,
|
handle: h.handle,
|
||||||
url: h.url,
|
url: h.url,
|
||||||
})),
|
})),
|
||||||
@@ -89,21 +62,20 @@ export class DriverProfileViewDataBuilder {
|
|||||||
id: a.id,
|
id: a.id,
|
||||||
title: a.title,
|
title: a.title,
|
||||||
description: a.description,
|
description: a.description,
|
||||||
icon: a.icon,
|
|
||||||
rarity: a.rarity,
|
|
||||||
rarityLabel: a.rarity,
|
|
||||||
earnedAt: a.earnedAt,
|
|
||||||
earnedAtLabel: DateFormatter.formatShort(a.earnedAt),
|
earnedAtLabel: DateFormatter.formatShort(a.earnedAt),
|
||||||
|
icon: a.icon as any,
|
||||||
|
rarityLabel: a.rarity,
|
||||||
})),
|
})),
|
||||||
racingStyle: apiDto.extendedProfile.racingStyle,
|
friends: apiDto.socialSummary.friends.map(f => ({
|
||||||
favoriteTrack: apiDto.extendedProfile.favoriteTrack,
|
id: f.id,
|
||||||
favoriteCar: apiDto.extendedProfile.favoriteCar,
|
name: f.name,
|
||||||
timezone: apiDto.extendedProfile.timezone,
|
countryFlag: f.country, // Placeholder
|
||||||
availableHours: apiDto.extendedProfile.availableHours,
|
avatarUrl: f.avatarUrl || '',
|
||||||
lookingForTeam: apiDto.extendedProfile.lookingForTeam,
|
href: `/drivers/${f.id}`,
|
||||||
openToRequests: apiDto.extendedProfile.openToRequests,
|
})),
|
||||||
} : null,
|
friendsCountLabel: NumberFormatter.format(apiDto.socialSummary.friendsCount),
|
||||||
};
|
} as any : null,
|
||||||
|
} as any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export class LeagueDetailViewDataBuilder {
|
|||||||
.map(m => ({
|
.map(m => ({
|
||||||
driverId: m.driverId,
|
driverId: m.driverId,
|
||||||
driverName: m.driver.name,
|
driverName: m.driver.name,
|
||||||
avatarUrl: (m.driver as GetDriverOutputDTO & { avatarUrl?: string }).avatarUrl || null,
|
avatarUrl: (m.driver as any).avatarUrl || null,
|
||||||
rating: null,
|
rating: null,
|
||||||
rank: null,
|
rank: null,
|
||||||
roleBadgeText: 'Admin',
|
roleBadgeText: 'Admin',
|
||||||
@@ -113,7 +113,7 @@ export class LeagueDetailViewDataBuilder {
|
|||||||
.map(m => ({
|
.map(m => ({
|
||||||
driverId: m.driverId,
|
driverId: m.driverId,
|
||||||
driverName: m.driver.name,
|
driverName: m.driver.name,
|
||||||
avatarUrl: (m.driver as GetDriverOutputDTO & { avatarUrl?: string }).avatarUrl || null,
|
avatarUrl: (m.driver as any).avatarUrl || null,
|
||||||
rating: null,
|
rating: null,
|
||||||
rank: null,
|
rank: null,
|
||||||
roleBadgeText: 'Steward',
|
roleBadgeText: 'Steward',
|
||||||
@@ -126,7 +126,7 @@ export class LeagueDetailViewDataBuilder {
|
|||||||
.map(m => ({
|
.map(m => ({
|
||||||
driverId: m.driverId,
|
driverId: m.driverId,
|
||||||
driverName: m.driver.name,
|
driverName: m.driver.name,
|
||||||
avatarUrl: (m.driver as GetDriverOutputDTO & { avatarUrl?: string }).avatarUrl || null,
|
avatarUrl: (m.driver as any).avatarUrl || null,
|
||||||
rating: null,
|
rating: null,
|
||||||
rank: null,
|
rank: null,
|
||||||
roleBadgeText: 'Member',
|
roleBadgeText: 'Member',
|
||||||
@@ -197,6 +197,10 @@ export class LeagueDetailViewDataBuilder {
|
|||||||
main: { price: 0, status: 'available' },
|
main: { price: 0, status: 'available' },
|
||||||
secondary: { price: 0, total: 0, occupied: 0 },
|
secondary: { price: 0, total: 0, occupied: 0 },
|
||||||
},
|
},
|
||||||
|
ownerId: league.ownerId,
|
||||||
|
createdAt: league.createdAt,
|
||||||
|
settings: league.settings,
|
||||||
|
usedSlots: league.usedSlots,
|
||||||
},
|
},
|
||||||
drivers: [],
|
drivers: [],
|
||||||
races: [],
|
races: [],
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ export class LeagueStandingsViewDataBuilder {
|
|||||||
races: standing.races,
|
races: standing.races,
|
||||||
racesFinished: standing.races,
|
racesFinished: standing.races,
|
||||||
racesStarted: standing.races,
|
racesStarted: standing.races,
|
||||||
avgFinish: null, // Not in DTO
|
avgFinish: 0, // Not in DTO
|
||||||
penaltyPoints: 0, // Not in DTO
|
penaltyPoints: 0, // Not in DTO
|
||||||
bonusPoints: 0, // Not in DTO
|
bonusPoints: 0, // Not in DTO
|
||||||
leaderPoints: 0, // Not in DTO
|
leaderPoints: 0, // Not in DTO
|
||||||
nextPoints: 0, // Not in DTO
|
nextPoints: 0, // Not in DTO
|
||||||
currentUserId: null, // Not in DTO
|
currentUserId: '', // Not in DTO
|
||||||
// New fields from Phase 3
|
// New fields from Phase 3
|
||||||
positionChange: standing.positionChange || 0,
|
positionChange: standing.positionChange || 0,
|
||||||
lastRacePoints: standing.lastRacePoints || 0,
|
lastRacePoints: standing.lastRacePoints || 0,
|
||||||
@@ -80,7 +80,7 @@ export class LeagueStandingsViewDataBuilder {
|
|||||||
drivers: driverData,
|
drivers: driverData,
|
||||||
memberships: membershipData,
|
memberships: membershipData,
|
||||||
leagueId,
|
leagueId,
|
||||||
currentDriverId: null, // Would need to get from auth
|
currentDriverId: '', // Would need to get from auth
|
||||||
isAdmin: false, // Would need to check permissions
|
isAdmin: false, // Would need to check permissions
|
||||||
isTeamChampionship: isTeamChampionship,
|
isTeamChampionship: isTeamChampionship,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import type { CompleteOnboardingInputDTO } from '../../../types/generated/CompleteOnboardingInputDTO';
|
import type {
|
||||||
import type { CompleteOnboardingOutputDTO } from '../../../types/generated/CompleteOnboardingOutputDTO';
|
CompleteOnboardingInputDTO,
|
||||||
import type { DriverLeaderboardItemDTO } from '../../../types/generated/DriverLeaderboardItemDTO';
|
CompleteOnboardingOutputDTO,
|
||||||
import type { DriverRegistrationStatusDTO } from '../../../types/generated/DriverRegistrationStatusDTO';
|
DriverLeaderboardItemDTO,
|
||||||
import type { GetDriverOutputDTO } from '../../../types/generated/GetDriverOutputDTO';
|
DriverRegistrationStatusDTO,
|
||||||
import type { GetDriverProfileOutputDTO } from '../../../types/generated/GetDriverProfileOutputDTO';
|
GetDriverOutputDTO,
|
||||||
|
GetDriverProfileOutputDTO
|
||||||
|
} from '../../../types/generated';
|
||||||
import { BaseApiClient } from '../base/BaseApiClient';
|
import { BaseApiClient } from '../base/BaseApiClient';
|
||||||
|
|
||||||
type DriversLeaderboardDto = {
|
type DriversLeaderboardDto = {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import type { MemberPaymentDTO } from '../../../types/generated/MemberPaymentDTO';
|
import type {
|
||||||
import type { MembershipFeeDTO } from '../../../types/generated/MembershipFeeDTO';
|
MemberPaymentDTO,
|
||||||
import type { PaymentDTO } from '../../../types/generated/PaymentDTO';
|
MembershipFeeDTO,
|
||||||
import type { PrizeDTO } from '../../../types/generated/PrizeDTO';
|
PaymentDTO,
|
||||||
import type { TransactionDTO } from '../../../types/generated/TransactionDTO';
|
PrizeDTO,
|
||||||
import type { UpdatePaymentStatusInputDTO } from '../../../types/generated/UpdatePaymentStatusInputDTO';
|
TransactionDTO,
|
||||||
import type { WalletDTO } from '../../../types/generated/WalletDTO';
|
UpdatePaymentStatusInputDTO,
|
||||||
|
WalletDTO
|
||||||
|
} from '../../../types/generated';
|
||||||
import { BaseApiClient } from '../base/BaseApiClient';
|
import { BaseApiClient } from '../base/BaseApiClient';
|
||||||
|
|
||||||
// Define missing types that are not fully generated
|
// Define missing types that are not fully generated
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { DomainError, Service } from '@/lib/contracts/services/Service';
|
|||||||
import { AuthApiClient } from '@/lib/gateways/api/auth/AuthApiClient';
|
import { AuthApiClient } from '@/lib/gateways/api/auth/AuthApiClient';
|
||||||
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
||||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||||
|
import type { ForgotPasswordDTO } from '@/lib/types/generated/ForgotPasswordDTO';
|
||||||
import type { LoginParamsDTO } from '@/lib/types/generated/LoginParamsDTO';
|
import type { LoginParamsDTO } from '@/lib/types/generated/LoginParamsDTO';
|
||||||
import type { ResetPasswordDTO } from '@/lib/types/generated/ResetPasswordDTO';
|
import type { ResetPasswordDTO } from '@/lib/types/generated/ResetPasswordDTO';
|
||||||
import type { SignupParamsDTO } from '@/lib/types/generated/SignupParamsDTO';
|
import type { SignupParamsDTO } from '@/lib/types/generated/SignupParamsDTO';
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ import { Result } from '@/lib/contracts/Result';
|
|||||||
import { DomainError, Service } from '@/lib/contracts/services/Service';
|
import { DomainError, Service } from '@/lib/contracts/services/Service';
|
||||||
import { DriversApiClient } from '@/lib/gateways/api/drivers/DriversApiClient';
|
import { DriversApiClient } from '@/lib/gateways/api/drivers/DriversApiClient';
|
||||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||||
import type { CompleteOnboardingInputDTO } from '@/lib/types/generated/CompleteOnboardingInputDTO';
|
import type {
|
||||||
import type { DriverDTO } from '@/lib/types/generated/DriverDTO';
|
CompleteOnboardingInputDTO,
|
||||||
import type { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';
|
DriverDTO,
|
||||||
import type { GetDriverProfileOutputDTO } from '@/lib/types/generated/GetDriverProfileOutputDTO';
|
GetDriverOutputDTO,
|
||||||
|
GetDriverProfileOutputDTO
|
||||||
|
} from '@/lib/types/generated';
|
||||||
import { CompleteOnboardingViewModel } from '@/lib/view-models/CompleteOnboardingViewModel';
|
import { CompleteOnboardingViewModel } from '@/lib/view-models/CompleteOnboardingViewModel';
|
||||||
import { DriverLeaderboardViewModel } from '@/lib/view-models/DriverLeaderboardViewModel';
|
import { DriverLeaderboardViewModel } from '@/lib/view-models/DriverLeaderboardViewModel';
|
||||||
import { DriverViewModel } from '@/lib/view-models/DriverViewModel';
|
import { DriverViewModel } from '@/lib/view-models/DriverViewModel';
|
||||||
@@ -28,7 +30,11 @@ export class DriverService implements Service {
|
|||||||
} else {
|
} else {
|
||||||
const baseUrl = getWebsiteApiBaseUrl();
|
const baseUrl = getWebsiteApiBaseUrl();
|
||||||
const logger = new ConsoleLogger();
|
const logger = new ConsoleLogger();
|
||||||
const errorReporter = new EnhancedErrorReporter(logger);
|
const errorReporter = new EnhancedErrorReporter(logger, {
|
||||||
|
showUserNotifications: false,
|
||||||
|
logToConsole: true,
|
||||||
|
reportToExternal: false,
|
||||||
|
});
|
||||||
this.apiClient = new DriversApiClient(baseUrl, errorReporter, logger);
|
this.apiClient = new DriversApiClient(baseUrl, errorReporter, logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,13 @@ import { DomainError, Service } from '@/lib/contracts/services/Service';
|
|||||||
import { DriversApiClient } from '@/lib/gateways/api/drivers/DriversApiClient';
|
import { DriversApiClient } from '@/lib/gateways/api/drivers/DriversApiClient';
|
||||||
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
||||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||||
import { CompleteOnboardingInputDTO } from '@/lib/types/generated/CompleteOnboardingInputDTO';
|
import {
|
||||||
import { CompleteOnboardingOutputDTO } from '@/lib/types/generated/CompleteOnboardingOutputDTO';
|
CompleteOnboardingInputDTO,
|
||||||
import { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';
|
CompleteOnboardingOutputDTO,
|
||||||
import { RequestAvatarGenerationInputDTO } from '@/lib/types/generated/RequestAvatarGenerationInputDTO';
|
GetDriverOutputDTO,
|
||||||
import { RequestAvatarGenerationOutputDTO } from '@/lib/types/generated/RequestAvatarGenerationOutputDTO';
|
RequestAvatarGenerationInputDTO,
|
||||||
|
RequestAvatarGenerationOutputDTO
|
||||||
|
} from '@/lib/types/generated';
|
||||||
|
|
||||||
export class OnboardingService implements Service {
|
export class OnboardingService implements Service {
|
||||||
private apiClient: DriversApiClient;
|
private apiClient: DriversApiClient;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Service } from '@/lib/contracts/services/Service';
|
|||||||
import { ProtestsApiClient } from '@/lib/gateways/api/protests/ProtestsApiClient';
|
import { ProtestsApiClient } from '@/lib/gateways/api/protests/ProtestsApiClient';
|
||||||
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
||||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||||
|
import type { ApplyPenaltyCommandDTO } from '@/lib/types/generated/ApplyPenaltyCommandDTO';
|
||||||
import type { RequestProtestDefenseCommandDTO } from '@/lib/types/generated/RequestProtestDefenseCommandDTO';
|
import type { RequestProtestDefenseCommandDTO } from '@/lib/types/generated/RequestProtestDefenseCommandDTO';
|
||||||
import type { ReviewProtestCommandDTO } from '@/lib/types/generated/ReviewProtestCommandDTO';
|
import type { ReviewProtestCommandDTO } from '@/lib/types/generated/ReviewProtestCommandDTO';
|
||||||
import { ProtestDriverViewModel } from '@/lib/view-models/ProtestDriverViewModel';
|
import { ProtestDriverViewModel } from '@/lib/view-models/ProtestDriverViewModel';
|
||||||
|
|||||||
@@ -5,12 +5,15 @@ import { DomainError, Service } from '@/lib/contracts/services/Service';
|
|||||||
import { TeamsApiClient } from '@/lib/gateways/api/teams/TeamsApiClient';
|
import { TeamsApiClient } from '@/lib/gateways/api/teams/TeamsApiClient';
|
||||||
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
||||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||||
import type { CreateTeamOutputDTO } from '@/lib/types/generated/CreateTeamOutputDTO';
|
import type {
|
||||||
import type { GetTeamDetailsOutputDTO } from '@/lib/types/generated/GetTeamDetailsOutputDTO';
|
CreateTeamInputDTO,
|
||||||
import type { GetTeamMembershipOutputDTO } from '@/lib/types/generated/GetTeamMembershipOutputDTO';
|
CreateTeamOutputDTO,
|
||||||
import type { TeamListItemDTO } from '@/lib/types/generated/TeamListItemDTO';
|
GetTeamDetailsOutputDTO,
|
||||||
import type { UpdateTeamInputDTO } from '@/lib/types/generated/UpdateTeamInputDTO';
|
GetTeamMembershipOutputDTO,
|
||||||
import type { UpdateTeamOutputDTO } from '@/lib/types/generated/UpdateTeamOutputDTO';
|
TeamListItemDTO,
|
||||||
|
UpdateTeamInputDTO,
|
||||||
|
UpdateTeamOutputDTO
|
||||||
|
} from '@/lib/types/generated';
|
||||||
import { TeamMemberViewModel } from '@/lib/view-models/TeamMemberViewModel';
|
import { TeamMemberViewModel } from '@/lib/view-models/TeamMemberViewModel';
|
||||||
import { injectable, unmanaged } from 'inversify';
|
import { injectable, unmanaged } from 'inversify';
|
||||||
|
|
||||||
@@ -82,7 +85,11 @@ export class TeamService implements Service {
|
|||||||
try {
|
try {
|
||||||
const result = await this.apiClient.getMembers(teamId);
|
const result = await this.apiClient.getMembers(teamId);
|
||||||
const members = (result as any).members || result;
|
const members = (result as any).members || result;
|
||||||
const viewModels = members.map((member: any) => new TeamMemberViewModel(member, currentDriverId, ownerId));
|
const viewModels = members.map((member: any) => new TeamMemberViewModel({
|
||||||
|
...member,
|
||||||
|
currentUserId: currentDriverId,
|
||||||
|
teamOwnerId: ownerId
|
||||||
|
}));
|
||||||
return Result.ok(viewModels);
|
return Result.ok(viewModels);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
return Result.err({ type: 'unknown', message: (error as Error).message || 'Failed to fetch team members' });
|
return Result.err({ type: 'unknown', message: (error as Error).message || 'Failed to fetch team members' });
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Auto-generated DTO from OpenAPI spec
|
||||||
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface DashboardStatsResponseDTO {
|
||||||
|
totalUsers: number;
|
||||||
|
activeUsers: number;
|
||||||
|
suspendedUsers: number;
|
||||||
|
deletedUsers: number;
|
||||||
|
systemAdmins: number;
|
||||||
|
recentLogins: number;
|
||||||
|
newUsersToday: number;
|
||||||
|
userGrowth: Record<string, unknown>;
|
||||||
|
label: string;
|
||||||
|
value: number;
|
||||||
|
color: string;
|
||||||
|
roleDistribution: Record<string, unknown>;
|
||||||
|
statusDistribution: Record<string, unknown>;
|
||||||
|
active: number;
|
||||||
|
suspended: number;
|
||||||
|
deleted: number;
|
||||||
|
activityTimeline: Record<string, unknown>;
|
||||||
|
date: string;
|
||||||
|
newUsers: number;
|
||||||
|
logins: number;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Auto-generated DTO from OpenAPI spec
|
* Auto-generated DTO from OpenAPI spec
|
||||||
* Spec SHA256: 959bfa650bb99dcba5d135d2d4f612f517af2cb62b0230c9349df5466066fe85
|
* Spec SHA256: e65b5e91cfaadaab01555afb00df2136b4a9968b09b0a07f814cd999b0464ed8
|
||||||
* This file is generated by scripts/generate-api-types.ts
|
* This file is generated by scripts/generate-api-types.ts
|
||||||
* Do not edit manually - regenerate using: npm run api:generate-types
|
* Do not edit manually - regenerate using: npm run api:generate-types
|
||||||
*/
|
*/
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user