cardamonRecipeForm.tsx5 matches
97switch (parseMode) {
98case 'url':
99endpoint = '/api/parse/url';
100requestData = { type: 'url', content: parseInput };
101break;
102case 'text':
103endpoint = '/api/parse/text';
104requestData = { type: 'url', content: parseInput }; // Note: backend expects 'url' type for text parsing
105break;
110
111if (uploadedFile.type.startsWith('image/')) {
112endpoint = '/api/parse/image';
113requestData = { type: 'image', content: parseInput };
114} else if (uploadedFile.type === 'application/pdf') {
115endpoint = '/api/parse/pdf';
116requestData = { type: 'pdf', content: parseInput };
117} else {
198};
199200const url = isEditing ? `/api/recipes/${recipe.id}` : '/api/recipes';
201const method = isEditing ? 'PUT' : 'POST';
202
15await runMigrations();
1617// API routes
18app.route('/api/recipes', recipesRoutes);
19app.route('/api/parse', parseRoutes);
2021// Health check endpoint
22app.get('/api/health', (c) => {
23return c.json({ status: 'ok', timestamp: new Date().toISOString() });
24});
2526// Test delete endpoint
27app.get('/api/test-delete', async (c) => {
28try {
29const { createRecipe, deleteRecipe, getAllRecipes } = await import('./database/queries.ts');
cardamonRecipeView.tsx1 match
34const handleDelete = async () => {
35try {
36const response = await fetch(`/api/recipes/${recipe.id}`, {
37method: 'DELETE'
38});
cardamonRecipeList.tsx1 match
48const handleDelete = async (recipeId: number) => {
49try {
50const response = await fetch(`/api/recipes/${recipeId}`, {
51method: 'DELETE'
52});
33setState(prev => ({ ...prev, loading: true, error: null }));
34try {
35const response = await fetch('/api/recipes');
36const data = await response.json();
37
cardamonrecipes.ts20 matches
1import { Hono } from "https://esm.sh/hono@3.11.7";
2import type { Recipe, RecipeFilters, ApiResponse } from "../../shared/types.ts";
3import {
4createRecipe,
2829const recipes = await getAllRecipes(filters);
30return c.json({ success: true, data: recipes } as ApiResponse<Recipe[]>);
31} catch (error) {
32console.error('Error fetching recipes:', error);
33return c.json({ success: false, error: 'Failed to fetch recipes' } as ApiResponse, 500);
34}
35});
40const id = parseInt(c.req.param('id'));
41if (isNaN(id)) {
42return c.json({ success: false, error: 'Invalid recipe ID' } as ApiResponse, 400);
43}
4445const recipe = await getRecipeById(id);
46if (!recipe) {
47return c.json({ success: false, error: 'Recipe not found' } as ApiResponse, 404);
48}
4950return c.json({ success: true, data: recipe } as ApiResponse<Recipe>);
51} catch (error) {
52console.error('Error fetching recipe:', error);
53return c.json({ success: false, error: 'Failed to fetch recipe' } as ApiResponse, 500);
54}
55});
65success: false,
66error: 'Missing required fields: title, ingredients, and steps are required'
67} as ApiResponse, 400);
68}
6972success: false,
73error: 'At least one ingredient is required'
74} as ApiResponse, 400);
75}
7679success: false,
80error: 'At least one cooking step is required'
81} as ApiResponse, 400);
82}
8384const recipe = await createRecipe(recipeData);
85return c.json({ success: true, data: recipe } as ApiResponse<Recipe>, 201);
86} catch (error) {
87console.error('Error creating recipe:', error);
88return c.json({ success: false, error: 'Failed to create recipe' } as ApiResponse, 500);
89}
90});
95const id = parseInt(c.req.param('id'));
96if (isNaN(id)) {
97return c.json({ success: false, error: 'Invalid recipe ID' } as ApiResponse, 400);
98}
99102const recipe = await updateRecipe(id, updates);
103if (!recipe) {
104return c.json({ success: false, error: 'Recipe not found' } as ApiResponse, 404);
105}
106107return c.json({ success: true, data: recipe } as ApiResponse<Recipe>);
108} catch (error) {
109console.error('Error updating recipe:', error);
110return c.json({ success: false, error: 'Failed to update recipe' } as ApiResponse, 500);
111}
112});
117const id = parseInt(c.req.param('id'));
118if (isNaN(id)) {
119return c.json({ success: false, error: 'Invalid recipe ID' } as ApiResponse, 400);
120}
121122const deleted = await deleteRecipe(id);
123if (!deleted) {
124return c.json({ success: false, error: 'Recipe not found' } as ApiResponse, 404);
125}
126127return c.json({ success: true, data: { deleted: true } } as ApiResponse);
128} catch (error) {
129console.error('Error deleting recipe:', error);
130return c.json({ success: false, error: 'Failed to delete recipe' } as ApiResponse, 500);
131}
132});
38}
3940export interface ApiResponse<T = any> {
41success: boolean;
42data?: T;
2021- **Frontend**: React with TypeScript, TailwindCSS
22- **Backend**: Hono API framework
23- **Database**: SQLite for recipe storage
24- **AI**: OpenAI GPT-4 for intelligent recipe parsing
35โ โ โโโ recipes.ts # Recipe CRUD operations
36โ โ โโโ parse.ts # Recipe parsing endpoints
37โ โโโ index.ts # Main API entry point
38โโโ frontend/
39โ โโโ components/
48```
4950## API Endpoints
5152- `POST /api/parse/url` - Parse recipe from URL
53- `POST /api/parse/pdf` - Parse recipe from PDF
54- `POST /api/parse/image` - Parse recipe from image
55- `GET /api/recipes` - Get all recipes
56- `POST /api/recipes` - Save a recipe
57- `GET /api/recipes/:id` - Get specific recipe
58- `PUT /api/recipes/:id` - Update recipe
59- `DELETE /api/recipes/:id` - Delete recipe
6061## Usage
5Generated using Val.town
67Relies on the Zoomin Software bundle API to fetch the data using the JSON API, and then pluck and render the HTML part.
1export default async function (req: Request): Promise<Response> {
2try {
3const response = await fetch("https://help-be.zerto.com/api/bundle/Lifecycle.Matrix.HTML/page/product_version_lifecycle_matrix_for_zerto.html");
4
5if (!response.ok) {