Merge version_3 into main #4
@@ -28,6 +28,7 @@ export default function ContactPage() {
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Features", id: "/features" },
|
||||
{ name: "Download", id: "/download" },
|
||||
{ name: "Generate", id: "/generate" },
|
||||
{ name: "Contact", id: "/contact" }
|
||||
]}
|
||||
button={{ text: "Get Started", href: "/download" }}
|
||||
@@ -84,13 +85,13 @@ export default function ContactPage() {
|
||||
title: "Product", items: [
|
||||
{ label: "Features", href: "/features" },
|
||||
{ label: "Download", href: "/download" },
|
||||
{ label: "Pricing", href: "#pricing" },
|
||||
{ label: "Pricing", href: "/" },
|
||||
{ label: "Help Center", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Company", items: [
|
||||
{ label: "About Us", href: "#about" },
|
||||
{ label: "About Us", href: "/" },
|
||||
{ label: "Careers", href: "#" },
|
||||
{ label: "Blog", href: "#" },
|
||||
{ label: "Press", href: "#" }
|
||||
|
||||
@@ -29,6 +29,7 @@ export default function DownloadPage() {
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Features", id: "/features" },
|
||||
{ name: "Download", id: "/download" },
|
||||
{ name: "Generate", id: "/generate" },
|
||||
{ name: "Contact", id: "/contact" }
|
||||
]}
|
||||
button={{ text: "Get Started", href: "/download" }}
|
||||
@@ -84,13 +85,13 @@ export default function DownloadPage() {
|
||||
title: "Product", items: [
|
||||
{ label: "Features", href: "/features" },
|
||||
{ label: "Download", href: "/download" },
|
||||
{ label: "Pricing", href: "#pricing" },
|
||||
{ label: "Pricing", href: "/" },
|
||||
{ label: "Help Center", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Company", items: [
|
||||
{ label: "About Us", href: "#about" },
|
||||
{ label: "About Us", href: "/" },
|
||||
{ label: "Careers", href: "#" },
|
||||
{ label: "Blog", href: "#" },
|
||||
{ label: "Press", href: "#" }
|
||||
|
||||
@@ -29,6 +29,7 @@ export default function FeaturesPage() {
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Features", id: "/features" },
|
||||
{ name: "Download", id: "/download" },
|
||||
{ name: "Generate", id: "/generate" },
|
||||
{ name: "Contact", id: "/contact" }
|
||||
]}
|
||||
button={{ text: "Get Started", href: "/download" }}
|
||||
@@ -113,13 +114,13 @@ export default function FeaturesPage() {
|
||||
title: "Product", items: [
|
||||
{ label: "Features", href: "/features" },
|
||||
{ label: "Download", href: "/download" },
|
||||
{ label: "Pricing", href: "#pricing" },
|
||||
{ label: "Pricing", href: "/" },
|
||||
{ label: "Help Center", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Company", items: [
|
||||
{ label: "About Us", href: "#about" },
|
||||
{ label: "About Us", href: "/" },
|
||||
{ label: "Careers", href: "#" },
|
||||
{ label: "Blog", href: "#" },
|
||||
{ label: "Press", href: "#" }
|
||||
|
||||
263
src/app/generate/page.tsx
Normal file
263
src/app/generate/page.tsx
Normal file
@@ -0,0 +1,263 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarLayoutFloatingInline from '@/components/navbar/NavbarLayoutFloatingInline';
|
||||
import HeroCentered from '@/components/sections/hero/HeroCentered';
|
||||
import FooterBaseReveal from '@/components/sections/footer/FooterBaseReveal';
|
||||
import { Sparkles, Mail, LogIn } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function GeneratePage() {
|
||||
const [ingredients, setIngredients] = useState<string[]>([]);
|
||||
const [currentInput, setCurrentInput] = useState("");
|
||||
const [recipes, setRecipes] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||
const [userEmail, setUserEmail] = useState("");
|
||||
|
||||
const handleGmailLogin = async () => {
|
||||
try {
|
||||
// Simulate Gmail OAuth flow
|
||||
const response = await fetch("/api/auth/gmail", {
|
||||
method: "POST"});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setUserEmail(data.email);
|
||||
setIsLoggedIn(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gmail login failed:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const addIngredient = () => {
|
||||
if (currentInput.trim()) {
|
||||
setIngredients([...ingredients, currentInput.trim()]);
|
||||
setCurrentInput("");
|
||||
}
|
||||
};
|
||||
|
||||
const removeIngredient = (index: number) => {
|
||||
setIngredients(ingredients.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
const handleKeyPress = (e: React.KeyboardEvent) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
addIngredient();
|
||||
}
|
||||
};
|
||||
|
||||
const generateRecipes = async () => {
|
||||
if (ingredients.length === 0) {
|
||||
alert("Please add at least one ingredient");
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await fetch("/api/recipes/generate", {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ ingredients }),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setRecipes(data.recipes || []);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Recipe generation failed:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="hover-magnetic"
|
||||
defaultTextAnimation="reveal-blur"
|
||||
borderRadius="pill"
|
||||
contentWidth="mediumLarge"
|
||||
sizing="mediumLargeSizeLargeTitles"
|
||||
background="floatingGradient"
|
||||
cardStyle="glass-elevated"
|
||||
primaryButtonStyle="radial-glow"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="semibold"
|
||||
>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarLayoutFloatingInline
|
||||
brandName="EasyRecipes"
|
||||
navItems={[
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Features", id: "/features" },
|
||||
{ name: "Download", id: "/download" },
|
||||
{ name: "Generate", id: "/generate" },
|
||||
{ name: "Contact", id: "/contact" }
|
||||
]}
|
||||
button={{ text: "Get Started", href: "/download" }}
|
||||
animateOnLoad={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="hero" data-section="hero">
|
||||
<HeroCentered
|
||||
title="Generate Recipes from Your Ingredients"
|
||||
description="Enter the ingredients you have on hand and let our AI create delicious recipe suggestions just for you. Create amazing meals with what you already have!"
|
||||
tag="AI Recipe Generator"
|
||||
tagIcon={Sparkles}
|
||||
background={{ variant: "sparkles-gradient" }}
|
||||
useInvertedBackground={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="generator" data-section="generator" className="w-full py-20 px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
{/* Login Section */}
|
||||
{!isLoggedIn && (
|
||||
<div className="mb-12 text-center">
|
||||
<h2 className="text-2xl font-bold mb-4">Sign in to Get Started</h2>
|
||||
<button
|
||||
onClick={handleGmailLogin}
|
||||
className="flex items-center justify-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-8 py-3 rounded-lg font-semibold transition-colors"
|
||||
>
|
||||
<Mail size={20} />
|
||||
Sign in with Gmail
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Generator Section */}
|
||||
{isLoggedIn && (
|
||||
<div className="space-y-8">
|
||||
<div className="text-center mb-8">
|
||||
<p className="text-lg text-gray-600 mb-2">Logged in as: {userEmail}</p>
|
||||
</div>
|
||||
|
||||
{/* Ingredient Input */}
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg p-8 shadow-lg">
|
||||
<h3 className="text-xl font-bold mb-4">Add Your Ingredients</h3>
|
||||
<div className="flex gap-2 mb-4">
|
||||
<input
|
||||
type="text"
|
||||
value={currentInput}
|
||||
onChange={(e) => setCurrentInput(e.target.value)}
|
||||
onKeyPress={handleKeyPress}
|
||||
placeholder="e.g., chicken, tomatoes, garlic"
|
||||
className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white"
|
||||
/>
|
||||
<button
|
||||
onClick={addIngredient}
|
||||
className="px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-semibold transition-colors"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Ingredients List */}
|
||||
{ingredients.length > 0 && (
|
||||
<div className="mb-6">
|
||||
<h4 className="font-semibold mb-3">Your Ingredients:</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{ingredients.map((ingredient, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center gap-2 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-100 px-4 py-2 rounded-full"
|
||||
>
|
||||
<span>{ingredient}</span>
|
||||
<button
|
||||
onClick={() => removeIngredient(index)}
|
||||
className="text-blue-600 dark:text-blue-300 hover:text-blue-800 font-bold"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Generate Button */}
|
||||
<button
|
||||
onClick={generateRecipes}
|
||||
disabled={loading || ingredients.length === 0}
|
||||
className="w-full px-6 py-3 bg-green-600 hover:bg-green-700 disabled:bg-gray-400 text-white rounded-lg font-semibold transition-colors"
|
||||
>
|
||||
{loading ? "Generating..." : "Generate Recipes"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Recipes Results */}
|
||||
{recipes.length > 0 && (
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-2xl font-bold mb-6">AI-Generated Recipes</h3>
|
||||
{recipes.map((recipe, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="bg-white dark:bg-gray-800 rounded-lg p-6 shadow-lg"
|
||||
>
|
||||
<h4 className="text-xl font-bold mb-2">{recipe.name}</h4>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4">
|
||||
{recipe.description}
|
||||
</p>
|
||||
<div className="mb-4">
|
||||
<h5 className="font-semibold mb-2">Ingredients:</h5>
|
||||
<ul className="list-disc pl-5 space-y-1 text-gray-700 dark:text-gray-300">
|
||||
{recipe.ingredients?.map((ing: string, i: number) => (
|
||||
<li key={i}>{ing}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h5 className="font-semibold mb-2">Instructions:</h5>
|
||||
<p className="text-gray-700 dark:text-gray-300">
|
||||
{recipe.instructions}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm text-gray-500">
|
||||
<span>⏱ {recipe.cookTime} mins</span>
|
||||
<span>👥 Serves {recipe.servings}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterBaseReveal
|
||||
columns={[
|
||||
{
|
||||
title: "Product", items: [
|
||||
{ label: "Features", href: "/features" },
|
||||
{ label: "Download", href: "/download" },
|
||||
{ label: "Pricing", href: "/" },
|
||||
{ label: "Help Center", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Company", items: [
|
||||
{ label: "About Us", href: "/" },
|
||||
{ label: "Careers", href: "#" },
|
||||
{ label: "Blog", href: "#" },
|
||||
{ label: "Press", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Support", items: [
|
||||
{ label: "Documentation", href: "#" },
|
||||
{ label: "Community", href: "#" },
|
||||
{ label: "Privacy Policy", href: "#" },
|
||||
{ label: "Terms of Service", href: "#" }
|
||||
]
|
||||
}
|
||||
]}
|
||||
copyrightText="© 2025 EasyRecipes | AI-Powered Cooking Made Easy"
|
||||
/>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -33,6 +33,7 @@ export default function LandingPage() {
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Features", id: "/features" },
|
||||
{ name: "Download", id: "/download" },
|
||||
{ name: "Generate", id: "/generate" },
|
||||
{ name: "Contact", id: "/contact" }
|
||||
]}
|
||||
button={{ text: "Get Started", href: "/download" }}
|
||||
@@ -209,7 +210,7 @@ export default function LandingPage() {
|
||||
background={{ variant: "radial-gradient" }}
|
||||
buttons={[
|
||||
{ text: "Download App", href: "/download" },
|
||||
{ text: "Contact Support", href: "#" }
|
||||
{ text: "Contact Support", href: "/contact" }
|
||||
]}
|
||||
useInvertedBackground={false}
|
||||
/>
|
||||
@@ -222,13 +223,13 @@ export default function LandingPage() {
|
||||
title: "Product", items: [
|
||||
{ label: "Features", href: "/features" },
|
||||
{ label: "Download", href: "/download" },
|
||||
{ label: "Pricing", href: "#pricing" },
|
||||
{ label: "Pricing", href: "/" },
|
||||
{ label: "Help Center", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Company", items: [
|
||||
{ label: "About Us", href: "#about" },
|
||||
{ label: "About Us", href: "/" },
|
||||
{ label: "Careers", href: "#" },
|
||||
{ label: "Blog", href: "#" },
|
||||
{ label: "Press", href: "#" }
|
||||
|
||||
Reference in New Issue
Block a user