From 25ce1007e5b6e6da459cff14c96ff5c9a0fe8493 Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 4 Mar 2026 19:04:18 +0000 Subject: [PATCH 1/5] Add src/app/app/page.tsx --- src/app/app/page.tsx | 418 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 418 insertions(+) create mode 100644 src/app/app/page.tsx diff --git a/src/app/app/page.tsx b/src/app/app/page.tsx new file mode 100644 index 0000000..a18daa7 --- /dev/null +++ b/src/app/app/page.tsx @@ -0,0 +1,418 @@ +"use client"; + +import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider"; +import NavbarStyleFullscreen from '@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen'; +import FooterMedia from '@/components/sections/footer/FooterMedia'; +import { useState, useEffect } from "react"; +import { ChefHat, LogOut, User, Heart, Settings, Plus, X, Sparkles, Clock, Users2 } from "lucide-react"; +import Link from "next/link"; + +export default function AppPage() { + const [isLoggedIn, setIsLoggedIn] = useState(false); + const [user, setUser] = useState(null); + const [ingredients, setIngredients] = useState([]); + const [currentInput, setCurrentInput] = useState(""); + const [recipes, setRecipes] = useState([]); + const [loading, setLoading] = useState(false); + const [savedRecipes, setSavedRecipes] = useState([]); + const [showSavedOnly, setShowSavedOnly] = useState(false); + const [showProfile, setShowProfile] = useState(false); + + useEffect(() => { + // Check if user is logged in + const checkAuth = async () => { + try { + const response = await fetch('/api/auth/me'); + if (response.ok) { + const data = await response.json(); + setUser(data.user); + setIsLoggedIn(true); + setSavedRecipes(data.user.savedRecipes || []); + } else { + window.location.href = '/auth'; + } + } catch (error) { + window.location.href = '/auth'; + } + }; + checkAuth(); + }, []); + + 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); + } + }; + + const saveRecipe = async (recipe: any) => { + try { + const response = await fetch("/api/recipes/save", { + method: "POST", headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ recipe }) + }); + + if (response.ok) { + setSavedRecipes([...savedRecipes, recipe]); + } + } catch (error) { + console.error("Failed to save recipe:", error); + } + }; + + const handleLogout = async () => { + try { + await fetch("/api/auth/logout", { method: "POST" }); + window.location.href = "/"; + } catch (error) { + console.error("Logout failed:", error); + } + }; + + if (!isLoggedIn || !user) { + return ( + +
+
+
+ +
+

Loading your kitchen...

+
+
+
+ ); + } + + const displayRecipes = showSavedOnly ? savedRecipes : recipes; + + return ( + + + +
+
+ {/* Header with Profile */} +
+
+
+ +
+
+

Welcome back!

+

{user?.email}

+
+
+
+ + +
+
+ + {/* Profile Card */} + {showProfile && ( +
+
+

Your Profile

+ +
+
+
+

Full Name

+

{user?.fullName}

+
+
+

Email

+

{user?.email}

+
+
+

Saved Recipes

+

{savedRecipes.length}

+
+
+ +
+ )} + + {/* Main Generator Section */} +
+ {/* Left - Ingredient Input */} +
+
+

+ + Your Ingredients +

+ + {/* Input */} +
+ setCurrentInput(e.target.value)} + onKeyPress={handleKeyPress} + placeholder="e.g., chicken" + className="flex-1 px-4 py-2.5 bg-white/10 border border-white/20 rounded-xl focus:outline-none focus:border-purple-500 dark:bg-white/5 dark:border-white/10 text-gray-900 dark:text-white placeholder-gray-500 dark:placeholder-gray-400" + /> + +
+ + {/* Ingredients List */} + {ingredients.length > 0 && ( +
+

Added ({ingredients.length}):

+
+ {ingredients.map((ingredient, index) => ( +
+ {ingredient} + +
+ ))} +
+
+ )} + + {/* Generate Button */} + +
+
+ + {/* Right - Recipes Display */} +
+ {/* Toggle Buttons */} +
+ + +
+ + {/* Recipes Grid */} + {displayRecipes.length > 0 ? ( +
+ {displayRecipes.map((recipe, index) => ( +
+
+
+

+ {recipe.name} +

+

+ {recipe.description} +

+
+ +
+ +
+
+
Ingredients:
+
    + {recipe.ingredients?.slice(0, 4).map((ing: string, i: number) => ( +
  • + + {ing} +
  • + ))} + {recipe.ingredients?.length > 4 && ( +
  • +{recipe.ingredients.length - 4} more
  • + )} +
+
+
+
+ + {recipe.cookTime || 30} mins +
+
+ + Serves {recipe.servings || 2} +
+
+
+ +

+ {recipe.instructions} +

+
+ ))} +
+ ) : ( +
+ +

+ {showSavedOnly + ? "No saved recipes yet. Generate some and save your favorites!" + : "Add ingredients and generate recipes to get started"} +

+
+ )} +
+
+
+
+ + +
+ ); +} -- 2.49.1 From 7d0711fa12e20ec62fe352c4157b299558b3312b Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 4 Mar 2026 19:04:20 +0000 Subject: [PATCH 2/5] Add src/app/auth/page.tsx --- src/app/auth/page.tsx | 298 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 src/app/auth/page.tsx diff --git a/src/app/auth/page.tsx b/src/app/auth/page.tsx new file mode 100644 index 0000000..3642a84 --- /dev/null +++ b/src/app/auth/page.tsx @@ -0,0 +1,298 @@ +"use client"; + +import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider"; +import NavbarStyleFullscreen from '@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen'; +import { useState } from "react"; +import Link from "next/link"; +import { Mail, Lock, User, Eye, EyeOff, ArrowRight, CheckCircle, AlertCircle } from "lucide-react"; + +export default function AuthPage() { + const [isLogin, setIsLogin] = useState(true); + const [showPassword, setShowPassword] = useState(false); + const [loading, setLoading] = useState(false); + const [message, setMessage] = useState<{ type: 'success' | 'error' | ''; text: string }>({ type: '', text: '' }); + const [formData, setFormData] = useState({ + email: '', + password: '', + confirmPassword: '', + fullName: '' + }); + + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData(prev => ({ ...prev, [name]: value })); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + setMessage({ type: '', text: '' }); + + try { + const endpoint = isLogin ? '/api/auth/login' : '/api/auth/signup'; + const body = isLogin + ? { email: formData.email, password: formData.password } + : { email: formData.email, password: formData.password, fullName: formData.fullName }; + + const response = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body) + }); + + const data = await response.json(); + + if (response.ok) { + setMessage({ type: 'success', text: isLogin ? 'Login successful!' : 'Account created successfully!' }); + setTimeout(() => { + window.location.href = '/app'; + }, 1500); + } else { + setMessage({ type: 'error', text: data.error || 'Something went wrong' }); + } + } catch (error) { + setMessage({ type: 'error', text: 'Connection error. Please try again.' }); + } finally { + setLoading(false); + } + }; + + const handleForgotPassword = async () => { + if (!formData.email) { + setMessage({ type: 'error', text: 'Please enter your email address' }); + return; + } + setLoading(true); + try { + const response = await fetch('/api/auth/forgot-password', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: formData.email }) + }); + if (response.ok) { + setMessage({ type: 'success', text: 'Recovery email sent! Check your inbox.' }); + } else { + setMessage({ type: 'error', text: 'Email not found' }); + } + } catch (error) { + setMessage({ type: 'error', text: 'Failed to send recovery email' }); + } finally { + setLoading(false); + } + }; + + return ( + + + +
+
+ {/* Liquid Glass Card */} +
+ {/* Header */} +
+

+ {isLogin ? 'Welcome Back' : 'Join EasyRecipes'} +

+

+ {isLogin ? 'Sign in to your account' : 'Create your account to get started'} +

+
+ + {/* Message Display */} + {message.text && ( +
+ {message.type === 'success' ? ( + + ) : ( + + )} + {message.text} +
+ )} + + {/* Form */} +
+ {!isLogin && ( +
+ +
+ + +
+
+ )} + +
+ +
+ + +
+
+ +
+ +
+ + + +
+
+ + {!isLogin && ( +
+ +
+ + +
+
+ )} + + {/* Forgot Password */} + {isLogin && ( +
+ +
+ )} + + {/* Submit Button */} + +
+ + {/* Toggle Auth Mode */} +
+

+ {isLogin ? "Don't have an account? " : 'Already have an account? '} + +

+
+ + {/* Divider */} +
+
+ or +
+
+ + {/* OAuth Buttons */} +
+ +
+
+ + {/* Security Note */} +
+

+ 🔒 Your data is encrypted and secure. We never share your information. +

+
+
+
+
+ ); +} -- 2.49.1 From eb9a36ca3831f5534d3173ff15268befc37be2bd Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 4 Mar 2026 19:04:21 +0000 Subject: [PATCH 3/5] Update src/app/globals.css --- src/app/globals.css | 79 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index f624db2..b0837f3 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,5 +1,74 @@ -@import "tailwindcss"; -@import "./styles/variables.css"; -@import "./styles/theme.css"; -@import "./styles/utilities.css"; -@import "./styles/base.css"; +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + body { + @apply antialiased; + } + + h1, h2, h3, h4, h5, h6 { + @apply font-bold; + } +} + +@layer components { + .rounded-theme { + @apply rounded-2xl; + } + + .rounded-theme-capped { + @apply rounded-3xl; + } + + .glass { + @apply backdrop-blur-md bg-white/10 border border-white/20 rounded-2xl; + } + + .glass-elevated { + @apply backdrop-blur-xl bg-white/10 dark:bg-white/5 border border-white/20 dark:border-white/10 rounded-3xl shadow-2xl; + } +} + +@layer utilities { + .text-balance { + text-wrap: balance; + } + + .w-content-width { + width: var(--width-content-width); + } + + .max-w-content-width { + max-width: var(--width-content-width); + } + + .mask-fade-top-long { + mask-image: linear-gradient(to bottom, transparent 0%, black 20%); + -webkit-mask-image: linear-gradient(to bottom, transparent 0%, black 20%); + } + + .mask-fade-bottom { + mask-image: linear-gradient(to top, transparent 0%, black 20%); + -webkit-mask-image: linear-gradient(to top, transparent 0%, black 20%); + } + + .mask-fade-left { + mask-image: linear-gradient(to right, transparent 0%, black 20%); + -webkit-mask-image: linear-gradient(to right, transparent 0%, black 20%); + } + + .mask-fade-right { + mask-image: linear-gradient(to left, transparent 0%, black 20%); + -webkit-mask-image: linear-gradient(to left, transparent 0%, black 20%); + } + + .no-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; + } + + .no-scrollbar::-webkit-scrollbar { + display: none; + } +} -- 2.49.1 From 6246fb8a98975162060431a746a0a897010172bc Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 4 Mar 2026 19:04:22 +0000 Subject: [PATCH 4/5] Update src/app/layout.tsx --- src/app/layout.tsx | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d554cee..87aa283 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,37 +1,25 @@ import type { Metadata } from "next"; import { Inter } from "next/font/google"; +import "./styles/variables.css"; import "./globals.css"; -import { ServiceWrapper } from "@/components/ServiceWrapper"; -import Tag from "@/tag/Tag"; const inter = Inter({ variable: "--font-inter", subsets: ["latin"], }); export const metadata: Metadata = { - title: "EasyRecipes - AI-Powered Recipe Generation", description: "Discover amazing recipes with AI recipe generation, ingredient scanner, and smart pantry system. Create delicious meals with what you have.", keywords: "recipe generator, AI recipes, ingredient scanner, smart pantry, cooking app", openGraph: { - title: "EasyRecipes - AI-Powered Recipe Generation", description: "Transform your cooking with AI-powered recipes and smart ingredient management.", siteName: "EasyRecipes", type: "website" - }, - robots: { - index: true, - follow: true, - }, -}; + title: "EasyRecipes - AI-Powered Recipe Generator", description: "Generate delicious recipes from your ingredients using AI"}; export default function RootLayout({ children, -}: Readonly<{ +}: { children: React.ReactNode; -}>) { +}) { return ( - - - - - {children} - + + + {children} +