ui/src/services/api.ts (view raw)
1import type { User, Exercise, Muscle, Routine, RecordRoutine, WorkoutStats } from '../types/models';
2
3const API_BASE = '/api';
4
5class BaseService<T> {
6 protected endpoint: string;
7 constructor(endpoint: string) {
8 this.endpoint = endpoint;
9 }
10
11 protected async request<R>(path: string, options?: RequestInit): Promise<R> {
12 const response = await fetch(`${API_BASE}${path}`, {
13 headers: {
14 'Content-Type': 'application/json',
15 ...options?.headers,
16 },
17 ...options,
18 });
19
20 if (!response.ok) {
21 throw new Error(`API Error: ${response.status} ${response.statusText}`);
22 }
23
24 return response.json();
25 }
26
27 async getAll(): Promise<T[]> {
28 return this.request<T[]>(this.endpoint);
29 }
30
31 async get(id: number): Promise<T> {
32 return this.request<T>(`${this.endpoint}/${id}`);
33 }
34
35 async create(data: Omit<T, 'id' | 'createdAt' | 'updatedAt'>): Promise<T> {
36 return this.request<T>(this.endpoint, {
37 method: 'POST',
38 body: JSON.stringify(data),
39 });
40 }
41
42 async update(id: number, data: Partial<T>): Promise<T> {
43 return this.request<T>(`${this.endpoint}/${id}`, {
44 method: 'PUT',
45 body: JSON.stringify(data),
46 });
47 }
48
49 async delete(id: number): Promise<void> {
50 await this.request<void>(`${this.endpoint}/${id}`, {
51 method: 'DELETE',
52 });
53 }
54}
55
56class UserService extends BaseService<User> {
57 constructor() {
58 super('/users');
59 }
60
61 // Override get to default to user ID 1
62 async get(id: number = 1): Promise<User> {
63 return super.get(id);
64 }
65}
66
67class ExerciseService extends BaseService<Exercise> {
68 constructor() {
69 super('/exercises');
70 }
71}
72
73class MuscleService extends BaseService<Muscle> {
74 constructor() {
75 super('/muscles');
76 }
77}
78
79class RoutineService extends BaseService<Routine> {
80 constructor() {
81 super('/routines');
82 }
83}
84
85class RecordService extends BaseService<RecordRoutine> {
86 constructor() {
87 super('/records');
88 }
89}
90
91class StatsService {
92 protected async request<R>(path: string, options?: RequestInit): Promise<R> {
93 const response = await fetch(`${API_BASE}${path}`, {
94 headers: {
95 'Content-Type': 'application/json',
96 ...options?.headers,
97 },
98 ...options,
99 });
100
101 if (!response.ok) {
102 throw new Error(`API Error: ${response.status} ${response.statusText}`);
103 }
104
105 return response.json();
106 }
107
108 async get(): Promise<WorkoutStats> {
109 return this.request<WorkoutStats>('/stats');
110 }
111}
112
113class HealthService {
114 protected async request<R>(path: string, options?: RequestInit): Promise<R> {
115 const response = await fetch(`${API_BASE}${path}`, {
116 headers: {
117 'Content-Type': 'application/json',
118 ...options?.headers,
119 },
120 ...options,
121 });
122
123 if (!response.ok) {
124 throw new Error(`API Error: ${response.status} ${response.statusText}`);
125 }
126
127 return response.json();
128 }
129
130 async ping(): Promise<{ message: string }> {
131 return this.request<{ message: string }>('/ping');
132 }
133}
134
135// Export service instances
136export const userService = new UserService();
137export const exerciseService = new ExerciseService();
138export const muscleService = new MuscleService();
139export const routineService = new RoutineService();
140export const recordService = new RecordService();
141export const statsService = new StatsService();
142export const healthService = new HealthService();