Skip to content

Commit 0d2f693

Browse files
committed
Factor all of the API calls into their own file
1 parent d26153f commit 0d2f693

File tree

8 files changed

+450
-414
lines changed

8 files changed

+450
-414
lines changed

frontend/src/api/index.ts

Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
import { config } from '@/config';
2+
3+
// Types
4+
export interface User {
5+
id: string;
6+
email: string;
7+
name: string;
8+
token: string;
9+
roles: string[];
10+
}
11+
12+
export interface UserResponse {
13+
user: {
14+
id: string;
15+
email: string;
16+
first_name: string;
17+
last_name: string;
18+
roles: string[];
19+
};
20+
token: string;
21+
}
22+
23+
export interface Profile {
24+
first_name: string;
25+
last_name: string;
26+
phone_number: string;
27+
street_1: string;
28+
street_2: string;
29+
city: string;
30+
state: string;
31+
zip: string;
32+
}
33+
34+
export interface Order {
35+
id: string;
36+
created: string;
37+
status: string;
38+
payment_status: string;
39+
delivery_fee: number;
40+
total_amount: number;
41+
tax_amount: number;
42+
delivery_address?: {
43+
street_address: string[];
44+
city: string;
45+
state: string;
46+
zip_code: string;
47+
};
48+
customer_phone?: string;
49+
stores: {
50+
store: {
51+
id: string;
52+
name: string;
53+
};
54+
items: {
55+
id: string;
56+
name: string;
57+
quantity: number;
58+
price: number;
59+
}[];
60+
}[];
61+
}
62+
63+
export interface Store {
64+
id: string;
65+
name: string;
66+
street_1: string;
67+
street_2?: string;
68+
city: string;
69+
state: string;
70+
zip_code: string;
71+
instagram?: string;
72+
facebook?: string;
73+
twitter?: string;
74+
items?: StoreItem[];
75+
}
76+
77+
export interface StoreItem {
78+
id: string;
79+
name: string;
80+
description: string;
81+
price: number;
82+
quantity: number;
83+
imageUrl?: string;
84+
}
85+
86+
export interface SavedCard {
87+
id: string;
88+
last4: string;
89+
brand: string;
90+
exp_month: number;
91+
exp_year: number;
92+
isDefault: boolean;
93+
}
94+
95+
export interface OrderCreateData {
96+
token: string;
97+
user_id: string;
98+
store_id: string;
99+
payment_method_id: string;
100+
items: {
101+
store_item_id: string;
102+
quantity: number;
103+
price: number;
104+
}[];
105+
subtotal_amount: number;
106+
tax_amount: number;
107+
delivery_fee: number;
108+
total_amount: number;
109+
delivery_address: {
110+
street_address: string[];
111+
city: string;
112+
state: string;
113+
zip_code: string;
114+
country: string;
115+
};
116+
}
117+
118+
// Helper function to handle API responses
119+
async function handleResponse<T>(response: Response): Promise<T> {
120+
if (!response.ok) {
121+
const errorData = await response.json().catch(() => ({}));
122+
throw new Error(errorData.detail || `API Error: ${response.status}`);
123+
}
124+
return response.json();
125+
}
126+
127+
// Auth API
128+
export const authApi = {
129+
login: async (email: string, password: string): Promise<UserResponse> => {
130+
const response = await fetch(`${config.apiUrl}/api/v0/auth/login`, {
131+
method: 'POST',
132+
headers: { 'Content-Type': 'application/json' },
133+
body: JSON.stringify({ email, password }),
134+
});
135+
return handleResponse(response);
136+
},
137+
138+
signup: async (email: string, password: string, first_name: string, last_name: string): Promise<UserResponse> => {
139+
const response = await fetch(`${config.apiUrl}/api/v0/auth/signup`, {
140+
method: 'POST',
141+
headers: { 'Content-Type': 'application/json' },
142+
body: JSON.stringify({
143+
email,
144+
password,
145+
passwordConfirm: password,
146+
first_name,
147+
last_name
148+
}),
149+
});
150+
return handleResponse(response);
151+
},
152+
153+
getProfile: async (token: string): Promise<Profile> => {
154+
const response = await fetch(`${config.apiUrl}/api/v0/auth/profile`, {
155+
headers: { 'Authorization': `Bearer ${token}` },
156+
});
157+
return handleResponse(response);
158+
},
159+
160+
updateProfile: async (token: string, profile: Partial<Profile>): Promise<Profile> => {
161+
const response = await fetch(`${config.apiUrl}/api/v0/auth/profile`, {
162+
method: 'PATCH',
163+
headers: {
164+
'Authorization': `Bearer ${token}`,
165+
'Content-Type': 'application/json',
166+
},
167+
body: JSON.stringify(profile),
168+
});
169+
return handleResponse(response);
170+
},
171+
};
172+
173+
// Orders API
174+
export const ordersApi = {
175+
getUserOrders: async (token: string): Promise<Order[]> => {
176+
const response = await fetch(`${config.apiUrl}/api/v0/user/orders`, {
177+
headers: { 'Authorization': `Bearer ${token}` },
178+
});
179+
return handleResponse(response);
180+
},
181+
182+
getAdminOrders: async (token: string): Promise<Order[]> => {
183+
const response = await fetch(`${config.apiUrl}/api/v0/orders`, {
184+
headers: { 'Authorization': `Bearer ${token}` },
185+
});
186+
return handleResponse(response);
187+
},
188+
189+
getOrder: async (token: string, orderId: string): Promise<Order> => {
190+
const response = await fetch(`${config.apiUrl}/api/v0/orders/${orderId}`, {
191+
headers: { 'Authorization': `Bearer ${token}` },
192+
});
193+
return handleResponse(response);
194+
},
195+
196+
createOrder: async (token: string, orderData: OrderCreateData): Promise<Order> => {
197+
const response = await fetch(`${config.apiUrl}/api/v0/orders`, {
198+
method: 'POST',
199+
headers: {
200+
'Authorization': `Bearer ${token}`,
201+
'Content-Type': 'application/json',
202+
},
203+
body: JSON.stringify(orderData),
204+
});
205+
return handleResponse(response);
206+
},
207+
208+
getOrderById: async (token: string, orderId: string): Promise<Order> => {
209+
const response = await fetch(`${config.apiUrl}/api/v0/orders/${orderId}`, {
210+
headers: { 'Authorization': `Bearer ${token}` },
211+
});
212+
return handleResponse(response);
213+
},
214+
215+
updateOrderStatus: async (token: string, orderId: string, status: string): Promise<Order> => {
216+
const response = await fetch(`${config.apiUrl}/api/v0/orders/${orderId}/status`, {
217+
method: 'PATCH',
218+
headers: {
219+
'Authorization': `Bearer ${token}`,
220+
'Content-Type': 'application/json',
221+
},
222+
body: JSON.stringify({ status }),
223+
});
224+
return handleResponse(response);
225+
},
226+
};
227+
228+
// Stores API
229+
export const storesApi = {
230+
getAllStores: async (): Promise<Store[]> => {
231+
const response = await fetch(`${config.apiUrl}/api/v0/stores`);
232+
return handleResponse(response);
233+
},
234+
235+
getStore: async (storeId: string): Promise<Store> => {
236+
const response = await fetch(`${config.apiUrl}/api/v0/stores/${storeId}`);
237+
return handleResponse(response);
238+
},
239+
240+
getStoreItems: async (storeId: string): Promise<StoreItem[]> => {
241+
const response = await fetch(`${config.apiUrl}/api/v0/stores/${storeId}/items`);
242+
return handleResponse(response);
243+
},
244+
245+
getStoreOrders: async (token: string, storeId: string): Promise<Order[]> => {
246+
const response = await fetch(`${config.apiUrl}/api/v0/stores/${storeId}/orders`, {
247+
headers: { 'Authorization': `Bearer ${token}` },
248+
});
249+
return handleResponse(response);
250+
},
251+
252+
getStoreRoles: async (token: string, storeId: string): Promise<{ roles: string[] }> => {
253+
const response = await fetch(`${config.apiUrl}/api/v0/stores/${storeId}/roles`, {
254+
headers: { 'Authorization': `Bearer ${token}` },
255+
});
256+
return handleResponse(response);
257+
},
258+
259+
createStoreItem: async (token: string, storeId: string, itemData: Partial<StoreItem>): Promise<StoreItem> => {
260+
const response = await fetch(`${config.apiUrl}/api/v0/stores/${storeId}/items`, {
261+
method: 'POST',
262+
headers: {
263+
'Authorization': `Bearer ${token}`,
264+
'Content-Type': 'application/json',
265+
},
266+
body: JSON.stringify(itemData),
267+
});
268+
return handleResponse(response);
269+
},
270+
271+
updateStoreItem: async (token: string, storeId: string, itemId: string, itemData: Partial<StoreItem>): Promise<StoreItem> => {
272+
const response = await fetch(`${config.apiUrl}/api/v0/stores/${storeId}/items/${itemId}`, {
273+
method: 'PATCH',
274+
headers: {
275+
'Authorization': `Bearer ${token}`,
276+
'Content-Type': 'application/json',
277+
},
278+
body: JSON.stringify(itemData),
279+
});
280+
return handleResponse(response);
281+
},
282+
283+
deleteStoreItem: async (token: string, storeId: string, itemId: string): Promise<void> => {
284+
const response = await fetch(`${config.apiUrl}/api/v0/stores/${storeId}/items/${itemId}`, {
285+
method: 'DELETE',
286+
headers: { 'Authorization': `Bearer ${token}` },
287+
});
288+
return handleResponse(response);
289+
},
290+
291+
getStoreWithItems: async (storeId: string): Promise<Store & { items: StoreItem[] }> => {
292+
const [store, items] = await Promise.all([
293+
storesApi.getStore(storeId),
294+
storesApi.getStoreItems(storeId)
295+
]);
296+
return {
297+
...store,
298+
items: items.map(item => ({
299+
...item,
300+
imageUrl: `https://picsum.photos/seed/${item.id}/400/300`
301+
}))
302+
};
303+
},
304+
305+
getAllStoresWithItems: async (): Promise<(Store & { items: StoreItem[] })[]> => {
306+
const stores = await storesApi.getAllStores();
307+
const storesWithItems = await Promise.all(
308+
stores.map(async (store) => {
309+
const items = await storesApi.getStoreItems(store.id);
310+
return {
311+
...store,
312+
items: items.map(item => ({
313+
...item,
314+
imageUrl: `https://picsum.photos/seed/${item.id}/400/300`
315+
}))
316+
};
317+
})
318+
);
319+
return storesWithItems;
320+
},
321+
};
322+
323+
// Payment API
324+
export const paymentApi = {
325+
getCards: async (token: string): Promise<SavedCard[]> => {
326+
const response = await fetch(`${config.apiUrl}/api/v0/payment/cards`, {
327+
headers: { 'Authorization': `Bearer ${token}` },
328+
});
329+
if (response.status === 404) return [];
330+
return handleResponse(response);
331+
},
332+
333+
createSetupIntent: async (token: string): Promise<{ clientSecret: string }> => {
334+
const response = await fetch(`${config.apiUrl}/api/v0/payment/setup-intent`, {
335+
method: 'POST',
336+
headers: { 'Authorization': `Bearer ${token}` },
337+
});
338+
return handleResponse(response);
339+
},
340+
341+
attachCard: async (token: string, paymentMethodId: string): Promise<SavedCard> => {
342+
const response = await fetch(`${config.apiUrl}/api/v0/payment/cards`, {
343+
method: 'POST',
344+
headers: {
345+
'Authorization': `Bearer ${token}`,
346+
'Content-Type': 'application/json',
347+
},
348+
body: JSON.stringify({ payment_method_id: paymentMethodId }),
349+
});
350+
return handleResponse(response);
351+
},
352+
353+
deleteCard: async (token: string, cardId: string): Promise<void> => {
354+
const response = await fetch(`${config.apiUrl}/api/v0/payment/cards/${cardId}`, {
355+
method: 'DELETE',
356+
headers: { 'Authorization': `Bearer ${token}` },
357+
});
358+
return handleResponse(response);
359+
},
360+
};
361+
362+
// Search API
363+
export const searchApi = {
364+
searchProducts: async (query: string): Promise<any[]> => {
365+
const response = await fetch(
366+
`${config.searchUrl}/api/search?query=${encodeURIComponent(query)}&index=products`
367+
);
368+
const { hits } = await handleResponse<{ hits: any[] }>(response);
369+
return hits;
370+
},
371+
};

0 commit comments

Comments
 (0)