feat(shopping): client API TypeScript typé
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
// frontend/src/api/shopping.ts
|
||||
|
||||
export interface Store {
|
||||
id: string
|
||||
name: string
|
||||
location: string | null
|
||||
}
|
||||
|
||||
export interface Product {
|
||||
id: string
|
||||
name: string
|
||||
brand: string | null
|
||||
category: string | null
|
||||
default_unit: string | null
|
||||
frequency_score: number
|
||||
}
|
||||
|
||||
export interface ShoppingItem {
|
||||
id: string
|
||||
product_id: string | null
|
||||
custom_name: string | null
|
||||
display_name: string
|
||||
quantity: string | null
|
||||
unit: string | null
|
||||
is_checked: boolean
|
||||
price_recorded: string | null
|
||||
carried_over: boolean
|
||||
sort_order: number | null
|
||||
}
|
||||
|
||||
export interface ShoppingList {
|
||||
id: string
|
||||
name: string | null
|
||||
store_id: string | null
|
||||
week_date: string | null
|
||||
status: 'draft' | 'active' | 'done'
|
||||
created_at: string
|
||||
item_count: number
|
||||
checked_count: number
|
||||
}
|
||||
|
||||
export interface ShoppingListDetail extends ShoppingList {
|
||||
items: ShoppingItem[]
|
||||
}
|
||||
|
||||
export interface ShoppingListCreate {
|
||||
name?: string
|
||||
store_id?: string
|
||||
week_date?: string
|
||||
}
|
||||
|
||||
export interface ShoppingListUpdate {
|
||||
name?: string
|
||||
store_id?: string
|
||||
status?: 'draft' | 'active' | 'done'
|
||||
}
|
||||
|
||||
export interface ShoppingItemCreate {
|
||||
product_id?: string
|
||||
custom_name?: string
|
||||
quantity?: string
|
||||
unit?: string
|
||||
}
|
||||
|
||||
export interface ShoppingItemUpdate {
|
||||
is_checked?: boolean
|
||||
quantity?: string
|
||||
unit?: string
|
||||
price_recorded?: string
|
||||
}
|
||||
|
||||
const BASE = '/api/shopping'
|
||||
|
||||
async function handleResponse<T>(res: Response): Promise<T> {
|
||||
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
|
||||
if (res.status === 204) return undefined as T
|
||||
return res.json() as Promise<T>
|
||||
}
|
||||
|
||||
export async function fetchStores(): Promise<Store[]> {
|
||||
return handleResponse(await fetch(`${BASE}/stores`))
|
||||
}
|
||||
|
||||
export async function searchProducts(q?: string): Promise<Product[]> {
|
||||
const qs = q ? `?q=${encodeURIComponent(q)}&limit=30` : '?limit=30'
|
||||
return handleResponse(await fetch(`${BASE}/products${qs}`))
|
||||
}
|
||||
|
||||
export async function fetchLists(): Promise<ShoppingList[]> {
|
||||
return handleResponse(await fetch(`${BASE}/lists`))
|
||||
}
|
||||
|
||||
export async function createList(data: ShoppingListCreate): Promise<ShoppingListDetail> {
|
||||
return handleResponse(await fetch(`${BASE}/lists`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
}))
|
||||
}
|
||||
|
||||
export async function fetchListDetail(id: string): Promise<ShoppingListDetail> {
|
||||
return handleResponse(await fetch(`${BASE}/lists/${id}`))
|
||||
}
|
||||
|
||||
export async function updateList(id: string, data: ShoppingListUpdate): Promise<ShoppingListDetail> {
|
||||
return handleResponse(await fetch(`${BASE}/lists/${id}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
}))
|
||||
}
|
||||
|
||||
export async function deleteList(id: string): Promise<void> {
|
||||
return handleResponse(await fetch(`${BASE}/lists/${id}`, { method: 'DELETE' }))
|
||||
}
|
||||
|
||||
export async function addItem(listId: string, data: ShoppingItemCreate): Promise<ShoppingItem> {
|
||||
return handleResponse(await fetch(`${BASE}/lists/${listId}/items`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
}))
|
||||
}
|
||||
|
||||
export async function updateItem(listId: string, itemId: string, data: ShoppingItemUpdate): Promise<ShoppingItem> {
|
||||
return handleResponse(await fetch(`${BASE}/lists/${listId}/items/${itemId}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
}))
|
||||
}
|
||||
|
||||
export async function deleteItem(listId: string, itemId: string): Promise<void> {
|
||||
return handleResponse(await fetch(`${BASE}/lists/${listId}/items/${itemId}`, { method: 'DELETE' }))
|
||||
}
|
||||
|
||||
export async function finishShopping(listId: string): Promise<ShoppingListDetail> {
|
||||
return handleResponse(await fetch(`${BASE}/lists/${listId}/finish`, { method: 'POST' }))
|
||||
}
|
||||
|
||||
export async function generateMagicList(): Promise<ShoppingListDetail> {
|
||||
return handleResponse(await fetch(`${BASE}/lists/generate`, { method: 'POST' }))
|
||||
}
|
||||
Reference in New Issue
Block a user