Compare commits
3 Commits
046852703f
...
1b0a1f4aee
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b0a1f4aee | |||
| c1750a33dd | |||
| 6749fe326b |
@@ -426,6 +426,16 @@
|
||||
"no-restricted-syntax": "error"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"apps/website/**/*.test.ts",
|
||||
"apps/website/**/*.test.tsx"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"tests/**/*.ts"
|
||||
|
||||
@@ -107,45 +107,49 @@ export class GetDashboardStatsUseCase {
|
||||
|
||||
// User growth (last 7 days)
|
||||
const userGrowth: DashboardStatsResult['userGrowth'] = [];
|
||||
for (let i = 6; i >= 0; i--) {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - i);
|
||||
const dateStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
||||
|
||||
const count = allUsers.filter((u: AdminUser) => {
|
||||
const userDate = new Date(u.createdAt);
|
||||
return userDate.toDateString() === date.toDateString();
|
||||
}).length;
|
||||
|
||||
userGrowth.push({
|
||||
label: dateStr,
|
||||
value: count,
|
||||
color: 'text-primary-blue',
|
||||
});
|
||||
if (allUsers.length > 0) {
|
||||
for (let i = 6; i >= 0; i--) {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - i);
|
||||
const dateStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
||||
|
||||
const count = allUsers.filter((u: AdminUser) => {
|
||||
const userDate = u.createdAt;
|
||||
return userDate.toDateString() === date.toDateString();
|
||||
}).length;
|
||||
|
||||
userGrowth.push({
|
||||
label: dateStr,
|
||||
value: count,
|
||||
color: 'text-primary-blue',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Activity timeline (last 7 days)
|
||||
const activityTimeline: DashboardStatsResult['activityTimeline'] = [];
|
||||
for (let i = 6; i >= 0; i--) {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - i);
|
||||
const dateStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
||||
|
||||
const newUsers = allUsers.filter((u: AdminUser) => {
|
||||
const userDate = new Date(u.createdAt);
|
||||
return userDate.toDateString() === date.toDateString();
|
||||
}).length;
|
||||
if (allUsers.length > 0) {
|
||||
for (let i = 6; i >= 0; i--) {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - i);
|
||||
const dateStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
||||
|
||||
const newUsers = allUsers.filter((u: AdminUser) => {
|
||||
const userDate = u.createdAt;
|
||||
return userDate.toDateString() === date.toDateString();
|
||||
}).length;
|
||||
|
||||
const logins = allUsers.filter((u: AdminUser) => {
|
||||
const loginDate = u.lastLoginAt;
|
||||
return loginDate && loginDate.toDateString() === date.toDateString();
|
||||
}).length;
|
||||
const logins = allUsers.filter((u: AdminUser) => {
|
||||
const loginDate = u.lastLoginAt;
|
||||
return loginDate && loginDate.toDateString() === date.toDateString();
|
||||
}).length;
|
||||
|
||||
activityTimeline.push({
|
||||
date: dateStr,
|
||||
newUsers,
|
||||
logins,
|
||||
});
|
||||
activityTimeline.push({
|
||||
date: dateStr,
|
||||
newUsers,
|
||||
logins,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const result: DashboardStatsResult = {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
'use server';
|
||||
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
import { ScheduleAdminMutation } from '@/lib/mutations/leagues/ScheduleAdminMutation';
|
||||
import { RacesApiClient } from '@/lib/api/races/RacesApiClient';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
import { RacesApiClient } from '@/lib/gateways/api/races/RacesApiClient';
|
||||
import { ConsoleErrorReporter } from '@/lib/infrastructure/logging/ConsoleErrorReporter';
|
||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||
import { ScheduleAdminMutation } from '@/lib/mutations/leagues/ScheduleAdminMutation';
|
||||
import { routes } from '@/lib/routing/RouteConfig';
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||
export async function publishScheduleAction(leagueId: string, seasonId: string): Promise<Result<void, string>> {
|
||||
@@ -124,16 +124,16 @@ export async function withdrawFromRaceAction(raceId: string, driverId: string, l
|
||||
}
|
||||
|
||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||
export async function navigateToEditRaceAction(raceId: string, leagueId: string): Promise<void> {
|
||||
export async function navigateToEditRaceAction(leagueId: string): Promise<void> {
|
||||
redirect(routes.league.scheduleAdmin(leagueId));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||
export async function navigateToRescheduleRaceAction(raceId: string, leagueId: string): Promise<void> {
|
||||
export async function navigateToRescheduleRaceAction(leagueId: string): Promise<void> {
|
||||
redirect(routes.league.scheduleAdmin(leagueId));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line gridpilot-rules/server-actions-interface
|
||||
export async function navigateToRaceResultsAction(raceId: string, leagueId: string): Promise<void> {
|
||||
export async function navigateToRaceResultsAction(raceId: string): Promise<void> {
|
||||
redirect(routes.race.results(raceId));
|
||||
}
|
||||
|
||||
@@ -32,14 +32,42 @@ export default async function LeagueLayout({
|
||||
leagueId,
|
||||
name: 'Error',
|
||||
description: 'Failed to load league',
|
||||
info: { name: 'Error', membersCount: 0, racesCount: 0, avgSOF: 0, structure: '', scoring: '', createdAt: '' },
|
||||
info: { name: 'Error', description: 'Error', membersCount: 0, racesCount: 0, avgSOF: 0, structure: '', scoring: '', createdAt: '' },
|
||||
runningRaces: [],
|
||||
sponsors: [],
|
||||
ownerSummary: null,
|
||||
adminSummaries: [],
|
||||
stewardSummaries: [],
|
||||
memberSummaries: [],
|
||||
sponsorInsights: null
|
||||
sponsorInsights: null,
|
||||
league: {
|
||||
id: leagueId,
|
||||
name: 'Error',
|
||||
game: 'Unknown',
|
||||
tier: 'starter',
|
||||
season: 'Unknown',
|
||||
description: 'Error',
|
||||
drivers: 0,
|
||||
races: 0,
|
||||
completedRaces: 0,
|
||||
totalImpressions: 0,
|
||||
avgViewsPerRace: 0,
|
||||
engagement: 0,
|
||||
rating: 0,
|
||||
seasonStatus: 'completed',
|
||||
seasonDates: { start: '', end: '' },
|
||||
sponsorSlots: {
|
||||
main: { price: 0, status: 'occupied' },
|
||||
secondary: { price: 0, total: 0, occupied: 0 }
|
||||
}
|
||||
},
|
||||
drivers: [],
|
||||
races: [],
|
||||
seasonProgress: { completedRaces: 0, totalRaces: 0, percentage: 0 },
|
||||
recentResults: [],
|
||||
walletBalance: 0,
|
||||
pendingProtestsCount: 0,
|
||||
pendingJoinRequestsCount: 0
|
||||
}}
|
||||
tabs={[]}
|
||||
>
|
||||
|
||||
@@ -22,22 +22,50 @@ export default async function LeagueSettingsPage({ params }: Props) {
|
||||
}
|
||||
// For serverError, show the template with empty data
|
||||
return <LeagueSettingsTemplate viewData={{
|
||||
leagueId,
|
||||
league: {
|
||||
id: leagueId,
|
||||
name: 'Unknown League',
|
||||
description: 'League information unavailable',
|
||||
visibility: 'private',
|
||||
ownerId: 'unknown',
|
||||
createdAt: '1970-01-01T00:00:00Z',
|
||||
updatedAt: '1970-01-01T00:00:00Z',
|
||||
},
|
||||
config: {
|
||||
maxDrivers: 0,
|
||||
scoringPresetId: 'unknown',
|
||||
allowLateJoin: false,
|
||||
requireApproval: false,
|
||||
basics: {
|
||||
name: 'Unknown League',
|
||||
description: 'League information unavailable',
|
||||
visibility: 'private',
|
||||
gameId: 'unknown',
|
||||
},
|
||||
structure: {
|
||||
mode: 'solo',
|
||||
maxDrivers: 0,
|
||||
},
|
||||
championships: {
|
||||
enableDriverChampionship: true,
|
||||
enableTeamChampionship: false,
|
||||
enableNationsChampionship: false,
|
||||
enableTrophyChampionship: false,
|
||||
},
|
||||
scoring: {
|
||||
patternId: 'unknown',
|
||||
},
|
||||
dropPolicy: {
|
||||
strategy: 'none',
|
||||
},
|
||||
timings: {},
|
||||
stewarding: {
|
||||
decisionMode: 'single_steward',
|
||||
requireDefense: false,
|
||||
defenseTimeLimit: 24,
|
||||
voteTimeLimit: 24,
|
||||
protestDeadlineHours: 24,
|
||||
stewardingClosesHours: 48,
|
||||
notifyAccusedOnProtest: true,
|
||||
notifyOnVoteRequired: true,
|
||||
},
|
||||
},
|
||||
presets: [],
|
||||
owner: null,
|
||||
members: [],
|
||||
}} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ export default async function Page({ params }: Props) {
|
||||
leagueId,
|
||||
currentDriverId: null,
|
||||
isAdmin: false,
|
||||
isTeamChampionship: false,
|
||||
}}
|
||||
/>;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ export default async function LeagueWalletPage({ params }: Props) {
|
||||
formattedPendingPayouts: '$0.00',
|
||||
currency: 'USD',
|
||||
transactions: [],
|
||||
totalWithdrawals: 0,
|
||||
canWithdraw: false,
|
||||
}} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
import { PageWrapper } from '@/components/shared/state/PageWrapper';
|
||||
import { SponsorLeagueDetailPageClient } from '@/client-wrapper/SponsorLeagueDetailPageClient';
|
||||
import { SponsorsApiClient } from '@/lib/api/sponsors/SponsorsApiClient';
|
||||
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||
import { PageWrapper } from '@/components/shared/state/PageWrapper';
|
||||
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
|
||||
import { getWebsiteServerEnv } from '@/lib/config/env';
|
||||
import { SponsorsApiClient } from '@/lib/gateways/api/sponsors/SponsorsApiClient';
|
||||
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||
import { notFound } from 'next/navigation';
|
||||
|
||||
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { PageWrapper } from '@/components/shared/state/PageWrapper';
|
||||
import { SponsorLeaguesPageClient } from '@/client-wrapper/SponsorLeaguesPageClient';
|
||||
import { SponsorsApiClient } from '@/lib/api/sponsors/SponsorsApiClient';
|
||||
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||
import { PageWrapper } from '@/components/shared/state/PageWrapper';
|
||||
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
|
||||
import { getWebsiteServerEnv } from '@/lib/config/env';
|
||||
import { SponsorsApiClient } from '@/lib/gateways/api/sponsors/SponsorsApiClient';
|
||||
import { EnhancedErrorReporter } from '@/lib/infrastructure/EnhancedErrorReporter';
|
||||
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
||||
|
||||
export default async function Page() {
|
||||
// Manual wiring: create dependencies
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { ActionItem } from '@/lib/queries/ActionsPageQuery';
|
||||
import { ActionStatusBadge } from './ActionStatusBadge';
|
||||
import { Table, TableHead, TableBody, TableRow, TableHeader, TableCell } from '@/ui/Table';
|
||||
import { ActionItem } from '@/lib/page-queries/ActionsPageQuery';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/ui/Table';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { ActionStatusBadge } from './ActionStatusBadge';
|
||||
|
||||
interface ActionListProps {
|
||||
actions: ActionItem[];
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
import { useNotifications } from '@/components/notifications/NotificationProvider';
|
||||
import type { NotificationVariant } from '@/components/notifications/notificationTypes';
|
||||
import { useEffectiveDriverId } from "@/hooks/useEffectiveDriverId";
|
||||
import { ApiConnectionMonitor } from '@/lib/api/base/ApiConnectionMonitor';
|
||||
import { CircuitBreakerRegistry } from '@/lib/api/base/RetryHandler';
|
||||
import { ApiConnectionMonitor } from '@/lib/gateways/api/base/ApiConnectionMonitor';
|
||||
import { CircuitBreakerRegistry } from '@/lib/gateways/api/base/RetryHandler';
|
||||
import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler';
|
||||
import { Activity, AlertTriangle, ChevronDown, ChevronUp, MessageSquare, Wrench, X } from 'lucide-react';
|
||||
import { ChevronUp, Wrench, X } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
// Import our new components
|
||||
@@ -15,8 +15,8 @@ import { Badge } from '@/ui/Badge';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { IconButton } from '@/ui/IconButton';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Toolbar } from '@/ui/Toolbar';
|
||||
import { APIStatusSection } from './sections/APIStatusSection';
|
||||
import { NotificationSendSection } from './sections/NotificationSendSection';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import React, { Component, ReactNode, useState } from 'react';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { connectionMonitor } from '@/lib/api/base/ApiConnectionMonitor';
|
||||
import { ErrorDisplay } from '@/components/shared/ErrorDisplay';
|
||||
import { DevErrorPanel } from '@/components/shared/DevErrorPanel';
|
||||
import { ErrorDisplay } from '@/components/shared/ErrorDisplay';
|
||||
import { connectionMonitor } from '@/lib/gateways/api/base/ApiConnectionMonitor';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { Component, ReactNode, useState } from 'react';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import React, { Component, ReactNode, ErrorInfo, useState, version } from 'react';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler';
|
||||
import { DevErrorPanel } from '@/components/shared/DevErrorPanel';
|
||||
import { ErrorDisplay } from '@/components/shared/ErrorDisplay';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { getGlobalErrorHandler } from '@/lib/infrastructure/GlobalErrorHandler';
|
||||
import React, { Component, ErrorInfo, ReactNode, useState, version } from 'react';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { getErrorSeverity, isConnectivityError, isRetryable, parseApiError } from '@/lib/utils/errorUtils';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
@@ -9,15 +9,15 @@ import { Stack } from '@/ui/Stack';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import {
|
||||
AlertCircle,
|
||||
AlertTriangle,
|
||||
Bug,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
Info,
|
||||
RefreshCw,
|
||||
Wifi,
|
||||
X
|
||||
AlertCircle,
|
||||
AlertTriangle,
|
||||
Bug,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
Info,
|
||||
RefreshCw,
|
||||
Wifi,
|
||||
X
|
||||
} from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ErrorDisplay as UiErrorDisplay } from '@/components/shared/ErrorDisplay';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
|
||||
interface ErrorDisplayProps {
|
||||
error: ApiError;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNotifications } from '@/components/notifications/NotificationProvider';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { connectionMonitor } from '@/lib/api/base/ApiConnectionMonitor';
|
||||
import { connectionMonitor } from '@/lib/gateways/api/base/ApiConnectionMonitor';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
/**
|
||||
* Integration component that listens for API errors and shows notifications
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { connectionMonitor } from '@/lib/api/base/ApiConnectionMonitor';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { CircuitBreakerRegistry } from '@/lib/api/base/RetryHandler';
|
||||
import { connectionMonitor } from '@/lib/gateways/api/base/ApiConnectionMonitor';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { CircuitBreakerRegistry } from '@/lib/gateways/api/base/RetryHandler';
|
||||
import { Badge } from '@/ui/Badge';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Button } from '@/ui/Button';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Button } from '@/ui/Button';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { EmptyState } from '@/ui/EmptyState';
|
||||
import { ErrorDisplay } from '@/components/shared/ErrorDisplay';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { EmptyState } from '@/ui/EmptyState';
|
||||
import { LoadingWrapper } from '@/ui/LoadingWrapper';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { Inbox, List, LucideIcon } from 'lucide-react';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { SESSION_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { enhanceQueryResult } from '@/lib/di/hooks/useReactQueryWithApiError';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { SESSION_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { SessionViewModel } from '@/lib/view-models/SessionViewModel';
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useCurrentSession(
|
||||
options?: Omit<UseQueryOptions<SessionViewModel | null, ApiError>, 'queryKey' | 'queryFn'> & { initialData?: SessionViewModel | null }
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { AUTH_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { ForgotPasswordDTO } from '@/lib/types/generated/ForgotPasswordDTO';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useForgotPassword(
|
||||
options?: Omit<UseMutationOptions<{ message: string; magicLink?: string }, ApiError, ForgotPasswordDTO>, 'mutationFn'>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { AUTH_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { SessionViewModel } from '@/lib/view-models/SessionViewModel';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { LoginParamsDTO } from '@/lib/types/generated/LoginParamsDTO';
|
||||
import { SessionViewModel } from '@/lib/view-models/SessionViewModel';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useLogin(
|
||||
options?: Omit<UseMutationOptions<SessionViewModel, ApiError, LoginParamsDTO>, 'mutationFn'>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { AUTH_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useLogout(
|
||||
options?: Omit<UseMutationOptions<void, ApiError, void>, 'mutationFn'>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { AUTH_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { ResetPasswordDTO } from '@/lib/types/generated/ResetPasswordDTO';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useResetPassword(
|
||||
options?: Omit<UseMutationOptions<{ message: string }, ApiError, ResetPasswordDTO>, 'mutationFn'>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { AUTH_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { SessionViewModel } from '@/lib/view-models/SessionViewModel';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { SignupParamsDTO } from '@/lib/types/generated/SignupParamsDTO';
|
||||
import { SessionViewModel } from '@/lib/view-models/SessionViewModel';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useSignup(
|
||||
options?: Omit<UseMutationOptions<SessionViewModel, ApiError, SignupParamsDTO>, 'mutationFn'>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use client';
|
||||
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { DRIVER_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { CompleteOnboardingInputDTO } from '@/lib/types/generated/CompleteOnboardingInputDTO';
|
||||
import { CompleteOnboardingViewModel } from '@/lib/view-models/CompleteOnboardingViewModel';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useCreateDriver(
|
||||
options?: Omit<UseMutationOptions<CompleteOnboardingViewModel, ApiError, CompleteOnboardingInputDTO>, 'mutationFn'>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { DRIVER_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { enhanceQueryResult } from '@/lib/di/hooks/useReactQueryWithApiError';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import type { DriverDTO } from '@/lib/types/generated/DriverDTO';
|
||||
import { useAuth } from '@/components/auth/AuthContext';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { enhanceQueryResult } from '@/lib/di/hooks/useReactQueryWithApiError';
|
||||
import { DRIVER_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { DriverDTO } from '@/lib/types/generated/DriverDTO';
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useCurrentDriver(
|
||||
options?: Omit<UseQueryOptions<DriverDTO | null, ApiError>, 'queryKey' | 'queryFn'>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { DRIVER_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { enhanceQueryResult } from '@/lib/di/hooks/useReactQueryWithApiError';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { DRIVER_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { DriverProfileViewModel, type DriverProfileViewModelData } from '@/lib/view-models/DriverProfileViewModel';
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useDriverProfile(
|
||||
driverId: string,
|
||||
@@ -19,7 +19,7 @@ export function useDriverProfile(
|
||||
const error = result.getError();
|
||||
throw new ApiError(error.message, 'SERVER_ERROR', { timestamp: new globalThis.Date().toISOString() });
|
||||
}
|
||||
return new DriverProfileViewModel(result.unwrap() as unknown as DriverProfileViewModelData);
|
||||
return new DriverProfileViewModel(result.unwrap());
|
||||
},
|
||||
enabled: !!driverId,
|
||||
...options,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { DRIVER_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { enhanceQueryResult } from '@/lib/di/hooks/useReactQueryWithApiError';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { DRIVER_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { GetDriverOutputDTO } from '@/lib/types/generated/GetDriverOutputDTO';
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useFindDriverById(
|
||||
driverId: string,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { DriverProfileViewModelBuilder } from '@/lib/builders/view-models/DriverProfileViewModelBuilder';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { DRIVER_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { DriverProfileViewModel } from '@/lib/view-models/DriverProfileViewModel';
|
||||
import { DriverProfileViewModelBuilder } from '@/lib/builders/view-models/DriverProfileViewModelBuilder';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { GetDriverProfileOutputDTO } from '@/lib/types/generated/GetDriverProfileOutputDTO';
|
||||
import { DriverProfileViewModel } from '@/lib/view-models/DriverProfileViewModel';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useUpdateDriverProfile(
|
||||
options?: Omit<UseMutationOptions<DriverProfileViewModel, ApiError, { bio?: string; country?: string }>, 'mutationFn'>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { LEAGUE_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { CreateLeagueInputDTO } from '@/lib/types/generated/CreateLeagueInputDTO';
|
||||
import { CreateLeagueOutputDTO } from '@/lib/types/generated/CreateLeagueOutputDTO';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export interface CreateLeagueInput {
|
||||
name: string;
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { LEAGUE_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import type { LeagueWithCapacityAndScoringDTO } from '@/lib/types/generated/LeagueWithCapacityAndScoringDTO';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { LeagueMembershipsDTO } from '@/lib/types/generated/LeagueMembershipsDTO';
|
||||
import type { AllLeaguesWithCapacityAndScoringDTO } from '@/lib/types/AllLeaguesWithCapacityAndScoringDTO';
|
||||
import type { LeagueWithCapacityAndScoringDTO } from '@/lib/types/generated/LeagueWithCapacityAndScoringDTO';
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
|
||||
interface UseLeagueDetailOptions {
|
||||
leagueId: string;
|
||||
queryOptions?: UseQueryOptions<LeagueWithCapacityAndScoringDTO, ApiError>;
|
||||
}
|
||||
|
||||
interface UseLeagueMembershipsOptions {
|
||||
leagueId: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { LEAGUE_MEMBERSHIP_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
export function useLeagueMembershipMutation() {
|
||||
const leagueMembershipService = useInject(LEAGUE_MEMBERSHIP_SERVICE_TOKEN);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useMutation, useQuery, UseMutationOptions, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { LEAGUE_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import type { LeagueRosterMemberDTO } from '@/lib/types/generated/LeagueRosterMemberDTO';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { LeagueRosterJoinRequestDTO } from '@/lib/types/generated/LeagueRosterJoinRequestDTO';
|
||||
import type { LeagueRosterMemberDTO } from '@/lib/types/generated/LeagueRosterMemberDTO';
|
||||
import { useMutation, UseMutationOptions, useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
|
||||
interface UpdateMemberRoleInput {
|
||||
leagueId: string;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { LEAGUE_SETTINGS_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { enhanceQueryResult } from '@/lib/di/hooks/useReactQueryWithApiError';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { LEAGUE_SETTINGS_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { LeagueSettingsViewModel } from '@/lib/view-models/LeagueSettingsViewModel';
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useLeagueSettings(
|
||||
leagueId: string,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { LEAGUE_STEWARDING_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { enhanceQueryResult } from '@/lib/di/hooks/useReactQueryWithApiError';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { LEAGUE_STEWARDING_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
export function useProtestDetail(leagueId: string, protestId: string, enabled: boolean = true) {
|
||||
const leagueStewardingService = useInject(LEAGUE_STEWARDING_SERVICE_TOKEN);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { RACE_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { FileProtestCommandDTO } from '@/lib/types/generated/FileProtestCommandDTO';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useFileProtest(
|
||||
options?: Omit<UseMutationOptions<void, ApiError, FileProtestCommandDTO>, 'mutationFn'>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { RACE_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
interface RegisterForRaceParams {
|
||||
raceId: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { RACE_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
interface WithdrawFromRaceParams {
|
||||
raceId: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { TEAM_JOIN_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useApproveJoinRequest(options?: Omit<UseMutationOptions<void, ApiError, void>, 'mutationFn'>) {
|
||||
const teamJoinService = useInject(TEAM_JOIN_SERVICE_TOKEN);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { TEAM_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { CreateTeamInputDTO } from '@/lib/types/generated/CreateTeamInputDTO';
|
||||
import type { CreateTeamOutputDTO } from '@/lib/types/generated/CreateTeamOutputDTO';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useCreateTeam(options?: UseMutationOptions<CreateTeamOutputDTO, ApiError, CreateTeamInputDTO>) {
|
||||
const teamService = useInject(TEAM_SERVICE_TOKEN);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
|
||||
interface JoinTeamParams {
|
||||
teamId: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
|
||||
interface LeaveTeamParams {
|
||||
teamId: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { TEAM_JOIN_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useRejectJoinRequest(options?: Omit<UseMutationOptions<void, ApiError, void>, 'mutationFn'>) {
|
||||
const teamJoinService = useInject(TEAM_JOIN_SERVICE_TOKEN);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { TEAM_JOIN_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { enhanceQueryResult } from '@/lib/di/hooks/useReactQueryWithApiError';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { TEAM_JOIN_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
export function useTeamJoinRequests(teamId: string, currentUserId: string, isOwner: boolean) {
|
||||
const teamJoinService = useInject(TEAM_JOIN_SERVICE_TOKEN);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { TEAM_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import type { UpdateTeamInputDTO } from '@/lib/types/generated/UpdateTeamInputDTO';
|
||||
import type { UpdateTeamOutputDTO } from '@/lib/types/generated/UpdateTeamOutputDTO';
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useUpdateTeam(options?: UseMutationOptions<UpdateTeamOutputDTO, ApiError, { teamId: string; input: UpdateTeamInputDTO }>) {
|
||||
const teamService = useInject(TEAM_SERVICE_TOKEN);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { useInject } from '@/lib/di/hooks/useInject';
|
||||
import { POLICY_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { enhanceQueryResult } from '@/lib/di/hooks/useReactQueryWithApiError';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import { PolicySnapshotDto } from '@/lib/api/policy/PolicyApiClient';
|
||||
import { POLICY_SERVICE_TOKEN } from '@/lib/di/tokens';
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { PolicySnapshotDto } from '@/lib/gateways/api/policy/PolicyApiClient';
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||
|
||||
export function useCapability(
|
||||
capabilityKey: string,
|
||||
|
||||
@@ -10,6 +10,8 @@ import { Result } from '@/lib/contracts/Result';
|
||||
import { DomainError } from '@/lib/contracts/services/Service';
|
||||
import { MediaBinaryDTO } from '@/lib/types/MediaBinaryDTO';
|
||||
|
||||
// TODO why is this an adapter?
|
||||
|
||||
/**
|
||||
* MediaAdapter
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ApiError } from '@/lib/gateways/api/base/ApiError';
|
||||
import { UseQueryResult } from '@tanstack/react-query';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
|
||||
/**
|
||||
* Converts React-Query error to ApiError for StateContainer compatibility
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
import { ContainerModule } from 'inversify';
|
||||
import { RacesApiClient } from '../../api/races/RacesApiClient';
|
||||
import { DriversApiClient } from '../../api/drivers/DriversApiClient';
|
||||
import { TeamsApiClient } from '../../api/teams/TeamsApiClient';
|
||||
import { LeaguesApiClient } from '../../api/leagues/LeaguesApiClient';
|
||||
import { SponsorsApiClient } from '../../api/sponsors/SponsorsApiClient';
|
||||
import { PaymentsApiClient } from '../../api/payments/PaymentsApiClient';
|
||||
import { WalletsApiClient } from '../../api/wallets/WalletsApiClient';
|
||||
import { AuthApiClient } from '../../api/auth/AuthApiClient';
|
||||
import { AnalyticsApiClient } from '../../api/analytics/AnalyticsApiClient';
|
||||
import { MediaApiClient } from '../../api/media/MediaApiClient';
|
||||
import { DashboardApiClient } from '../../api/dashboard/DashboardApiClient';
|
||||
import { PolicyApiClient } from '../../api/policy/PolicyApiClient';
|
||||
import { ProtestsApiClient } from '../../api/protests/ProtestsApiClient';
|
||||
import { PenaltiesApiClient } from '../../api/penalties/PenaltiesApiClient';
|
||||
import { AnalyticsApiClient } from '../../gateways/api/analytics/AnalyticsApiClient';
|
||||
import { AuthApiClient } from '../../gateways/api/auth/AuthApiClient';
|
||||
import { DashboardApiClient } from '../../gateways/api/dashboard/DashboardApiClient';
|
||||
import { DriversApiClient } from '../../gateways/api/drivers/DriversApiClient';
|
||||
import { LeaguesApiClient } from '../../gateways/api/leagues/LeaguesApiClient';
|
||||
import { MediaApiClient } from '../../gateways/api/media/MediaApiClient';
|
||||
import { PaymentsApiClient } from '../../gateways/api/payments/PaymentsApiClient';
|
||||
import { PenaltiesApiClient } from '../../gateways/api/penalties/PenaltiesApiClient';
|
||||
import { PolicyApiClient } from '../../gateways/api/policy/PolicyApiClient';
|
||||
import { ProtestsApiClient } from '../../gateways/api/protests/ProtestsApiClient';
|
||||
import { RacesApiClient } from '../../gateways/api/races/RacesApiClient';
|
||||
import { SponsorsApiClient } from '../../gateways/api/sponsors/SponsorsApiClient';
|
||||
import { TeamsApiClient } from '../../gateways/api/teams/TeamsApiClient';
|
||||
import { WalletsApiClient } from '../../gateways/api/wallets/WalletsApiClient';
|
||||
|
||||
import {
|
||||
LOGGER_TOKEN,
|
||||
ERROR_REPORTER_TOKEN,
|
||||
CONFIG_TOKEN,
|
||||
LEAGUE_API_CLIENT_TOKEN,
|
||||
DRIVER_API_CLIENT_TOKEN,
|
||||
TEAM_API_CLIENT_TOKEN,
|
||||
RACE_API_CLIENT_TOKEN,
|
||||
SPONSOR_API_CLIENT_TOKEN,
|
||||
PAYMENT_API_CLIENT_TOKEN,
|
||||
WALLET_API_CLIENT_TOKEN,
|
||||
AUTH_API_CLIENT_TOKEN,
|
||||
import {
|
||||
ANALYTICS_API_CLIENT_TOKEN,
|
||||
MEDIA_API_CLIENT_TOKEN,
|
||||
AUTH_API_CLIENT_TOKEN,
|
||||
CONFIG_TOKEN,
|
||||
DASHBOARD_API_CLIENT_TOKEN,
|
||||
DRIVER_API_CLIENT_TOKEN,
|
||||
ERROR_REPORTER_TOKEN,
|
||||
LEAGUE_API_CLIENT_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
MEDIA_API_CLIENT_TOKEN,
|
||||
PAYMENT_API_CLIENT_TOKEN,
|
||||
PENALTY_API_CLIENT_TOKEN,
|
||||
POLICY_API_CLIENT_TOKEN,
|
||||
PROTEST_API_CLIENT_TOKEN,
|
||||
PENALTY_API_CLIENT_TOKEN
|
||||
RACE_API_CLIENT_TOKEN,
|
||||
SPONSOR_API_CLIENT_TOKEN,
|
||||
TEAM_API_CLIENT_TOKEN,
|
||||
WALLET_API_CLIENT_TOKEN
|
||||
} from '../tokens';
|
||||
|
||||
export const ApiModule = new ContainerModule((options) => {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { ErrorReporter } from '../../interfaces/ErrorReporter';
|
||||
import { Logger } from '../../interfaces/Logger';
|
||||
import { AdminApiClient } from './admin/AdminApiClient';
|
||||
import { AnalyticsApiClient } from './analytics/AnalyticsApiClient';
|
||||
import { AuthApiClient } from './auth/AuthApiClient';
|
||||
@@ -13,10 +15,8 @@ import { RacesApiClient } from './races/RacesApiClient';
|
||||
import { SponsorsApiClient } from './sponsors/SponsorsApiClient';
|
||||
import { TeamsApiClient } from './teams/TeamsApiClient';
|
||||
import { WalletsApiClient } from './wallets/WalletsApiClient';
|
||||
import { ErrorReporter } from '../interfaces/ErrorReporter';
|
||||
import { Logger } from '../interfaces/Logger';
|
||||
|
||||
import { ConsoleLogger } from '../infrastructure/logging/ConsoleLogger';
|
||||
import { ConsoleLogger } from '../../infrastructure/logging/ConsoleLogger';
|
||||
|
||||
export class ApiClient {
|
||||
public readonly admin: AdminApiClient;
|
||||
@@ -1,10 +1,10 @@
|
||||
import { GetAnalyticsMetricsOutputDTO } from '../../../types/generated/GetAnalyticsMetricsOutputDTO';
|
||||
import { GetDashboardDataOutputDTO } from '../../../types/generated/GetDashboardDataOutputDTO';
|
||||
import { RecordEngagementInputDTO } from '../../../types/generated/RecordEngagementInputDTO';
|
||||
import { RecordEngagementOutputDTO } from '../../../types/generated/RecordEngagementOutputDTO';
|
||||
import { RecordPageViewInputDTO } from '../../../types/generated/RecordPageViewInputDTO';
|
||||
import { RecordPageViewOutputDTO } from '../../../types/generated/RecordPageViewOutputDTO';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import { RecordPageViewOutputDTO } from '../../types/generated/RecordPageViewOutputDTO';
|
||||
import { RecordEngagementOutputDTO } from '../../types/generated/RecordEngagementOutputDTO';
|
||||
import { GetDashboardDataOutputDTO } from '../../types/generated/GetDashboardDataOutputDTO';
|
||||
import { GetAnalyticsMetricsOutputDTO } from '../../types/generated/GetAnalyticsMetricsOutputDTO';
|
||||
import { RecordPageViewInputDTO } from '../../types/generated/RecordPageViewInputDTO';
|
||||
import { RecordEngagementInputDTO } from '../../types/generated/RecordEngagementInputDTO';
|
||||
|
||||
/**
|
||||
* Analytics API Client
|
||||
@@ -1,9 +1,9 @@
|
||||
import { AuthSessionDTO } from '../../../types/generated/AuthSessionDTO';
|
||||
import { ForgotPasswordDTO } from '../../../types/generated/ForgotPasswordDTO';
|
||||
import { LoginParamsDTO } from '../../../types/generated/LoginParamsDTO';
|
||||
import { ResetPasswordDTO } from '../../../types/generated/ResetPasswordDTO';
|
||||
import { SignupParamsDTO } from '../../../types/generated/SignupParamsDTO';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import { AuthSessionDTO } from '../../types/generated/AuthSessionDTO';
|
||||
import { LoginParamsDTO } from '../../types/generated/LoginParamsDTO';
|
||||
import { SignupParamsDTO } from '../../types/generated/SignupParamsDTO';
|
||||
import { ForgotPasswordDTO } from '../../types/generated/ForgotPasswordDTO';
|
||||
import { ResetPasswordDTO } from '../../types/generated/ResetPasswordDTO';
|
||||
|
||||
/**
|
||||
* Auth API Client
|
||||
@@ -5,12 +5,12 @@
|
||||
* error handling, authentication, retry logic, and circuit breaker.
|
||||
*/
|
||||
|
||||
import { Logger } from '../../interfaces/Logger';
|
||||
import { ErrorReporter } from '../../interfaces/ErrorReporter';
|
||||
import { ApiError, ApiErrorType } from './ApiError';
|
||||
import { RetryHandler, CircuitBreakerRegistry, DEFAULT_RETRY_CONFIG } from './RetryHandler';
|
||||
import { ApiConnectionMonitor } from './ApiConnectionMonitor';
|
||||
import { getGlobalApiLogger } from '@/lib/infrastructure/ApiRequestLogger';
|
||||
import { ErrorReporter } from '../../../interfaces/ErrorReporter';
|
||||
import { Logger } from '../../../interfaces/Logger';
|
||||
import { ApiConnectionMonitor } from './ApiConnectionMonitor';
|
||||
import { ApiError, ApiErrorType } from './ApiError';
|
||||
import { CircuitBreakerRegistry, DEFAULT_RETRY_CONFIG, RetryHandler } from './RetryHandler';
|
||||
|
||||
export interface BaseApiClientOptions {
|
||||
timeout?: number;
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { DashboardOverviewDTO } from '../../../types/generated/DashboardOverviewDTO';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type { DashboardOverviewDTO } from '../../types/generated/DashboardOverviewDTO';
|
||||
|
||||
/**
|
||||
* Dashboard API Client
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { CompleteOnboardingInputDTO } from '../../../types/generated/CompleteOnboardingInputDTO';
|
||||
import type { CompleteOnboardingOutputDTO } from '../../../types/generated/CompleteOnboardingOutputDTO';
|
||||
import type { DriverLeaderboardItemDTO } from '../../../types/generated/DriverLeaderboardItemDTO';
|
||||
import type { DriverRegistrationStatusDTO } from '../../../types/generated/DriverRegistrationStatusDTO';
|
||||
import type { GetDriverOutputDTO } from '../../../types/generated/GetDriverOutputDTO';
|
||||
import type { GetDriverProfileOutputDTO } from '../../../types/generated/GetDriverProfileOutputDTO';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type { CompleteOnboardingInputDTO } from '../../types/generated/CompleteOnboardingInputDTO';
|
||||
import type { CompleteOnboardingOutputDTO } from '../../types/generated/CompleteOnboardingOutputDTO';
|
||||
import type { DriverRegistrationStatusDTO } from '../../types/generated/DriverRegistrationStatusDTO';
|
||||
import type { DriverLeaderboardItemDTO } from '../../types/generated/DriverLeaderboardItemDTO';
|
||||
import type { GetDriverOutputDTO } from '../../types/generated/GetDriverOutputDTO';
|
||||
import type { GetDriverProfileOutputDTO } from '../../types/generated/GetDriverProfileOutputDTO';
|
||||
|
||||
type DriversLeaderboardDto = {
|
||||
drivers: DriverLeaderboardItemDTO[];
|
||||
@@ -1,28 +1,28 @@
|
||||
import type { AllLeaguesWithCapacityAndScoringDTO } from '../../../types/AllLeaguesWithCapacityAndScoringDTO';
|
||||
import type { AllLeaguesWithCapacityDTO } from '../../../types/generated/AllLeaguesWithCapacityDTO';
|
||||
import type { ApproveJoinRequestOutputDTO } from '../../../types/generated/ApproveJoinRequestOutputDTO';
|
||||
import type { CreateLeagueInputDTO } from '../../../types/generated/CreateLeagueInputDTO';
|
||||
import type { CreateLeagueOutputDTO } from '../../../types/generated/CreateLeagueOutputDTO';
|
||||
import type { CreateLeagueScheduleRaceInputDTO } from '../../../types/generated/CreateLeagueScheduleRaceInputDTO';
|
||||
import type { CreateLeagueScheduleRaceOutputDTO } from '../../../types/generated/CreateLeagueScheduleRaceOutputDTO';
|
||||
import type { GetLeagueAdminConfigOutputDTO } from '../../../types/generated/GetLeagueAdminConfigOutputDTO';
|
||||
import type { LeagueMembershipsDTO } from '../../../types/generated/LeagueMembershipsDTO';
|
||||
import type { LeagueRosterJoinRequestDTO } from '../../../types/generated/LeagueRosterJoinRequestDTO';
|
||||
import type { LeagueRosterMemberDTO } from '../../../types/generated/LeagueRosterMemberDTO';
|
||||
import type { LeagueScheduleDTO } from '../../../types/generated/LeagueScheduleDTO';
|
||||
import type { LeagueScheduleRaceMutationSuccessDTO } from '../../../types/generated/LeagueScheduleRaceMutationSuccessDTO';
|
||||
import type { LeagueScoringPresetDTO } from '../../../types/generated/LeagueScoringPresetDTO';
|
||||
import type { LeagueSeasonSchedulePublishOutputDTO } from '../../../types/generated/LeagueSeasonSchedulePublishOutputDTO';
|
||||
import type { LeagueSeasonSummaryDTO } from '../../../types/generated/LeagueSeasonSummaryDTO';
|
||||
import type { LeagueStandingsDTO } from '../../../types/generated/LeagueStandingsDTO';
|
||||
import type { RaceDTO } from '../../../types/generated/RaceDTO';
|
||||
import type { RejectJoinRequestOutputDTO } from '../../../types/generated/RejectJoinRequestOutputDTO';
|
||||
import type { RemoveLeagueMemberOutputDTO } from '../../../types/generated/RemoveLeagueMemberOutputDTO';
|
||||
import type { SponsorshipDetailDTO } from '../../../types/generated/SponsorshipDetailDTO';
|
||||
import type { TotalLeaguesDTO } from '../../../types/generated/TotalLeaguesDTO';
|
||||
import type { UpdateLeagueMemberRoleOutputDTO } from '../../../types/generated/UpdateLeagueMemberRoleOutputDTO';
|
||||
import type { UpdateLeagueScheduleRaceInputDTO } from '../../../types/generated/UpdateLeagueScheduleRaceInputDTO';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type { AllLeaguesWithCapacityDTO } from '../../types/generated/AllLeaguesWithCapacityDTO';
|
||||
import type { TotalLeaguesDTO } from '../../types/generated/TotalLeaguesDTO';
|
||||
import type { LeagueStandingsDTO } from '../../types/generated/LeagueStandingsDTO';
|
||||
import type { LeagueScheduleDTO } from '../../types/generated/LeagueScheduleDTO';
|
||||
import type { LeagueMembershipsDTO } from '../../types/generated/LeagueMembershipsDTO';
|
||||
import type { CreateLeagueInputDTO } from '../../types/generated/CreateLeagueInputDTO';
|
||||
import type { CreateLeagueOutputDTO } from '../../types/generated/CreateLeagueOutputDTO';
|
||||
import type { SponsorshipDetailDTO } from '../../types/generated/SponsorshipDetailDTO';
|
||||
import type { RaceDTO } from '../../types/generated/RaceDTO';
|
||||
import type { GetLeagueAdminConfigOutputDTO } from '../../types/generated/GetLeagueAdminConfigOutputDTO';
|
||||
import type { LeagueScoringPresetDTO } from '../../types/generated/LeagueScoringPresetDTO';
|
||||
import type { LeagueSeasonSummaryDTO } from '../../types/generated/LeagueSeasonSummaryDTO';
|
||||
import type { CreateLeagueScheduleRaceInputDTO } from '../../types/generated/CreateLeagueScheduleRaceInputDTO';
|
||||
import type { CreateLeagueScheduleRaceOutputDTO } from '../../types/generated/CreateLeagueScheduleRaceOutputDTO';
|
||||
import type { UpdateLeagueScheduleRaceInputDTO } from '../../types/generated/UpdateLeagueScheduleRaceInputDTO';
|
||||
import type { LeagueScheduleRaceMutationSuccessDTO } from '../../types/generated/LeagueScheduleRaceMutationSuccessDTO';
|
||||
import type { LeagueSeasonSchedulePublishOutputDTO } from '../../types/generated/LeagueSeasonSchedulePublishOutputDTO';
|
||||
import type { LeagueRosterMemberDTO } from '../../types/generated/LeagueRosterMemberDTO';
|
||||
import type { LeagueRosterJoinRequestDTO } from '../../types/generated/LeagueRosterJoinRequestDTO';
|
||||
import type { ApproveJoinRequestOutputDTO } from '../../types/generated/ApproveJoinRequestOutputDTO';
|
||||
import type { RejectJoinRequestOutputDTO } from '../../types/generated/RejectJoinRequestOutputDTO';
|
||||
import type { UpdateLeagueMemberRoleOutputDTO } from '../../types/generated/UpdateLeagueMemberRoleOutputDTO';
|
||||
import type { RemoveLeagueMemberOutputDTO } from '../../types/generated/RemoveLeagueMemberOutputDTO';
|
||||
import type { AllLeaguesWithCapacityAndScoringDTO } from '../../types/AllLeaguesWithCapacityAndScoringDTO';
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null;
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { DeleteMediaOutputDTO } from '../../types/generated/DeleteMediaOutputDTO';
|
||||
import type { GetAvatarOutputDTO } from '../../types/generated/GetAvatarOutputDTO';
|
||||
import type { GetMediaOutputDTO } from '../../types/generated/GetMediaOutputDTO';
|
||||
import type { RequestAvatarGenerationInputDTO } from '../../types/generated/RequestAvatarGenerationInputDTO';
|
||||
import type { RequestAvatarGenerationOutputDTO } from '../../types/generated/RequestAvatarGenerationOutputDTO';
|
||||
import type { UpdateAvatarInputDTO } from '../../types/generated/UpdateAvatarInputDTO';
|
||||
import type { UpdateAvatarOutputDTO } from '../../types/generated/UpdateAvatarOutputDTO';
|
||||
import type { UploadMediaOutputDTO } from '../../types/generated/UploadMediaOutputDTO';
|
||||
import type { ValidateFaceInputDTO } from '../../types/generated/ValidateFaceInputDTO';
|
||||
import type { ValidateFaceOutputDTO } from '../../types/generated/ValidateFaceOutputDTO';
|
||||
import type { DeleteMediaOutputDTO } from '../../../types/generated/DeleteMediaOutputDTO';
|
||||
import type { GetAvatarOutputDTO } from '../../../types/generated/GetAvatarOutputDTO';
|
||||
import type { GetMediaOutputDTO } from '../../../types/generated/GetMediaOutputDTO';
|
||||
import type { RequestAvatarGenerationInputDTO } from '../../../types/generated/RequestAvatarGenerationInputDTO';
|
||||
import type { RequestAvatarGenerationOutputDTO } from '../../../types/generated/RequestAvatarGenerationOutputDTO';
|
||||
import type { UpdateAvatarInputDTO } from '../../../types/generated/UpdateAvatarInputDTO';
|
||||
import type { UpdateAvatarOutputDTO } from '../../../types/generated/UpdateAvatarOutputDTO';
|
||||
import type { UploadMediaOutputDTO } from '../../../types/generated/UploadMediaOutputDTO';
|
||||
import type { ValidateFaceInputDTO } from '../../../types/generated/ValidateFaceInputDTO';
|
||||
import type { ValidateFaceOutputDTO } from '../../../types/generated/ValidateFaceOutputDTO';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
|
||||
/**
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { MemberPaymentDTO } from '../../../types/generated/MemberPaymentDTO';
|
||||
import type { MembershipFeeDTO } from '../../../types/generated/MembershipFeeDTO';
|
||||
import type { PaymentDTO } from '../../../types/generated/PaymentDTO';
|
||||
import type { PrizeDTO } from '../../../types/generated/PrizeDTO';
|
||||
import type { TransactionDTO } from '../../../types/generated/TransactionDTO';
|
||||
import type { UpdatePaymentStatusInputDTO } from '../../../types/generated/UpdatePaymentStatusInputDTO';
|
||||
import type { WalletDTO } from '../../../types/generated/WalletDTO';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type { MembershipFeeDTO } from '../../types/generated/MembershipFeeDTO';
|
||||
import type { MemberPaymentDTO } from '../../types/generated/MemberPaymentDTO';
|
||||
import type { PaymentDTO } from '../../types/generated/PaymentDTO';
|
||||
import type { PrizeDTO } from '../../types/generated/PrizeDTO';
|
||||
import type { TransactionDTO } from '../../types/generated/TransactionDTO';
|
||||
import type { UpdatePaymentStatusInputDTO } from '../../types/generated/UpdatePaymentStatusInputDTO';
|
||||
import type { WalletDTO } from '../../types/generated/WalletDTO';
|
||||
|
||||
// Define missing types that are not fully generated
|
||||
type GetPaymentsOutputDto = { payments: PaymentDTO[] };
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApplyPenaltyCommandDTO } from '../../../types/generated/ApplyPenaltyCommandDTO';
|
||||
import { RacePenaltiesDTO } from '../../../types/generated/RacePenaltiesDTO';
|
||||
import type { PenaltyTypesReferenceDTO } from '../../../types/PenaltyTypesReferenceDTO';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import { RacePenaltiesDTO } from '../../types/generated/RacePenaltiesDTO';
|
||||
import { ApplyPenaltyCommandDTO } from '../../types/generated/ApplyPenaltyCommandDTO';
|
||||
import type { PenaltyTypesReferenceDTO } from '../../types/PenaltyTypesReferenceDTO';
|
||||
|
||||
/**
|
||||
* Penalties API Client
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ErrorReporter } from '../../../interfaces/ErrorReporter';
|
||||
import type { Logger } from '../../../interfaces/Logger';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type { ErrorReporter } from '../../interfaces/ErrorReporter';
|
||||
import type { Logger } from '../../interfaces/Logger';
|
||||
|
||||
export type OperationalMode = 'normal' | 'maintenance' | 'test';
|
||||
export type FeatureState = 'enabled' | 'disabled' | 'coming_soon' | 'hidden';
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { ApplyPenaltyCommandDTO } from '../../../types/generated/ApplyPenaltyCommandDTO';
|
||||
import type { LeagueAdminProtestsDTO } from '../../../types/generated/LeagueAdminProtestsDTO';
|
||||
import type { RaceProtestsDTO } from '../../../types/generated/RaceProtestsDTO';
|
||||
import type { RequestProtestDefenseCommandDTO } from '../../../types/generated/RequestProtestDefenseCommandDTO';
|
||||
import type { ReviewProtestCommandDTO } from '../../../types/generated/ReviewProtestCommandDTO';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type { ApplyPenaltyCommandDTO } from '../../types/generated/ApplyPenaltyCommandDTO';
|
||||
import type { LeagueAdminProtestsDTO } from '../../types/generated/LeagueAdminProtestsDTO';
|
||||
import type { RaceProtestsDTO } from '../../types/generated/RaceProtestsDTO';
|
||||
import type { RequestProtestDefenseCommandDTO } from '../../types/generated/RequestProtestDefenseCommandDTO';
|
||||
import type { ReviewProtestCommandDTO } from '../../types/generated/ReviewProtestCommandDTO';
|
||||
|
||||
/**
|
||||
* Protests API Client
|
||||
@@ -1,17 +1,17 @@
|
||||
import type { FileProtestCommandDTO } from '../../../types/generated/FileProtestCommandDTO';
|
||||
import type { ImportRaceResultsDTO } from '../../../types/generated/ImportRaceResultsDTO';
|
||||
import type { RaceDetailEntryDTO } from '../../../types/generated/RaceDetailEntryDTO';
|
||||
import type { RaceDetailLeagueDTO } from '../../../types/generated/RaceDetailLeagueDTO';
|
||||
import type { RaceDetailRaceDTO } from '../../../types/generated/RaceDetailRaceDTO';
|
||||
import type { RaceDetailRegistrationDTO } from '../../../types/generated/RaceDetailRegistrationDTO';
|
||||
import type { RaceDetailUserResultDTO } from '../../../types/generated/RaceDetailUserResultDTO';
|
||||
import type { RaceResultsDetailDTO } from '../../../types/generated/RaceResultsDetailDTO';
|
||||
import type { RaceStatsDTO } from '../../../types/generated/RaceStatsDTO';
|
||||
import type { RaceWithSOFDTO } from '../../../types/generated/RaceWithSOFDTO';
|
||||
import type { RacesPageDataRaceDTO } from '../../../types/generated/RacesPageDataRaceDTO';
|
||||
import type { RegisterForRaceParamsDTO } from '../../../types/generated/RegisterForRaceParamsDTO';
|
||||
import type { WithdrawFromRaceParamsDTO } from '../../../types/generated/WithdrawFromRaceParamsDTO';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type { RaceStatsDTO } from '../../types/generated/RaceStatsDTO';
|
||||
import type { RacesPageDataRaceDTO } from '../../types/generated/RacesPageDataRaceDTO';
|
||||
import type { RaceResultsDetailDTO } from '../../types/generated/RaceResultsDetailDTO';
|
||||
import type { RaceWithSOFDTO } from '../../types/generated/RaceWithSOFDTO';
|
||||
import type { RegisterForRaceParamsDTO } from '../../types/generated/RegisterForRaceParamsDTO';
|
||||
import type { ImportRaceResultsDTO } from '../../types/generated/ImportRaceResultsDTO';
|
||||
import type { WithdrawFromRaceParamsDTO } from '../../types/generated/WithdrawFromRaceParamsDTO';
|
||||
import type { RaceDetailRaceDTO } from '../../types/generated/RaceDetailRaceDTO';
|
||||
import type { RaceDetailLeagueDTO } from '../../types/generated/RaceDetailLeagueDTO';
|
||||
import type { RaceDetailEntryDTO } from '../../types/generated/RaceDetailEntryDTO';
|
||||
import type { RaceDetailRegistrationDTO } from '../../types/generated/RaceDetailRegistrationDTO';
|
||||
import type { RaceDetailUserResultDTO } from '../../types/generated/RaceDetailUserResultDTO';
|
||||
import type { FileProtestCommandDTO } from '../../types/generated/FileProtestCommandDTO';
|
||||
|
||||
// Define missing types
|
||||
export type RacesPageDataDTO = { races: RacesPageDataRaceDTO[] };
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { AcceptSponsorshipRequestInputDTO } from '../../../types/generated/AcceptSponsorshipRequestInputDTO';
|
||||
import type { CreateSponsorInputDTO } from '../../../types/generated/CreateSponsorInputDTO';
|
||||
import type { GetPendingSponsorshipRequestsOutputDTO } from '../../../types/generated/GetPendingSponsorshipRequestsOutputDTO';
|
||||
import type { GetSponsorOutputDTO } from '../../../types/generated/GetSponsorOutputDTO';
|
||||
import type { RejectSponsorshipRequestInputDTO } from '../../../types/generated/RejectSponsorshipRequestInputDTO';
|
||||
import type { SponsorDashboardDTO } from '../../../types/generated/SponsorDashboardDTO';
|
||||
import type { SponsorDTO } from '../../../types/generated/SponsorDTO';
|
||||
import type { SponsorSponsorshipsDTO } from '../../../types/generated/SponsorSponsorshipsDTO';
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type { CreateSponsorInputDTO } from '../../types/generated/CreateSponsorInputDTO';
|
||||
import type { SponsorDashboardDTO } from '../../types/generated/SponsorDashboardDTO';
|
||||
import type { SponsorSponsorshipsDTO } from '../../types/generated/SponsorSponsorshipsDTO';
|
||||
import type { GetPendingSponsorshipRequestsOutputDTO } from '../../types/generated/GetPendingSponsorshipRequestsOutputDTO';
|
||||
import type { AcceptSponsorshipRequestInputDTO } from '../../types/generated/AcceptSponsorshipRequestInputDTO';
|
||||
import type { RejectSponsorshipRequestInputDTO } from '../../types/generated/RejectSponsorshipRequestInputDTO';
|
||||
import type { GetSponsorOutputDTO } from '../../types/generated/GetSponsorOutputDTO';
|
||||
import type { SponsorDTO } from '../../types/generated/SponsorDTO';
|
||||
|
||||
// Types that are not yet generated
|
||||
export type CreateSponsorOutputDto = { id: string; name: string };
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getWebsiteApiBaseUrl } from '../config/apiBaseUrl';
|
||||
import { ApiClient } from './api/ApiClient';
|
||||
import { getWebsiteApiBaseUrl } from './config/apiBaseUrl';
|
||||
|
||||
export const apiClient = new ApiClient(getWebsiteApiBaseUrl());
|
||||
@@ -2,10 +2,10 @@
|
||||
* Enhanced Error Reporter with user notifications and environment-specific handling
|
||||
*/
|
||||
|
||||
import { connectionMonitor } from '../gateways/api/base/ApiConnectionMonitor';
|
||||
import { ApiError } from '../gateways/api/base/ApiError';
|
||||
import { ErrorReporter } from '../interfaces/ErrorReporter';
|
||||
import { Logger } from '../interfaces/Logger';
|
||||
import { ApiError } from '../api/base/ApiError';
|
||||
import { connectionMonitor } from '../api/base/ApiConnectionMonitor';
|
||||
|
||||
// Import notification system (will be used if available)
|
||||
try {
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
* Allows developers to replay errors with the exact same context
|
||||
*/
|
||||
|
||||
import { getGlobalErrorHandler } from './GlobalErrorHandler';
|
||||
import { ApiError } from '../gateways/api/base/ApiError';
|
||||
import { getGlobalApiLogger } from './ApiRequestLogger';
|
||||
import { ApiError } from '../api/base/ApiError';
|
||||
import { getGlobalErrorHandler } from './GlobalErrorHandler';
|
||||
|
||||
export interface ReplayContext {
|
||||
timestamp: string;
|
||||
@@ -178,7 +178,7 @@ export class ErrorReplaySystem {
|
||||
const error = replay.error.type === 'ApiError'
|
||||
? new ApiError(
|
||||
replay.error.message,
|
||||
((replay.error.context as { type?: string } | undefined)?.type as import('../api/base/ApiError').ApiErrorType) || 'UNKNOWN_ERROR',
|
||||
((replay.error.context as { type?: string } | undefined)?.type as import('../gateways/api/base/ApiError').ApiErrorType) || 'UNKNOWN_ERROR',
|
||||
{
|
||||
timestamp: replay.timestamp,
|
||||
...(replay.error.context as Record<string, unknown> | undefined),
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
* Captures all uncaught errors, promise rejections, and React errors
|
||||
*/
|
||||
|
||||
import { ApiError } from '../api/base/ApiError';
|
||||
import { getGlobalErrorReporter } from './EnhancedErrorReporter';
|
||||
import { ConsoleLogger } from './logging/ConsoleLogger';
|
||||
import { ApiError } from '../gateways/api/base/ApiError';
|
||||
import { getGlobalReplaySystem } from './ErrorReplay';
|
||||
import { ConsoleLogger } from './logging/ConsoleLogger';
|
||||
|
||||
export interface GlobalErrorHandlerOptions {
|
||||
/**
|
||||
@@ -38,7 +37,6 @@ export interface GlobalErrorHandlerOptions {
|
||||
export class GlobalErrorHandler {
|
||||
private options: GlobalErrorHandlerOptions;
|
||||
private logger: ConsoleLogger;
|
||||
private errorReporter: ReturnType<typeof getGlobalErrorReporter>;
|
||||
private errorHistory: Array<{
|
||||
error: Error | ApiError;
|
||||
timestamp: string;
|
||||
@@ -58,7 +56,6 @@ export class GlobalErrorHandler {
|
||||
};
|
||||
|
||||
this.logger = new ConsoleLogger();
|
||||
this.errorReporter = getGlobalErrorReporter();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
193
apps/website/lib/mutations/admin/DeleteUserMutation.test.ts
Normal file
193
apps/website/lib/mutations/admin/DeleteUserMutation.test.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { DeleteUserMutation } from './DeleteUserMutation';
|
||||
import { AdminService } from '@/lib/services/admin/AdminService';
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('@/lib/services/admin/AdminService', () => {
|
||||
return {
|
||||
AdminService: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@/lib/config/apiBaseUrl', () => ({
|
||||
getWebsiteApiBaseUrl: () => 'http://localhost:3000',
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/config/env', () => ({
|
||||
getWebsiteServerEnv: () => ({ NODE_ENV: 'test' }),
|
||||
}));
|
||||
|
||||
describe('DeleteUserMutation', () => {
|
||||
let mutation: DeleteUserMutation;
|
||||
let mockServiceInstance: any;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mutation = new DeleteUserMutation();
|
||||
mockServiceInstance = {
|
||||
deleteUser: vi.fn(),
|
||||
};
|
||||
// Use mockImplementation to return the instance
|
||||
(AdminService as any).mockImplementation(function() {
|
||||
return mockServiceInstance;
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
describe('happy paths', () => {
|
||||
it('should successfully delete a user', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123' };
|
||||
mockServiceInstance.deleteUser.mockResolvedValue(Result.ok(undefined));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
expect(mockServiceInstance.deleteUser).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle deletion without userId parameter', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-456' };
|
||||
mockServiceInstance.deleteUser.mockResolvedValue(Result.ok(undefined));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
expect(mockServiceInstance.deleteUser).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe('failure modes', () => {
|
||||
it('should handle service failure during deletion', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123' };
|
||||
const serviceError = new Error('Service error');
|
||||
mockServiceInstance.deleteUser.mockRejectedValue(serviceError);
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('deleteFailed');
|
||||
expect(mockServiceInstance.deleteUser).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle service returning error result', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123' };
|
||||
const domainError = { type: 'serverError', message: 'Database connection failed' };
|
||||
mockServiceInstance.deleteUser.mockResolvedValue(Result.err(domainError));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('serverError');
|
||||
expect(mockServiceInstance.deleteUser).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle service returning userNotFound error', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-999' };
|
||||
const domainError = { type: 'notFound', message: 'User not found' };
|
||||
mockServiceInstance.deleteUser.mockResolvedValue(Result.err(domainError));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('userNotFound');
|
||||
expect(mockServiceInstance.deleteUser).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle service returning noPermission error', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123' };
|
||||
const domainError = { type: 'unauthorized', message: 'Insufficient permissions' };
|
||||
mockServiceInstance.deleteUser.mockResolvedValue(Result.err(domainError));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('noPermission');
|
||||
expect(mockServiceInstance.deleteUser).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error mapping', () => {
|
||||
it('should map various domain errors to mutation errors', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123' };
|
||||
const testCases = [
|
||||
{ domainError: { type: 'notFound' }, expectedError: 'userNotFound' },
|
||||
{ domainError: { type: 'unauthorized' }, expectedError: 'noPermission' },
|
||||
{ domainError: { type: 'validationError' }, expectedError: 'invalidData' },
|
||||
{ domainError: { type: 'serverError' }, expectedError: 'serverError' },
|
||||
{ domainError: { type: 'networkError' }, expectedError: 'networkError' },
|
||||
{ domainError: { type: 'notImplemented' }, expectedError: 'notImplemented' },
|
||||
{ domainError: { type: 'unknown' }, expectedError: 'unknown' },
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
mockServiceInstance.deleteUser.mockResolvedValue(Result.err(testCase.domainError));
|
||||
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe(testCase.expectedError);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('input validation', () => {
|
||||
it('should accept valid userId input', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123' };
|
||||
mockServiceInstance.deleteUser.mockResolvedValue(Result.ok(undefined));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(mockServiceInstance.deleteUser).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle empty userId gracefully', async () => {
|
||||
// Arrange
|
||||
const input = { userId: '' };
|
||||
mockServiceInstance.deleteUser.mockResolvedValue(Result.ok(undefined));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(mockServiceInstance.deleteUser).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('service instantiation', () => {
|
||||
it('should create AdminService instance', () => {
|
||||
// Arrange & Act
|
||||
const mutation = new DeleteUserMutation();
|
||||
|
||||
// Assert
|
||||
expect(mutation).toBeInstanceOf(DeleteUserMutation);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,331 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { UpdateUserStatusMutation } from './UpdateUserStatusMutation';
|
||||
import { AdminService } from '@/lib/services/admin/AdminService';
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('@/lib/services/admin/AdminService', () => {
|
||||
return {
|
||||
AdminService: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@/lib/config/apiBaseUrl', () => ({
|
||||
getWebsiteApiBaseUrl: () => 'http://localhost:3000',
|
||||
}));
|
||||
|
||||
vi.mock('@/lib/config/env', () => ({
|
||||
getWebsiteServerEnv: () => ({ NODE_ENV: 'test' }),
|
||||
}));
|
||||
|
||||
describe('UpdateUserStatusMutation', () => {
|
||||
let mutation: UpdateUserStatusMutation;
|
||||
let mockServiceInstance: any;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mutation = new UpdateUserStatusMutation();
|
||||
mockServiceInstance = {
|
||||
updateUserStatus: vi.fn(),
|
||||
};
|
||||
// Use mockImplementation to return the instance
|
||||
(AdminService as any).mockImplementation(function() {
|
||||
return mockServiceInstance;
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
describe('happy paths', () => {
|
||||
it('should successfully update user status to active', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123', status: 'active' };
|
||||
mockServiceInstance.updateUserStatus.mockResolvedValue(
|
||||
Result.ok({
|
||||
id: 'user-123',
|
||||
email: 'mock@example.com',
|
||||
displayName: 'Mock User',
|
||||
roles: ['user'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
})
|
||||
);
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledWith('user-123', 'active');
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should successfully update user status to suspended', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-456', status: 'suspended' };
|
||||
mockServiceInstance.updateUserStatus.mockResolvedValue(
|
||||
Result.ok({
|
||||
id: 'user-456',
|
||||
email: 'mock@example.com',
|
||||
displayName: 'Mock User',
|
||||
roles: ['user'],
|
||||
status: 'suspended',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
})
|
||||
);
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledWith('user-456', 'suspended');
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should successfully update user status to deleted', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-789', status: 'deleted' };
|
||||
mockServiceInstance.updateUserStatus.mockResolvedValue(
|
||||
Result.ok({
|
||||
id: 'user-789',
|
||||
email: 'mock@example.com',
|
||||
displayName: 'Mock User',
|
||||
roles: ['user'],
|
||||
status: 'deleted',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
})
|
||||
);
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledWith('user-789', 'deleted');
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle different status values', async () => {
|
||||
// Arrange
|
||||
const statuses = ['active', 'suspended', 'deleted', 'pending'];
|
||||
const userId = 'user-123';
|
||||
|
||||
for (const status of statuses) {
|
||||
mockServiceInstance.updateUserStatus.mockResolvedValue(
|
||||
Result.ok({
|
||||
id: userId,
|
||||
email: 'mock@example.com',
|
||||
displayName: 'Mock User',
|
||||
roles: ['user'],
|
||||
status,
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
})
|
||||
);
|
||||
|
||||
const result = await mutation.execute({ userId, status });
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledWith(userId, status);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('failure modes', () => {
|
||||
it('should handle service failure during status update', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123', status: 'suspended' };
|
||||
const serviceError = new Error('Service error');
|
||||
mockServiceInstance.updateUserStatus.mockRejectedValue(serviceError);
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('updateFailed');
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle service returning error result', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123', status: 'suspended' };
|
||||
const domainError = { type: 'serverError', message: 'Database connection failed' };
|
||||
mockServiceInstance.updateUserStatus.mockResolvedValue(Result.err(domainError));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('serverError');
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle service returning userNotFound error', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-999', status: 'suspended' };
|
||||
const domainError = { type: 'notFound', message: 'User not found' };
|
||||
mockServiceInstance.updateUserStatus.mockResolvedValue(Result.err(domainError));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('userNotFound');
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle service returning noPermission error', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123', status: 'suspended' };
|
||||
const domainError = { type: 'unauthorized', message: 'Insufficient permissions' };
|
||||
mockServiceInstance.updateUserStatus.mockResolvedValue(Result.err(domainError));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('noPermission');
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle service returning invalidData error', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123', status: 'invalid-status' };
|
||||
const domainError = { type: 'validationError', message: 'Invalid status value' };
|
||||
mockServiceInstance.updateUserStatus.mockResolvedValue(Result.err(domainError));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('invalidData');
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error mapping', () => {
|
||||
it('should map various domain errors to mutation errors', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123', status: 'suspended' };
|
||||
const testCases = [
|
||||
{ domainError: { type: 'notFound' }, expectedError: 'userNotFound' },
|
||||
{ domainError: { type: 'unauthorized' }, expectedError: 'noPermission' },
|
||||
{ domainError: { type: 'validationError' }, expectedError: 'invalidData' },
|
||||
{ domainError: { type: 'serverError' }, expectedError: 'serverError' },
|
||||
{ domainError: { type: 'networkError' }, expectedError: 'networkError' },
|
||||
{ domainError: { type: 'notImplemented' }, expectedError: 'notImplemented' },
|
||||
{ domainError: { type: 'unknown' }, expectedError: 'unknown' },
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
mockServiceInstance.updateUserStatus.mockResolvedValue(Result.err(testCase.domainError));
|
||||
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe(testCase.expectedError);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('input validation', () => {
|
||||
it('should accept valid userId and status input', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123', status: 'active' };
|
||||
mockServiceInstance.updateUserStatus.mockResolvedValue(
|
||||
Result.ok({
|
||||
id: 'user-123',
|
||||
email: 'mock@example.com',
|
||||
displayName: 'Mock User',
|
||||
roles: ['user'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
})
|
||||
);
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle empty userId gracefully', async () => {
|
||||
// Arrange
|
||||
const input = { userId: '', status: 'active' };
|
||||
mockServiceInstance.updateUserStatus.mockResolvedValue(
|
||||
Result.ok({
|
||||
id: '',
|
||||
email: 'mock@example.com',
|
||||
displayName: 'Mock User',
|
||||
roles: ['user'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
})
|
||||
);
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledWith('', 'active');
|
||||
});
|
||||
|
||||
it('should handle empty status gracefully', async () => {
|
||||
// Arrange
|
||||
const input = { userId: 'user-123', status: '' };
|
||||
mockServiceInstance.updateUserStatus.mockResolvedValue(
|
||||
Result.ok({
|
||||
id: 'user-123',
|
||||
email: 'mock@example.com',
|
||||
displayName: 'Mock User',
|
||||
roles: ['user'],
|
||||
status: '',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
})
|
||||
);
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(mockServiceInstance.updateUserStatus).toHaveBeenCalledWith('user-123', '');
|
||||
});
|
||||
});
|
||||
|
||||
describe('service instantiation', () => {
|
||||
it('should create AdminService instance', () => {
|
||||
// Arrange & Act
|
||||
const mutation = new UpdateUserStatusMutation();
|
||||
|
||||
// Assert
|
||||
expect(mutation).toBeInstanceOf(UpdateUserStatusMutation);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
189
apps/website/lib/mutations/auth/ForgotPasswordMutation.test.ts
Normal file
189
apps/website/lib/mutations/auth/ForgotPasswordMutation.test.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { ForgotPasswordMutation } from './ForgotPasswordMutation';
|
||||
import { AuthService } from '@/lib/services/auth/AuthService';
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('@/lib/services/auth/AuthService', () => {
|
||||
return {
|
||||
AuthService: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('ForgotPasswordMutation', () => {
|
||||
let mutation: ForgotPasswordMutation;
|
||||
let mockServiceInstance: { forgotPassword: ReturnType<typeof vi.fn> };
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mutation = new ForgotPasswordMutation();
|
||||
mockServiceInstance = {
|
||||
forgotPassword: vi.fn(),
|
||||
};
|
||||
// Use mockImplementation to return the instance
|
||||
(AuthService as any).mockImplementation(function() {
|
||||
return mockServiceInstance;
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
describe('happy paths', () => {
|
||||
it('should successfully send forgot password request', async () => {
|
||||
// Arrange
|
||||
const input = { email: 'test@example.com' };
|
||||
const serviceOutput = { message: 'Reset link sent', magicLink: 'https://example.com/reset' };
|
||||
mockServiceInstance.forgotPassword.mockResolvedValue(Result.ok(serviceOutput));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toEqual(serviceOutput);
|
||||
expect(mockServiceInstance.forgotPassword).toHaveBeenCalledWith(input);
|
||||
expect(mockServiceInstance.forgotPassword).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle forgot password request without magicLink', async () => {
|
||||
// Arrange
|
||||
const input = { email: 'test@example.com' };
|
||||
const serviceOutput = { message: 'Reset link sent' };
|
||||
mockServiceInstance.forgotPassword.mockResolvedValue(Result.ok(serviceOutput));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toEqual(serviceOutput);
|
||||
expect(mockServiceInstance.forgotPassword).toHaveBeenCalledWith(input);
|
||||
});
|
||||
});
|
||||
|
||||
describe('failure modes', () => {
|
||||
it('should handle service failure during forgot password request', async () => {
|
||||
// Arrange
|
||||
const input = { email: 'test@example.com' };
|
||||
const serviceError = new Error('Service error');
|
||||
mockServiceInstance.forgotPassword.mockRejectedValue(serviceError);
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('Service error');
|
||||
expect(mockServiceInstance.forgotPassword).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle service returning error result', async () => {
|
||||
// Arrange
|
||||
const input = { email: 'test@example.com' };
|
||||
const domainError = { type: 'serverError', message: 'Email not found' };
|
||||
mockServiceInstance.forgotPassword.mockResolvedValue(Result.err(domainError));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('Email not found');
|
||||
expect(mockServiceInstance.forgotPassword).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle service returning validation error', async () => {
|
||||
// Arrange
|
||||
const input = { email: 'invalid-email' };
|
||||
const domainError = { type: 'validationError', message: 'Invalid email format' };
|
||||
mockServiceInstance.forgotPassword.mockResolvedValue(Result.err(domainError));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('Invalid email format');
|
||||
expect(mockServiceInstance.forgotPassword).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle service returning rate limit error', async () => {
|
||||
// Arrange
|
||||
const input = { email: 'test@example.com' };
|
||||
const domainError = { type: 'rateLimit', message: 'Too many requests' };
|
||||
mockServiceInstance.forgotPassword.mockResolvedValue(Result.err(domainError));
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe('Too many requests');
|
||||
expect(mockServiceInstance.forgotPassword).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error mapping', () => {
|
||||
it('should map various domain errors to mutation errors', async () => {
|
||||
// Arrange
|
||||
const input = { email: 'test@example.com' };
|
||||
const testCases = [
|
||||
{ domainError: { type: 'serverError', message: 'Server error' }, expectedError: 'Server error' },
|
||||
{ domainError: { type: 'validationError', message: 'Validation error' }, expectedError: 'Validation error' },
|
||||
{ domainError: { type: 'notFound', message: 'Not found' }, expectedError: 'Not found' },
|
||||
{ domainError: { type: 'rateLimit', message: 'Rate limit exceeded' }, expectedError: 'Rate limit exceeded' },
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
mockServiceInstance.forgotPassword.mockResolvedValue(Result.err(testCase.domainError));
|
||||
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toBe(testCase.expectedError);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('input validation', () => {
|
||||
it('should accept valid email input', async () => {
|
||||
// Arrange
|
||||
const input = { email: 'test@example.com' };
|
||||
mockServiceInstance.forgotPassword.mockResolvedValue(
|
||||
Result.ok({ message: 'Reset link sent' })
|
||||
);
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(mockServiceInstance.forgotPassword).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle empty email gracefully', async () => {
|
||||
// Arrange
|
||||
const input = { email: '' };
|
||||
mockServiceInstance.forgotPassword.mockResolvedValue(
|
||||
Result.ok({ message: 'Reset link sent' })
|
||||
);
|
||||
|
||||
// Act
|
||||
const result = await mutation.execute(input);
|
||||
|
||||
// Assert
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(mockServiceInstance.forgotPassword).toHaveBeenCalledWith(input);
|
||||
});
|
||||
});
|
||||
|
||||
describe('service instantiation', () => {
|
||||
it('should create AuthService instance', () => {
|
||||
// Arrange & Act
|
||||
const mutation = new ForgotPasswordMutation();
|
||||
|
||||
// Assert
|
||||
expect(mutation).toBeInstanceOf(ForgotPasswordMutation);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user