feat: stabilization + recipe edit/create UI

This commit is contained in:
clawd
2026-02-18 09:55:39 +00:00
commit ee452efa6a
75 changed files with 15160 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
import { apiFetch } from './client'
import type { Recipe, PaginatedResponse } from './types'
interface RecipeListParams {
category?: string
favorite?: boolean
sort?: string
page?: number
limit?: number
}
export function fetchRecipes(params?: RecipeListParams) {
const sp = new URLSearchParams()
if (params?.category) sp.set('category', params.category)
if (params?.favorite) sp.set('favorite', 'true')
if (params?.sort) sp.set('sort', params.sort)
if (params?.page) sp.set('page', String(params.page))
if (params?.limit) sp.set('limit', String(params.limit))
const qs = sp.toString()
return apiFetch<PaginatedResponse<Recipe>>(`/recipes${qs ? `?${qs}` : ''}`)
}
export function fetchRecipe(slug: string) {
return apiFetch<Recipe>(`/recipes/${slug}`)
}
export function searchRecipes(q: string) {
return apiFetch<PaginatedResponse<Recipe>>(`/recipes/search?q=${encodeURIComponent(q)}`)
}
export function toggleFavorite(id: string) {
return apiFetch<Recipe>(`/recipes/${id}/favorite`, { method: 'PATCH' })
}
export interface RecipeFormData {
title: string
description?: string
category_id?: string
difficulty?: 'easy' | 'medium' | 'hard'
prep_time?: number
cook_time?: number
servings?: number
image_url?: string
source_url?: string
ingredients?: { amount?: number; unit?: string; name: string; group_name?: string; sort_order?: number }[]
steps?: { step_number: number; instruction: string; duration_minutes?: number }[]
}
export function createRecipe(data: RecipeFormData) {
return apiFetch<Recipe>('/recipes', {
method: 'POST',
body: JSON.stringify(data),
})
}
export function updateRecipe(id: string, data: RecipeFormData) {
return apiFetch<Recipe>(`/recipes/${id}`, {
method: 'PUT',
body: JSON.stringify(data),
})
}
export function deleteRecipe(id: string) {
return apiFetch<{ ok: boolean }>(`/recipes/${id}`, { method: 'DELETE' })
}
export function uploadRecipeImage(id: string, file: File) {
const formData = new FormData()
formData.append('file', file)
return fetch(`/api/recipes/${id}/image`, { method: 'POST', body: formData }).then(r => {
if (!r.ok) throw new Error('Upload failed')
return r.json() as Promise<{ image_url: string }>
})
}