website refactor
This commit is contained in:
@@ -11,20 +11,14 @@ import { PresentationError, mapToPresentationError } from '@/lib/contracts/page-
|
||||
* Server-side composition for admin users page.
|
||||
* Fetches user list from API and transforms to ViewData.
|
||||
*/
|
||||
export class AdminUsersPageQuery implements PageQuery<AdminUsersViewData, { search?: string; role?: string; status?: string; page?: number; limit?: number }> {
|
||||
async execute(query: { search?: string; role?: string; status?: string; page?: number; limit?: number }): Promise<Result<AdminUsersViewData, PresentationError>> {
|
||||
export class AdminUsersPageQuery implements PageQuery<AdminUsersViewData, void> {
|
||||
async execute(): Promise<Result<AdminUsersViewData, PresentationError>> {
|
||||
try {
|
||||
// Manual construction: Service creates its own dependencies
|
||||
const adminService = new AdminService();
|
||||
|
||||
// Fetch user list via service
|
||||
const apiDtoResult = await adminService.listUsers({
|
||||
search: query.search,
|
||||
role: query.role,
|
||||
status: query.status,
|
||||
page: query.page || 1,
|
||||
limit: query.limit || 50,
|
||||
});
|
||||
const apiDtoResult = await adminService.listUsers();
|
||||
|
||||
if (apiDtoResult.isErr()) {
|
||||
return Result.err(mapToPresentationError(apiDtoResult.getError()));
|
||||
@@ -46,8 +40,8 @@ export class AdminUsersPageQuery implements PageQuery<AdminUsersViewData, { sear
|
||||
}
|
||||
|
||||
// Static method to avoid object construction in server code
|
||||
static async execute(query: { search?: string; role?: string; status?: string; page?: number; limit?: number }): Promise<Result<AdminUsersViewData, PresentationError>> {
|
||||
static async execute(): Promise<Result<AdminUsersViewData, PresentationError>> {
|
||||
const queryInstance = new AdminUsersPageQuery();
|
||||
return queryInstance.execute(query);
|
||||
return queryInstance.execute();
|
||||
}
|
||||
}
|
||||
38
apps/website/lib/page-queries/SponsorDashboardPageQuery.ts
Normal file
38
apps/website/lib/page-queries/SponsorDashboardPageQuery.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
import { PageQuery } from '@/lib/contracts/page-queries/PageQuery';
|
||||
import { SponsorService } from '@/lib/services/sponsors/SponsorService';
|
||||
import { SponsorDashboardViewDataBuilder } from '@/lib/builders/view-data/SponsorDashboardViewDataBuilder';
|
||||
import type { SponsorDashboardViewData } from '@/lib/view-data/SponsorDashboardViewData';
|
||||
|
||||
/**
|
||||
* Sponsor Dashboard Page Query
|
||||
*
|
||||
* Composes data for the sponsor dashboard page.
|
||||
* Maps domain errors to presentation errors.
|
||||
*/
|
||||
export class SponsorDashboardPageQuery implements PageQuery<SponsorDashboardViewData, string> {
|
||||
async execute(sponsorId: string): Promise<Result<SponsorDashboardViewData, string>> {
|
||||
const service = new SponsorService();
|
||||
|
||||
const dashboardResult = await service.getSponsorDashboard(sponsorId);
|
||||
if (dashboardResult.isErr()) {
|
||||
return Result.err(this.mapToPresentationError(dashboardResult.getError()));
|
||||
}
|
||||
|
||||
const dto = dashboardResult.unwrap();
|
||||
const viewData = SponsorDashboardViewDataBuilder.build(dto);
|
||||
|
||||
return Result.ok(viewData);
|
||||
}
|
||||
|
||||
private mapToPresentationError(domainError: { type: string }): string {
|
||||
switch (domainError.type) {
|
||||
case 'notFound':
|
||||
return 'Dashboard not found';
|
||||
case 'notImplemented':
|
||||
return 'Dashboard feature not yet implemented';
|
||||
default:
|
||||
return 'Failed to load dashboard';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,25 @@
|
||||
import type { PageQueryResult } from '@/lib/contracts/page-queries/PageQueryResult';
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
import { DriverProfilePageService } from '@/lib/services/drivers/DriverProfilePageService';
|
||||
import { DriverProfileViewModelBuilder } from '@/lib/builders/view-models/DriverProfileViewModelBuilder';
|
||||
import type { DriverProfileViewModel } from '@/lib/view-models/DriverProfileViewModel';
|
||||
import { DriverProfileViewDataBuilder } from '@/lib/builders/view-data/DriverProfileViewDataBuilder';
|
||||
import type { DriverProfileViewData } from '@/lib/types/view-data/DriverProfileViewData';
|
||||
|
||||
/**
|
||||
* DriverProfilePageQuery
|
||||
*
|
||||
* Server-side data fetcher for the driver profile page.
|
||||
* Returns a discriminated union with all possible page states.
|
||||
* Uses Service for data access and ViewModelBuilder for transformation.
|
||||
* Returns Result<ViewData, PresentationError>
|
||||
* Uses Service for data access and ViewDataBuilder for transformation.
|
||||
*/
|
||||
export class DriverProfilePageQuery {
|
||||
/**
|
||||
* Execute the driver profile page query
|
||||
*
|
||||
* @param driverId - The driver ID to fetch profile for
|
||||
* @returns PageQueryResult with discriminated union of states
|
||||
* @returns Result with ViewData or error
|
||||
*/
|
||||
static async execute(driverId: string | null): Promise<PageQueryResult<DriverProfileViewModel>> {
|
||||
// Handle missing driver ID
|
||||
static async execute(driverId: string | null): Promise<Result<DriverProfileViewData, string>> {
|
||||
if (!driverId) {
|
||||
return { status: 'notFound' };
|
||||
return Result.err('NotFound');
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -31,26 +30,26 @@ export class DriverProfilePageQuery {
|
||||
|
||||
if (result.isErr()) {
|
||||
const error = result.getError();
|
||||
|
||||
|
||||
if (error === 'notFound') {
|
||||
return { status: 'notFound' };
|
||||
return Result.err('NotFound');
|
||||
}
|
||||
|
||||
|
||||
if (error === 'unauthorized') {
|
||||
return { status: 'error', errorId: 'UNAUTHORIZED' };
|
||||
return Result.err('Unauthorized');
|
||||
}
|
||||
|
||||
return { status: 'error', errorId: 'DRIVER_PROFILE_FETCH_FAILED' };
|
||||
|
||||
return Result.err('Error');
|
||||
}
|
||||
|
||||
// Build ViewModel from DTO
|
||||
// Build ViewData from DTO
|
||||
const dto = result.unwrap();
|
||||
const viewModel = DriverProfileViewModelBuilder.build(dto);
|
||||
return { status: 'ok', dto: viewModel };
|
||||
const viewData = DriverProfileViewDataBuilder.build(dto);
|
||||
return Result.ok(viewData);
|
||||
|
||||
} catch (error) {
|
||||
console.error('DriverProfilePageQuery failed:', error);
|
||||
return { status: 'error', errorId: 'DRIVER_PROFILE_FETCH_FAILED' };
|
||||
return Result.err('Error');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
import type { PageQueryResult } from '@/lib/contracts/page-queries/PageQueryResult';
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
import { DriversPageService } from '@/lib/services/drivers/DriversPageService';
|
||||
import { DriversViewModelBuilder } from '@/lib/builders/view-models/DriversViewModelBuilder';
|
||||
import type { DriverLeaderboardViewModel } from '@/lib/view-models/DriverLeaderboardViewModel';
|
||||
import { DriversViewDataBuilder } from '@/lib/builders/view-data/DriversViewDataBuilder';
|
||||
import type { DriversViewData } from '@/lib/types/view-data/DriversViewData';
|
||||
|
||||
/**
|
||||
* DriversPageQuery
|
||||
*
|
||||
* Server-side data fetcher for the drivers listing page.
|
||||
* Returns a discriminated union with all possible page states.
|
||||
* Uses Service for data access and ViewModelBuilder for transformation.
|
||||
* Returns Result<ViewData, PresentationError>
|
||||
* Uses Service for data access and ViewDataBuilder for transformation.
|
||||
*/
|
||||
export class DriversPageQuery {
|
||||
/**
|
||||
* Execute the drivers page query
|
||||
*
|
||||
* @returns PageQueryResult with discriminated union of states
|
||||
* @returns Result with ViewData or error
|
||||
*/
|
||||
static async execute(): Promise<PageQueryResult<DriverLeaderboardViewModel>> {
|
||||
static async execute(): Promise<Result<DriversViewData, string>> {
|
||||
try {
|
||||
// Manual wiring: construct dependencies explicitly
|
||||
const service = new DriversPageService();
|
||||
@@ -25,22 +25,22 @@ export class DriversPageQuery {
|
||||
|
||||
if (result.isErr()) {
|
||||
const error = result.getError();
|
||||
|
||||
|
||||
if (error === 'notFound') {
|
||||
return { status: 'notFound' };
|
||||
return Result.err('NotFound');
|
||||
}
|
||||
|
||||
return { status: 'error', errorId: 'DRIVERS_FETCH_FAILED' };
|
||||
|
||||
return Result.err('Error');
|
||||
}
|
||||
|
||||
// Build ViewModel from DTO
|
||||
// Build ViewData from DTO
|
||||
const dto = result.unwrap();
|
||||
const viewModel = DriversViewModelBuilder.build(dto);
|
||||
return { status: 'ok', dto: viewModel };
|
||||
const viewData = DriversViewDataBuilder.build(dto);
|
||||
return Result.ok(viewData);
|
||||
|
||||
} catch (error) {
|
||||
console.error('DriversPageQuery failed:', error);
|
||||
return { status: 'error', errorId: 'DRIVERS_FETCH_FAILED' };
|
||||
return Result.err('Error');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,7 @@ import { PageQuery } from '@/lib/contracts/page-queries/PageQuery';
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
import { LeaguesViewDataBuilder } from '@/lib/builders/view-data/LeaguesViewDataBuilder';
|
||||
import type { LeaguesViewData } from '@/lib/view-data/LeaguesViewData';
|
||||
import { LeaguesApiClient } from '@/lib/api/leagues/LeaguesApiClient';
|
||||
import { DomainError } from '@/lib/contracts/services/Service';
|
||||
import { ConsoleErrorReporter } from '@/lib/infrastructure/logging/ConsoleErrorReporter';
|
||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||
import { LeagueService } from '@/lib/services/leagues/LeagueService';
|
||||
|
||||
/**
|
||||
* Leagues page query
|
||||
@@ -14,40 +11,30 @@ import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||
*/
|
||||
export class LeaguesPageQuery implements PageQuery<LeaguesViewData, void> {
|
||||
async execute(): Promise<Result<LeaguesViewData, 'notFound' | 'redirect' | 'LEAGUES_FETCH_FAILED' | 'UNKNOWN_ERROR'>> {
|
||||
// Manual wiring: create API client
|
||||
const baseUrl = process.env.NEXT_PUBLIC_API_URL || '';
|
||||
const errorReporter = new ConsoleErrorReporter();
|
||||
const logger = new ConsoleLogger();
|
||||
const apiClient = new LeaguesApiClient(baseUrl, errorReporter, logger);
|
||||
|
||||
// Fetch data using API client
|
||||
try {
|
||||
const apiDto = await apiClient.getAllWithCapacityAndScoring();
|
||||
|
||||
if (!apiDto || !apiDto.leagues) {
|
||||
return Result.err('notFound');
|
||||
}
|
||||
|
||||
// Transform to ViewData using builder
|
||||
const viewData = LeaguesViewDataBuilder.build(apiDto);
|
||||
return Result.ok(viewData);
|
||||
} catch (error) {
|
||||
console.error('LeaguesPageQuery failed:', error);
|
||||
|
||||
if (error instanceof Error) {
|
||||
if (error.message.includes('403') || error.message.includes('401')) {
|
||||
return Result.err('redirect');
|
||||
}
|
||||
if (error.message.includes('404')) {
|
||||
// Manual construction: Service creates its own dependencies
|
||||
const service = new LeagueService();
|
||||
|
||||
// Fetch data using service
|
||||
const result = await service.getAllLeagues();
|
||||
|
||||
if (result.isErr()) {
|
||||
const error = result.getError();
|
||||
switch (error.type) {
|
||||
case 'notFound':
|
||||
return Result.err('notFound');
|
||||
}
|
||||
if (error.message.includes('5') || error.message.includes('server')) {
|
||||
case 'unauthorized':
|
||||
case 'forbidden':
|
||||
return Result.err('redirect');
|
||||
case 'serverError':
|
||||
return Result.err('LEAGUES_FETCH_FAILED');
|
||||
}
|
||||
default:
|
||||
return Result.err('UNKNOWN_ERROR');
|
||||
}
|
||||
|
||||
return Result.err('UNKNOWN_ERROR');
|
||||
}
|
||||
|
||||
// Transform to ViewData using builder
|
||||
const viewData = LeaguesViewDataBuilder.build(result.unwrap());
|
||||
return Result.ok(viewData);
|
||||
}
|
||||
|
||||
// Static method to avoid object construction in server code
|
||||
|
||||
Reference in New Issue
Block a user