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