diff --git a/app/api/recipe/[id]/route.js b/app/api/recipe/[id]/route.js new file mode 100644 index 0000000..402d31b --- /dev/null +++ b/app/api/recipe/[id]/route.js @@ -0,0 +1,22 @@ +import connectToDatabase from "../../../lib/connectMongoose"; +import Recipe from "../../../models/Recipe"; +import { NextResponse } from "next/server"; + +/** + * + * @param {*} req + * @param {id} param1 - This Will get the exact recipe id from database + * @returns - The recipe object by its Id + */ + +export async function GET(req, { params }) { + try { + let { id } = params; + await connectToDatabase(); + const recipe = await Recipe.findOne({ _id: id }); + + return NextResponse.json({ recipe }, { status: 200 }); + } catch (error) { + console.error(error); + } +} diff --git a/app/api/recipe/route.js b/app/api/recipe/route.js new file mode 100644 index 0000000..9adb9de --- /dev/null +++ b/app/api/recipe/route.js @@ -0,0 +1,16 @@ +import connectToDatabase from "../../../lib/connectMongoose"; +import Recipe from "../../../models/Recipe"; +import { NextResponse } from "next/server"; + +export async function GET(req) { + console.log('cjsd csdcdsc') + try { + let db = await connectToDatabase(); + console.log('mdcakmdcma') + const recipes = await Recipe.find({}).limit(50); + //console.log(recipes) + return NextResponse.json({ recipes }); + } catch (error) { + console.error(error); + } +} diff --git a/app/components/Footer.jsx b/app/components/Footer.jsx new file mode 100644 index 0000000..237906e --- /dev/null +++ b/app/components/Footer.jsx @@ -0,0 +1,141 @@ +import { AiOutlineArrowRight } from 'react-icons/ai'; // Correct icon import for ArrowRight +import { FaUtensils, FaEnvelope, FaPhone, FaMapPin, FaTwitter, FaYoutube, FaFacebook, FaInstagram } from 'react-icons/fa'; // Correct icon imports +import Link from 'next/link'; // Corrected import for Link + +const Footer = () => { + const socialLinks = [ + { + icon: FaTwitter, + href: 'https://twitter.com', + color: 'hover:text-[#1DA1F2] transition-colors duration-300', + label: 'Twitter' + }, + { + icon: FaYoutube, + href: 'https://youtube.com', + color: 'hover:text-[#FF0000] transition-colors duration-300', + label: 'YouTube' + }, + { + icon: FaFacebook, + href: 'https://facebook.com', + color: 'hover:text-[#4267B2] transition-colors duration-300', + label: 'Facebook' + }, + { + icon: FaInstagram, + href: 'https://instagram.com', + color: 'hover:text-[#E1306C] transition-colors duration-300', + label: 'Instagram' + }, + ]; + + const quickLinks = [ + { name: 'About Us', href: '/about' }, + { name: 'Contact', href: '/contact' }, + { name: 'Recipe Index', href: '/recipes' }, + { name: 'Privacy Policy', href: '/privacy' }, + { name: 'Terms of Service', href: '/terms' }, + ]; + + return ( + + ); +}; + +export default Footer; + diff --git a/app/components/Navbar.jsx b/app/components/Navbar.jsx index e69de29..329c7f5 100644 --- a/app/components/Navbar.jsx +++ b/app/components/Navbar.jsx @@ -0,0 +1,193 @@ +'use client'; + +import React, { useState, useEffect, useRef } from 'react'; +import Link from 'next/link'; + +const Navbar = ({ position }) => { + const [isSearchOpen, setIsSearchOpen] = useState(false); + const [isSubNavVisible, setIsSubNavVisible] = useState(false); + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + const [dragPosition, setDragPosition] = useState(0); + const [isDragging, setIsDragging] = useState(false); + const dragRef = useRef(null); + const startXRef = useRef(0); + + useEffect(() => { + let timeoutId; + const handleMouseMove = (e) => { + if (e.clientY <= 100) { + setIsSubNavVisible(true); + clearTimeout(timeoutId); + } else { + timeoutId = setTimeout(() => setIsSubNavVisible(false), 1000); + } + }; + + window.addEventListener('mousemove', handleMouseMove); + return () => { + window.removeEventListener('mousemove', handleMouseMove); + clearTimeout(timeoutId); + }; + }, []); + + const handleDragStart = (e) => { + setIsDragging(true); + startXRef.current = e.touches ? e.touches[0].clientX - dragPosition : e.clientX - dragPosition; + }; + + const handleDragMove = (e) => { + if (!isDragging) return; + + const currentX = e.touches ? e.touches[0].clientX : e.clientX; + const newPosition = currentX - startXRef.current; + + // Limit the drag range + const limitedPosition = Math.min(Math.max(newPosition, -100), 100); + setDragPosition(limitedPosition); + }; + + const handleDragEnd = () => { + setIsDragging(false); + }; + + const navLinks = [ + { name: 'Home', href: '/' }, + { + name: 'Recipes', + href: '/recipes', + sublinks: [ + { name: 'Breakfast', href: '/recipes/breakfast' }, + { name: 'Lunch', href: '/recipes/lunch' }, + { name: 'Dinner', href: '/recipes/dinner' }, + ] + }, + { name: 'Favorites', href: '/favorites' }, + { name: 'About', href: '/about' }, + { name: 'Contact', href: '/contact' }, + { + name: 'Account', + href: '/account', + sublinks: [ + { name: 'Sign Up', href: '/account/signup' }, + { name: 'Sign In', href: '/account/signin' }, + ] + }, + ]; + + return ( + <> + + + {/* Search Overlay */} +
+
+ +
+
+ + ); +}; + +export default Navbar; \ No newline at end of file diff --git a/app/components/RecipeCard.jsx b/app/components/RecipeCard.jsx new file mode 100644 index 0000000..23eb6e7 --- /dev/null +++ b/app/components/RecipeCard.jsx @@ -0,0 +1,31 @@ +// app/components/RecipeCard.jsx (rename from RecipeCards.jsx) +import React from 'react'; +import Link from 'next/link'; + +const RecipeCard = ({ recipe }) => { + return ( + +
+ {recipe.title} +
+

{recipe.title}

+

Prep time: {recipe.prep} mins

+

Cook time: {recipe.cook} mins

+ + +
+
+ + {recipe.rating} +
+
+ Servings: {recipe.servings} +
+
+
+
+ + ); +}; + +export default RecipeCard; \ No newline at end of file diff --git a/app/components/RecipeCards.jsx b/app/components/RecipeCards.jsx deleted file mode 100644 index e69de29..0000000 diff --git a/app/components/RecipeDetailSkeleton.jsx b/app/components/RecipeDetailSkeleton.jsx new file mode 100644 index 0000000..3108a69 --- /dev/null +++ b/app/components/RecipeDetailSkeleton.jsx @@ -0,0 +1,37 @@ +import React from 'react'; + +const RecipeSkeleton = () => { + return ( +
+ {/* Image Skeleton */} +
+ + {/* Text Skeleton */} +
+ {/* Title */} +
+ {/* Subtitle */} +
+ {/* Time and Servings */} +
+
+
+
+
+ {/* Ingredients/Instructions Tabs */} +
+
+
+
+ {/* Ingredients List */} +
+ {Array(6).fill('').map((_, index) => ( +
+ ))} +
+
+
+ ); +}; + +export default RecipeSkeleton; \ No newline at end of file diff --git a/app/components/RecipeGrid.jsx b/app/components/RecipeGrid.jsx index e69de29..eeb96cf 100644 --- a/app/components/RecipeGrid.jsx +++ b/app/components/RecipeGrid.jsx @@ -0,0 +1,40 @@ +// app/components/RecipeGrid.jsx +"use client" +import React, { useEffect, useState } from 'react'; +import RecipeCard from './RecipeCard'; // Update this import +import SkeletonGrid from './SkeletonMain'; + +const RecipeGrid = () => { + const [recipes, setRecipes] = useState(null); + + useEffect(() => { + const fetchRecipes = async () => { + console.log('hdbachbdashcb') + try { + console + const response = await fetch('http://localhost:3000/api/recipe',{cache:"no-store"}); + console.log(response) + const data = await response.json(); + console.log(data) + setRecipes(data.recipes); + } catch (error) { + console.error('Error fetching recipes:', error); + } + }; + fetchRecipes(); + }, []); + + if (!recipes) { + return + } + + return ( +
+ {recipes.map(recipe => ( + + ))} +
+ ); +}; + +export default RecipeGrid; \ No newline at end of file diff --git a/app/components/SkeletonMain.jsx b/app/components/SkeletonMain.jsx new file mode 100644 index 0000000..5257936 --- /dev/null +++ b/app/components/SkeletonMain.jsx @@ -0,0 +1,26 @@ +const SkeletonGrid = () => { + // console.log('12345678ioiuygfv') + return ( +
+
+ {/* Creating 8 skeletons to mimic the recipe cards */} + {Array(30).fill("").map((_, index) => ( +
+ {/* Image Skeleton */} +
+ {/* Text Skeleton */} +
+
+
+
+
+ ))} +
+
+ ); + }; + + export default SkeletonGrid; diff --git a/app/layout.js b/app/layout.js index 9800bf8..02e0b5e 100644 --- a/app/layout.js +++ b/app/layout.js @@ -1,30 +1,39 @@ -import localFont from "next/font/local"; -import "./globals.css"; - -const geistSans = localFont({ - src: "./fonts/GeistVF.woff", - variable: "--font-geist-sans", - weight: "100 900", -}); -const geistMono = localFont({ - src: "./fonts/GeistMonoVF.woff", - variable: "--font-geist-mono", - weight: "100 900", -}); - -export const metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; + +'use client'; +import { useState, useEffect } from 'react'; +import Navbar from './components/Navbar'; +import Footer from './components/Footer'; +import './globals.css'; export default function RootLayout({ children }) { + const [navbarPosition, setNavbarPosition] = useState(0); + + useEffect(() => { + const handleKeyDown = (e) => { + if (e.key === 'ArrowLeft') { + setNavbarPosition((prev) => Math.max(prev - 10, -100)); + } else if (e.key === 'ArrowRight') { + setNavbarPosition((prev) => Math.min(prev + 10, 100)); + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, []); + return ( - - {children} + + +
{/* Add padding-top to account for fixed navbar */} + {children} +
+