From 61f675d991ab75d19f911d9d38c45e8dbc590f3b Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Thu, 18 Dec 2025 14:04:31 +0100 Subject: [PATCH] view models --- adapters/logging/ConsoleErrorReporter.ts | 8 ++++++ adapters/logging/ConsoleLogger.ts | 22 ++++++++++------ apps/website/lib/api/base/BaseApiClient.ts | 15 +++++++++-- .../logging/ConsoleErrorReporter.ts | 8 ++++++ .../infrastructure/logging/ConsoleLogger.ts | 26 +++++++++++++++++++ apps/website/lib/interfaces/ErrorReporter.ts | 3 +++ apps/website/lib/interfaces/Logger.ts | 6 +++++ apps/website/lib/services/ServiceFactory.ts | 23 +++++++++------- core/shared/application/ErrorReporter.ts | 3 +++ 9 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 adapters/logging/ConsoleErrorReporter.ts create mode 100644 apps/website/lib/infrastructure/logging/ConsoleErrorReporter.ts create mode 100644 apps/website/lib/infrastructure/logging/ConsoleLogger.ts create mode 100644 apps/website/lib/interfaces/ErrorReporter.ts create mode 100644 apps/website/lib/interfaces/Logger.ts create mode 100644 core/shared/application/ErrorReporter.ts diff --git a/adapters/logging/ConsoleErrorReporter.ts b/adapters/logging/ConsoleErrorReporter.ts new file mode 100644 index 000000000..648ed1d6d --- /dev/null +++ b/adapters/logging/ConsoleErrorReporter.ts @@ -0,0 +1,8 @@ +import { ErrorReporter } from "@core/shared/application"; + +export class ConsoleErrorReporter implements ErrorReporter { + report(error: Error, context?: unknown): void { + const timestamp = new Date().toISOString(); + console.error(`[${timestamp}] Error reported:`, error.message, { error, context }); + } +} \ No newline at end of file diff --git a/adapters/logging/ConsoleLogger.ts b/adapters/logging/ConsoleLogger.ts index a08e193c4..7ffc0f984 100644 --- a/adapters/logging/ConsoleLogger.ts +++ b/adapters/logging/ConsoleLogger.ts @@ -1,20 +1,26 @@ import { Logger } from "@core/shared/application"; export class ConsoleLogger implements Logger { - debug(message: string, ...args: any[]): void { - console.debug(message, ...args); + private formatMessage(level: string, message: string, context?: unknown): string { + const timestamp = new Date().toISOString(); + const contextStr = context ? ` | ${JSON.stringify(context)}` : ''; + return `[${timestamp}] ${level.toUpperCase()}: ${message}${contextStr}`; } - info(message: string, ...args: any[]): void { - console.info(message, ...args); + debug(message: string, context?: unknown): void { + console.debug(this.formatMessage('debug', message, context)); } - warn(message: string, ...args: any[]): void { - console.warn(message, ...args); + info(message: string, context?: unknown): void { + console.info(this.formatMessage('info', message, context)); } - error(message: string, ...args: any[]): void { - console.error(message, ...args); + warn(message: string, context?: unknown): void { + console.warn(this.formatMessage('warn', message, context)); } + error(message: string, error?: Error, context?: unknown): void { + const errorStr = error ? ` | Error: ${error.message}` : ''; + console.error(this.formatMessage('error', message, context) + errorStr); + } } diff --git a/apps/website/lib/api/base/BaseApiClient.ts b/apps/website/lib/api/base/BaseApiClient.ts index 866999cfd..f7a986973 100644 --- a/apps/website/lib/api/base/BaseApiClient.ts +++ b/apps/website/lib/api/base/BaseApiClient.ts @@ -5,14 +5,23 @@ * error handling, and authentication. */ +import { Logger } from '../../interfaces/Logger'; +import { ErrorReporter } from '../../interfaces/ErrorReporter'; + export class BaseApiClient { private baseUrl: string; + private errorReporter: ErrorReporter; + private logger: Logger; - constructor(baseUrl: string) { + constructor(baseUrl: string, errorReporter: ErrorReporter, logger: Logger) { this.baseUrl = baseUrl; + this.errorReporter = errorReporter; + this.logger = logger; } protected async request(method: string, path: string, data?: object): Promise { + this.logger.info(`${method} ${path}`); + const headers: HeadersInit = { 'Content-Type': 'application/json', }; @@ -36,7 +45,9 @@ export class BaseApiClient { } catch { // Keep default error message } - throw new Error(errorData.message || `API request failed with status ${response.status}`); + const error = new Error(errorData.message || `API request failed with status ${response.status}`); + this.errorReporter.report(error); + throw error; } const text = await response.text(); diff --git a/apps/website/lib/infrastructure/logging/ConsoleErrorReporter.ts b/apps/website/lib/infrastructure/logging/ConsoleErrorReporter.ts new file mode 100644 index 000000000..7a82e611f --- /dev/null +++ b/apps/website/lib/infrastructure/logging/ConsoleErrorReporter.ts @@ -0,0 +1,8 @@ +import { ErrorReporter } from '../../interfaces/ErrorReporter'; + +export class ConsoleErrorReporter implements ErrorReporter { + report(error: Error, context?: unknown): void { + const timestamp = new Date().toISOString(); + console.error(`[${timestamp}] Error reported:`, error.message, { error, context }); + } +} \ No newline at end of file diff --git a/apps/website/lib/infrastructure/logging/ConsoleLogger.ts b/apps/website/lib/infrastructure/logging/ConsoleLogger.ts new file mode 100644 index 000000000..c601a5fab --- /dev/null +++ b/apps/website/lib/infrastructure/logging/ConsoleLogger.ts @@ -0,0 +1,26 @@ +import { Logger } from '../../interfaces/Logger'; + +export class ConsoleLogger implements Logger { + private formatMessage(level: string, message: string, context?: unknown): string { + const timestamp = new Date().toISOString(); + const contextStr = context ? ` | ${JSON.stringify(context)}` : ''; + return `[${timestamp}] ${level.toUpperCase()}: ${message}${contextStr}`; + } + + debug(message: string, context?: unknown): void { + console.debug(this.formatMessage('debug', message, context)); + } + + info(message: string, context?: unknown): void { + console.info(this.formatMessage('info', message, context)); + } + + warn(message: string, context?: unknown): void { + console.warn(this.formatMessage('warn', message, context)); + } + + error(message: string, error?: Error, context?: unknown): void { + const errorStr = error ? ` | Error: ${error.message}` : ''; + console.error(this.formatMessage('error', message, context) + errorStr); + } +} \ No newline at end of file diff --git a/apps/website/lib/interfaces/ErrorReporter.ts b/apps/website/lib/interfaces/ErrorReporter.ts new file mode 100644 index 000000000..f42ee91bd --- /dev/null +++ b/apps/website/lib/interfaces/ErrorReporter.ts @@ -0,0 +1,3 @@ +export interface ErrorReporter { + report(error: Error, context?: unknown): void; +} \ No newline at end of file diff --git a/apps/website/lib/interfaces/Logger.ts b/apps/website/lib/interfaces/Logger.ts new file mode 100644 index 000000000..f5777d454 --- /dev/null +++ b/apps/website/lib/interfaces/Logger.ts @@ -0,0 +1,6 @@ +export interface Logger { + debug(message: string, context?: unknown): void; + info(message: string, context?: unknown): void; + warn(message: string, context?: unknown): void; + error(message: string, error?: Error, context?: unknown): void; +} \ No newline at end of file diff --git a/apps/website/lib/services/ServiceFactory.ts b/apps/website/lib/services/ServiceFactory.ts index 3222c91ea..5d45f326d 100644 --- a/apps/website/lib/services/ServiceFactory.ts +++ b/apps/website/lib/services/ServiceFactory.ts @@ -7,6 +7,8 @@ import { PaymentsApiClient } from '../api/payments/PaymentsApiClient'; import { AuthApiClient } from '../api/auth/AuthApiClient'; import { AnalyticsApiClient } from '../api/analytics/AnalyticsApiClient'; import { MediaApiClient } from '../api/media/MediaApiClient'; +import { ConsoleErrorReporter } from '../infrastructure/logging/ConsoleErrorReporter'; +import { ConsoleLogger } from '../infrastructure/logging/ConsoleLogger'; // Services import { RaceService } from './races/RaceService'; @@ -37,6 +39,9 @@ import { SessionService } from './auth/SessionService'; * Services now directly instantiate View Models instead of using Presenters. */ export class ServiceFactory { + private readonly errorReporter = new ConsoleErrorReporter(); + private readonly logger = new ConsoleLogger(); + private readonly apiClients: { races: RacesApiClient; drivers: DriversApiClient; @@ -52,15 +57,15 @@ export class ServiceFactory { constructor(baseUrl: string) { // Initialize API clients this.apiClients = { - races: new RacesApiClient(baseUrl), - drivers: new DriversApiClient(baseUrl), - teams: new TeamsApiClient(baseUrl), - leagues: new LeaguesApiClient(baseUrl), - sponsors: new SponsorsApiClient(baseUrl), - payments: new PaymentsApiClient(baseUrl), - auth: new AuthApiClient(baseUrl), - analytics: new AnalyticsApiClient(baseUrl), - media: new MediaApiClient(baseUrl), + races: new RacesApiClient(baseUrl, this.errorReporter, this.logger), + drivers: new DriversApiClient(baseUrl, this.errorReporter, this.logger), + teams: new TeamsApiClient(baseUrl, this.errorReporter, this.logger), + leagues: new LeaguesApiClient(baseUrl, this.errorReporter, this.logger), + sponsors: new SponsorsApiClient(baseUrl, this.errorReporter, this.logger), + payments: new PaymentsApiClient(baseUrl, this.errorReporter, this.logger), + auth: new AuthApiClient(baseUrl, this.errorReporter, this.logger), + analytics: new AnalyticsApiClient(baseUrl, this.errorReporter, this.logger), + media: new MediaApiClient(baseUrl, this.errorReporter, this.logger), }; } diff --git a/core/shared/application/ErrorReporter.ts b/core/shared/application/ErrorReporter.ts new file mode 100644 index 000000000..f42ee91bd --- /dev/null +++ b/core/shared/application/ErrorReporter.ts @@ -0,0 +1,3 @@ +export interface ErrorReporter { + report(error: Error, context?: unknown): void; +} \ No newline at end of file