Backend (Phase 8.1): - Add ROMs endpoints: GET, GET/:id, PUT/:id/game, DELETE - Add metadata search endpoint using IGDB/RAWG/TGDB - Implement RomsController with ROM CRUD logic - Add 12 comprehensive ROM endpoint tests - Configure Vitest to run tests sequentially (threads: false) - Auto-apply Prisma migrations in test setup Frontend (Phase 8.2 + 8.3): - Create ROM types: RomFile, Artwork, EnrichedGame - Extend API client with roms and metadata namespaces - Implement 5 custom hooks with TanStack Query - Create ScanDialog, MetadataSearchDialog, RomCard components - Rewrite roms.tsx page with table and all actions - Add 37 comprehensive component and page tests All 122 tests passing: 63 backend + 59 frontend Lint: 0 errors, only unused directive warnings
65 lines
1.7 KiB
TypeScript
65 lines
1.7 KiB
TypeScript
import { Game, CreateGameInput, UpdateGameInput } from '../types/game';
|
|
import { RomFile, EnrichedGame, ScanResult } from '../types/rom';
|
|
|
|
const API_BASE = '/api';
|
|
|
|
async function request<T>(endpoint: string, options?: RequestInit): Promise<T> {
|
|
const response = await fetch(`${API_BASE}${endpoint}`, {
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...options?.headers,
|
|
},
|
|
...options,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`API error: ${response.status} ${response.statusText}`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
export const api = {
|
|
games: {
|
|
list: () => request<Game[]>('/games'),
|
|
create: (data: CreateGameInput) =>
|
|
request<Game>('/games', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
}),
|
|
update: (id: string, data: UpdateGameInput) =>
|
|
request<Game>(`/games/${id}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
}),
|
|
delete: (id: string) =>
|
|
request<void>(`/games/${id}`, {
|
|
method: 'DELETE',
|
|
}),
|
|
},
|
|
|
|
roms: {
|
|
list: () => request<RomFile[]>('/roms'),
|
|
getById: (id: string) => request<RomFile>(`/roms/${id}`),
|
|
linkGame: (romId: string, gameId: string) =>
|
|
request<RomFile>(`/roms/${romId}/game`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify({ gameId }),
|
|
}),
|
|
delete: (id: string) => request<void>(`/roms/${id}`, { method: 'DELETE' }),
|
|
},
|
|
|
|
metadata: {
|
|
search: (query: string) =>
|
|
request<EnrichedGame[]>('/metadata/search?q=' + encodeURIComponent(query)),
|
|
},
|
|
|
|
import: {
|
|
scan: (dir: string) =>
|
|
request<ScanResult>('/import/scan', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ dir }),
|
|
}),
|
|
},
|
|
};
|