Skip to content

Commit

Permalink
Merge pull request #84 from kumarkrishh/edit-recipe
Browse files Browse the repository at this point in the history
Edit recipe
  • Loading branch information
xuandy05 authored Dec 3, 2024
2 parents b1301c8 + 00043e1 commit d024747
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 33 deletions.
46 changes: 46 additions & 0 deletions app/api/updateRecipe/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { NextResponse } from 'next/server';
import { getServerSession } from 'next-auth/next';
import { authOptions } from '../auth/[...nextauth]/route';
import { getClientPromise } from '@/lib/mongodb';

export async function PUT(request: Request) {
const session = await getServerSession(authOptions);

if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

try {
const recipeData = await request.json();
const client = await getClientPromise();
const db = client.db();

// Ensure the 'id' field exists and is a string
if (!recipeData.id) {
return NextResponse.json({ error: 'Recipe ID is missing' }, { status: 400 });
}

recipeData.id = recipeData.id.toString();

// Remove '_id' field if it exists to prevent immutable field update error
if (recipeData._id) {
delete recipeData._id;
}

// Update the recipe in the database
const result = await db.collection('recipes').updateOne(
{ id: recipeData.id, userId: session.user.id },
{ $set: recipeData },
{ upsert: true }
);

if (result.modifiedCount > 0 || result.upsertedCount > 0) {
return NextResponse.json({ message: 'Recipe updated successfully!' }, { status: 200 });
} else {
return NextResponse.json({ error: 'Failed to update recipe' }, { status: 500 });
}
} catch (error) {
console.error('Error updating recipe:', error);
return NextResponse.json({ error: 'Failed to update recipe' }, { status: 500 });
}
}
62 changes: 45 additions & 17 deletions app/saved-recipes/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import axios from "axios";
import { useSession } from "next-auth/react";
import Navbar from '../../components/Navbar';
import RecipeCard from '../../components/RecipeCard';
import EditRecipeCard from '@/components/EditRecipeCard';

function SavedRecipesPage() {
const { data: session, status } = useSession();
const [recipes, setRecipes] = useState<any[]>([]);
const [favoritedRecipes, setFavoritedRecipes] = useState<Set<string>>(new Set());
const [editingRecipe, setEditingRecipe] = useState<any | null>(null);

useEffect(() => {
if (session) {
Expand Down Expand Up @@ -57,6 +59,23 @@ function SavedRecipesPage() {
}
};

const handleEditRecipe = (recipe: any) => {
setEditingRecipe(recipe);
};

const handleSaveEditedRecipe = (editedRecipe: any) => {
setRecipes(prevRecipes =>
prevRecipes.map(recipe =>
recipe.id === editedRecipe.id ? editedRecipe : recipe
)
);
setEditingRecipe(null);
};

const handleCancelEdit = () => {
setEditingRecipe(null);
};

if (status === "loading") return <p>Loading...</p>;
if (!session) return <p>You need to be logged in to view this page.</p>;

Expand All @@ -67,26 +86,35 @@ function SavedRecipesPage() {
<h1 className="text-3xl font-bold text-center mb-8 bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">
Favorite Recipes
</h1>
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
{recipes.length > 0 ? (
recipes.map((recipe) => (
<RecipeCard
key={recipe.id}
recipe={recipe}
isFavorited={true}
onFavoriteToggle={handleFavoriteToggle}
ingredientVariants={[]}
disableIngredientColor={true}
/>
))
) : (
<p className="text-center text-gray-600">No saved recipes found.</p>
)}
</div>

{editingRecipe ? (
<EditRecipeCard
recipe={editingRecipe}
onSave={handleSaveEditedRecipe}
onCancel={handleCancelEdit}
/>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
{recipes.length > 0 ? (
recipes.map((recipe) => (
<RecipeCard
key={recipe.id}
recipe={recipe}
isFavorited={true}
onFavoriteToggle={handleFavoriteToggle}
ingredientVariants={[]}
disableIngredientColor={true}
onEditRecipe={handleEditRecipe}
/>
))
) : (
<p className="text-center text-gray-600">No saved recipes found.</p>
)}
</div>
)}
</div>
</div>
);
}

export default SavedRecipesPage;

68 changes: 52 additions & 16 deletions components/EditRecipeCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Checkbox } from '@/components/ui/checkbox';
import { MoreHorizontal, Plus } from 'lucide-react';
import { X, Plus } from 'lucide-react';
import OpenAI from 'openai';
import { useCallback } from 'react';




const openaiKey = process.env.NEXT_PUBLIC_OPENAI_API_KEY;
let openai: OpenAI;
if (openaiKey) {
Expand Down Expand Up @@ -56,6 +53,14 @@ export default function EditRecipeCard({
setIngredients([...ingredients, { id: Date.now(), original: '', selected: true }]);
};

const handleRemoveIngredient = (index: number) => {
setIngredients((prevIngredients: any) => {
const updatedIngredients = [...prevIngredients];
updatedIngredients.splice(index, 1);
return updatedIngredients;
});
};

const handleUpdateRecipe = async () => {
// console.log('Updating recipe...');
// console.log('Ingredients:', ingredients);
Expand All @@ -72,7 +77,11 @@ export default function EditRecipeCard({
},
{
role: "user",
content: `Update the following recipe with these new ingredients: ${selectedIngredients.map((ing: { original: any; }) => ing.original).join(', ')}. Original recipe: ${editedRecipe.instructions}. Strictly include the ingredient amount values and only use the new ingredients in the instructions. Stricly just write out the instructions very descriptively, nothing else.`
content: `Here is the original recipe: ${editedRecipe.instructions}. \
Here are the only ingredients we have: ${selectedIngredients.map((ing: { original: any; }) => ing.original).join(', ')}. \
Edit the recipe as little as possible with these new ingredients. Make sure not to include any ingredient not in the list.\
Strictly include the ingredient amount values and only use the new ingredients in the instructions. \
Stricly just write out the instructions very descriptively, nothing else.`
}
],
temperature: 0.7,
Expand All @@ -91,19 +100,42 @@ export default function EditRecipeCard({
}
};


const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setEditedRecipe((prev: any) => ({ ...prev, [name]: value }));
}, [setEditedRecipe]); // Include all dependencies used inside the callback

const handleSave = () => {
const updatedRecipe = {
...editedRecipe,
extendedIngredients: ingredients,
};
onSave(updatedRecipe);
};
const handleSave = async () => {
const { _id, ...recipeToUpdate } = editedRecipe; // Exclude _id

const updatedRecipe = {
...recipeToUpdate,
extendedIngredients: ingredients,
};

try {
const response = await fetch('/api/updateRecipe', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(updatedRecipe),
});

const data = await response.json();

if (response.ok) {
// Update successful
console.log('Recipe updated successfully:', data);
onSave(updatedRecipe); // Optionally update parent state or close modal
} else {
// Handle error
console.error('Failed to update recipe:', data.error);
}
} catch (error) {
console.error('Error updating recipe:', error);
}
};

return (
<Card className="fixed inset-0 z-50 overflow-auto bg-white">
Expand Down Expand Up @@ -139,8 +171,12 @@ export default function EditRecipeCard({
onChange={(e) => handleIngredientEdit(index, e.target.value)}
className={`ml-2 flex-grow ${!ingredient.selected ? 'text-gray-400' : ''}`}
/>
<Button variant="ghost" size="sm">
<MoreHorizontal className="h-4 w-4" />
<Button
variant="ghost"
size="sm"
onClick={() => handleRemoveIngredient(index)}
>
<X className="h-4 w-4" />
</Button>
</div>
))}
Expand All @@ -156,7 +192,7 @@ export default function EditRecipeCard({
<Button onClick={onCancel} variant="outline" className="mr-2">
Cancel
</Button>
<Button onClick={() => onSave(editedRecipe)}>Save Changes</Button>
<Button onClick={handleSave}>Save Changes</Button>
</div>
</Card>
);
Expand Down

0 comments on commit d024747

Please sign in to comment.