Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/app/api/generate/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,16 @@ Use this exact structure:
"protein": 30,
"carbs": 45,
"fat": 20
}
},
"estimatedCost": 12.50,
"takeoutEquivalent": 25.00
}
]
}

Generate 5 diverse recipes with realistic nutritional information and accurate prep/cook times in minutes. Make them practical and delicious.`,
Generate 5 diverse recipes with realistic nutritional information and accurate prep/cook times in minutes. Make them practical and delicious.

For each recipe, include estimatedCost (realistic total grocery cost for ingredients in USD) and takeoutEquivalent (what a similar meal would cost at a restaurant or delivery service in USD). Be realistic with pricing.`,
},
],
},
Expand Down
2 changes: 2 additions & 0 deletions src/app/generate/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ interface Recipe {
carbs: number;
fat: number;
};
estimatedCost: number;
takeoutEquivalent: number;
}

export default function GeneratePage() {
Expand Down
44 changes: 26 additions & 18 deletions src/components/InstructionsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
"use client";

import { useState } from "react";
import { useState, ReactNode } from "react";

interface InstructionsSectionProps {
recipeName: string;
ingredients: string[];
instructions: string[];
detailedInstructions: string[] | null;
onDetailedInstructionsLoaded: (instructions: string[]) => void;
additionalButtons?: ReactNode;
}

export default function InstructionsSection({
Expand All @@ -16,6 +17,7 @@ export default function InstructionsSection({
instructions,
detailedInstructions,
onDetailedInstructionsLoaded,
additionalButtons,
}: InstructionsSectionProps) {
const [isLoadingDetails, setIsLoadingDetails] = useState(false);
const [detailsError, setDetailsError] = useState<string | null>(null);
Expand Down Expand Up @@ -61,24 +63,30 @@ export default function InstructionsSection({
))}
</ol>

{/* Detailed Instructions Button */}
{!detailedInstructions && (
<button
onClick={(e) => {
e.stopPropagation();
handleExpandInstructions();
}}
disabled={isLoadingDetails}
className="mt-4 minecraft-text bg-black/30 px-4 py-2 border-2 border-black/50 text-sm hover:bg-black/50 transition-colors disabled:opacity-50 hover:cursor-pointer"
>
{isLoadingDetails ? "Loading..." : "Detailed Instructions"}
</button>
)}
{/* Action Buttons Row */}
<div className="mt-4 flex flex-wrap gap-3 items-center">
{/* Detailed Instructions Button */}
{!detailedInstructions && (
<button
onClick={(e) => {
e.stopPropagation();
handleExpandInstructions();
}}
disabled={isLoadingDetails}
className="minecraft-text bg-black/30 px-4 py-2 border-2 border-black/50 text-sm hover:bg-black/50 transition-colors disabled:opacity-50 hover:cursor-pointer"
>
{isLoadingDetails ? "Loading..." : "Detailed Instructions"}
</button>
)}

{/* Show label when detailed instructions are displayed */}
{detailedInstructions && (
<p className="mt-3 text-xs opacity-60">βœ“ Showing detailed instructions</p>
)}
{/* Show label when detailed instructions are displayed */}
{detailedInstructions && (
<p className="text-xs opacity-60">βœ“ Showing detailed instructions</p>
)}

{/* Additional buttons passed from parent */}
{additionalButtons}
</div>

{/* Error message */}
{detailsError && <p className="mt-2 text-sm text-red-400">{detailsError}</p>}
Expand Down
33 changes: 33 additions & 0 deletions src/components/RecipeCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ interface Recipe {
carbs: number;
fat: number;
};
estimatedCost: number;
takeoutEquivalent: number;
}

interface RecipeCardProps {
Expand All @@ -30,11 +32,13 @@ export default function RecipeCard({ recipe }: RecipeCardProps) {
const [isOpen, setIsOpen] = useState(false);
const [copied, setCopied] = useState(false);
const [detailedInstructions, setDetailedInstructions] = useState<string[] | null>(null);
const [showCostBreakdown, setShowCostBreakdown] = useState(false);
const playClick = useClickSound();
const playPageTurn = usePageTurnSound();
const playPageBack = usePageBackSound();

const handleCopy = async () => {
const savings = recipe.takeoutEquivalent - recipe.estimatedCost;
const recipeText = `
${recipe.name}

Expand All @@ -44,6 +48,8 @@ Time: Prep ${recipe.prepTime} min | Cook ${recipe.cookTime} min | Total ${recipe

Nutrition: ${recipe.macros.calories} cal | ${recipe.macros.protein}g protein | ${recipe.macros.carbs}g carbs | ${recipe.macros.fat}g fat

Cost: ~$${recipe.estimatedCost.toFixed(2)} | Takeout: ~$${recipe.takeoutEquivalent.toFixed(2)} | You save: $${savings.toFixed(2)}

Ingredients:
${recipe.ingredients.map((ing) => `β€’ ${ing}`).join("\n")}

Expand Down Expand Up @@ -139,7 +145,34 @@ ${recipe.instructions.map((step, i) => `${i + 1}. ${step}`).join("\n")}
instructions={recipe.instructions}
detailedInstructions={detailedInstructions}
onDetailedInstructionsLoaded={setDetailedInstructions}
additionalButtons={
<button
onClick={(e) => {
e.stopPropagation();
playClick();
setShowCostBreakdown(!showCostBreakdown);
}}
className="minecraft-text bg-black/30 px-4 py-2 border-2 border-black/50 text-sm hover:bg-black/50 transition-colors hover:cursor-pointer"
>
{showCostBreakdown ? "Hide Cost Breakdown" : "Cost Breakdown"}
</button>
}
/>

{/* Cost Breakdown Content */}
{showCostBreakdown && (
<div className="mt-3 flex flex-wrap gap-4 text-sm">
<span className="bg-black/30 px-3 py-1 border-2 border-black/50">
πŸ›’ Grocery Cost: ~${recipe.estimatedCost.toFixed(2)}
</span>
<span className="bg-black/30 px-3 py-1 border-2 border-black/50">
πŸ” Takeout Equivalent: ~${recipe.takeoutEquivalent.toFixed(2)}
</span>
<span className="bg-green-900/50 px-3 py-1 border-2 border-green-700/50">
βœ… You Save: ${(recipe.takeoutEquivalent - recipe.estimatedCost).toFixed(2)}
</span>
</div>
)}
</>
)}
</div>
Expand Down