all repos — go-lift @ 387721fc5e90ba268efbae885c9baf8e0a543f28

Lightweight workout tracker prototype..

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();