Skip to content

Commit

Permalink
Merge pull request #66 from Cheplusplus/Productdetail
Browse files Browse the repository at this point in the history
Productdetail
  • Loading branch information
SABELOMAWELA authored Oct 23, 2024
2 parents 47c7b1d + d12f32c commit 6ba9c9d
Show file tree
Hide file tree
Showing 11 changed files with 1,428 additions and 74 deletions.
40 changes: 26 additions & 14 deletions app/api/recipe/[id]/route.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
const fetchSingleRecipe = async (id) => {
try {
const res = await fetch(`https://dummyjson.com/recipes/${id}`, {cache : 'force-cache'});
if (!res.ok) {
throw new Error("Fetch Product Failed");
}
const data = await res.json();
return data;
} catch (error) {
console.error("Failed to Fetch Data:", error)
}
};

export default fetchSingleRecipe;
import connectToDatabase from "../../../../lib/connectMongoose";
import Recipe from "../../../../models/Recipe";
import { NextResponse } from "next/server";
import mongoose from "mongoose";

/**
*
* @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;
console.log(id)
// id = new mongoose.Types.ObjectId();
console.log(id)
await connectToDatabase();
const recipe = await Recipe.findOne({_id: id}).lean();
console.log(recipe,'123456789df')
return NextResponse.json({ recipe }, { status: 200 });
} catch (error) {
console.error(error);
}
}
24 changes: 13 additions & 11 deletions app/api/recipe/route.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
const fetchRecipes = async () => {
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 {
const res = await fetch("https://dummyjson.com/recipes", {cache : 'force-cache'});
if (!res.ok) {
throw new Error("Fetch Products Failed");
}
const data = await res.json();
return data;
let db = await connectToDatabase();
console.log('mdcakmdcma')
const recipes = await Recipe.find({}, { _id: 1, title: 1, description: 1, prep: 1, cook: 1, images: 1, ingredients: 1, instructions: 1, nutrition: 1, servings: 1 }).limit(50).lean();
//console.log(recipes[0])
return NextResponse.json({ recipes });
} catch (error) {
console.error("Failed to Fetch Data:", error)
console.error(error);
}
};

export default fetchRecipes;
}
82 changes: 79 additions & 3 deletions app/components/Navbar.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
'use client';

import React, { useState, useEffect } from 'react';
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;
Expand All @@ -25,6 +30,26 @@ const Navbar = ({ position }) => {
};
}, []);

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: '/' },
{
Expand Down Expand Up @@ -53,7 +78,15 @@ const Navbar = ({ position }) => {
<>
<nav
className="fixed top-0 left-0 right-0 z-50 backdrop-blur-md bg-white/30 shadow-lg transition-all duration-300 ease-in-out"
style={{ transform: `translateX(${position}px)` }}
style={{ transform: `translateX(${position + dragPosition}px)` }}
ref={dragRef}
onTouchStart={handleDragStart}
onTouchMove={handleDragMove}
onTouchEnd={handleDragEnd}
onMouseDown={handleDragStart}
onMouseMove={handleDragMove}
onMouseUp={handleDragEnd}
onMouseLeave={handleDragEnd}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
Expand All @@ -62,6 +95,8 @@ const Navbar = ({ position }) => {
RecipeApp
</Link>
</div>

{/* Desktop Navigation */}
<div className="hidden md:block">
<div className="ml-10 flex items-baseline space-x-4">
{navLinks.map((link) => (
Expand All @@ -84,7 +119,24 @@ const Navbar = ({ position }) => {
))}
</div>
</div>
<div className="flex items-center">

{/* Mobile Navigation Button */}
<div className="md:hidden flex items-center">
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="p-2 rounded-md text-gray-800 hover:text-gray-600 focus:outline-none"
aria-label="Toggle menu"
>
<div className="w-6 h-6 flex flex-col justify-between">
<span className={`block w-full h-0.5 bg-current transform transition duration-300 ease-in-out ${isMobileMenuOpen ? 'rotate-45 translate-y-2.5' : ''}`} />
<span className={`block w-full h-0.5 bg-current transition duration-300 ease-in-out ${isMobileMenuOpen ? 'opacity-0' : ''}`} />
<span className={`block w-full h-0.5 bg-current transform transition duration-300 ease-in-out ${isMobileMenuOpen ? '-rotate-45 -translate-y-2.5' : ''}`} />
</div>
</button>
</div>

{/* Search Button */}
<div className="flex items-center ml-4">
<button
onClick={() => setIsSearchOpen(!isSearchOpen)}
className="p-2 rounded-md text-gray-800 hover:text-gray-600 focus:outline-none"
Expand All @@ -96,7 +148,31 @@ const Navbar = ({ position }) => {
</div>
</div>
</div>

{/* Mobile Menu */}
<div className={`md:hidden transition-all duration-300 ease-in-out ${isMobileMenuOpen ? 'max-h-screen' : 'max-h-0'} overflow-hidden`}>
<div className="px-2 pt-2 pb-3 space-y-1">
{navLinks.map((link) => (
<div key={link.name}>
<Link href={link.href} className="block px-3 py-2 rounded-md text-base font-medium text-gray-800 hover:text-gray-600 hover:bg-gray-50">
{link.name}
</Link>
{link.sublinks && (
<div className="pl-4 space-y-1">
{link.sublinks.map((sublink) => (
<Link key={sublink.name} href={sublink.href} className="block px-3 py-2 rounded-md text-sm font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50">
{sublink.name}
</Link>
))}
</div>
)}
</div>
))}
</div>
</div>
</nav>

{/* Search Overlay */}
<div
className={`fixed top-16 left-0 right-0 z-40 backdrop-blur-md bg-white/30 shadow-lg transition-all duration-300 ease-in-out ${
isSubNavVisible ? 'opacity-100 translate-y-0' : 'opacity-0 -translate-y-full'
Expand Down
15 changes: 7 additions & 8 deletions app/components/RecipeCard.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
// app/components/RecipeCard.jsx (rename from RecipeCards.jsx)
import React from 'react';
import Link from 'next/link';

const RecipeCard = ({ recipe }) => {
console.log(recipe)
return (
<Link href={`/recipes/${recipe.id}`}>
<Link href={`/recipes/${recipe._id}`}>
<div className="bg-white shadow-md rounded-lg overflow-hidden">
<img src={recipe.image} alt={recipe.title} className="w-full h-48 object-cover" />
<img src={recipe.images[0]} alt={recipe.title} className="w-full h-48 object-cover" />
<div className="p-4">
<h2 className="text-xl font-bold text-gray-800">{recipe.name}</h2>
<p className="text-gray-600 mt-2">Prep time: {recipe.prepTimeMinutes} mins</p>
<p className="text-gray-600">Cook time: {recipe.cookTimeMinutes} mins</p>
<h2 className="text-xl font-bold text-gray-800">{recipe.title}</h2>
<p className="text-gray-600 mt-2">Prep time: {recipe.prep} mins</p>
<p className="text-gray-600">Cook time: {recipe.cook} mins</p>


<div className="flex items-center justify-between mt-2">
<div className="flex items-center">
<span className="text-yellow-500"></span>
Expand All @@ -28,4 +27,4 @@ const RecipeCard = ({ recipe }) => {
);
};

export default RecipeCard;
export default RecipeCard;
11 changes: 8 additions & 3 deletions app/components/RecipeGrid.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// app/components/RecipeGrid.jsx
"use client"
import React, { useEffect, useState } from 'react';
import RecipeCard from './RecipeCard'; // Update this import
Expand All @@ -8,9 +9,13 @@ const RecipeGrid = () => {

useEffect(() => {
const fetchRecipes = async () => {
console.log('hdbachbdashcb')
try {
const response = await fetch('https://dummyjson.com/recipes');
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);
Expand All @@ -26,10 +31,10 @@ const RecipeGrid = () => {
return (
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6 p-6">
{recipes.map(recipe => (
<RecipeCard key={recipe._id} recipe={recipe} /> // Use _id if recipes are fetched from MongoDB
<RecipeCard key={recipe.title} recipe={recipe} />
))}
</div>
);
};

export default RecipeGrid;
export default RecipeGrid;
59 changes: 31 additions & 28 deletions app/recipes/[id]/page.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
"use client";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import fetchSingleRecipe from "../../api/recipe/[id]/route";
import { useRouter } from "next/navigation";
import RecipeSkeleton from "../../components/RecipeDetailSkeleton";


const formatTime = (minutes) => {
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
Expand All @@ -13,59 +11,66 @@ const formatTime = (minutes) => {

const RecipeDetail = ({ params }) => {
const { id } = params;
const router = useRouter();

const [recipe, setRecipe] = useState(null);
const [activeTab, setActiveTab] = useState("ingredients");
const router = useRouter();

// Log the ID to make sure it's correctly passed
console.log("Recipe ID from params:", id); // Log ID to verify it's correct

useEffect(() => {
const getRecipe = async () => {
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// Delay for 2 seconds
await delay(4000);
const data = await fetchSingleRecipe(id);
if (data) {
setRecipe(data);
try {
if (!id) {
console.error("No ID found in params");
return;
}
const response = await fetch(`/api/recipe/${id}`);
if (!response.ok) {
throw new Error("Failed to fetch recipe");
}
const data = await response.json();
console.log(data.recipe,'data')
setRecipe(data.recipe);
} catch (error) {
console.error("Error fetching recipe:", error);
}
};

getRecipe();
}, [id]);

if (!recipe) {
return <RecipeSkeleton/>
return <RecipeSkeleton />;
}

const totalTime = recipe.prepTimeMinutes + recipe.cookTimeMinutes;

return (
<div className="p-6 max-w-6xl mx-auto font-sans">

<button
onClick={() => router.back()}
onClick={() => router.back()}
className="text-gray-600 hover:text-gray-900 mb-4 flex items-center"
>
← Back
</button>


<div className="grid items-start grid-cols-1 md:grid-cols-2 gap-6">
<div className="w-full lg:sticky top-0 flex gap-3">
<img
src={recipe.image}
alt={recipe.name}
src={recipe.images[0]}
alt={recipe.title}
className="w-3/4 rounded-lg object-cover"
/>
</div>

<div>

<h1 className="text-3xl font-bold mb-2 text-gray-800">
{recipe.name}
</h1>
<h1 className="text-3xl font-bold mb-2 text-gray-800">{recipe.title}</h1>


<p className="text-lg italic text-gray-600 mb-6">
Discover how to make this delicious {recipe.name}. Perfect for {recipe.mealType || "any occasion"}.
Discover how to make this delicious {recipe.title}. Perfect for{" "}
{recipe.mealType || "any occasion"}.
</p>

<div className="text-lg text-gray-800 space-y-2">
Expand All @@ -83,7 +88,6 @@ const RecipeDetail = ({ params }) => {
</p>
</div>


<ul className="grid grid-cols-2 mt-10 border-b-2">
<li
className={`text-gray-800 font-semibold text-base text-center py-3 cursor-pointer ${
Expand All @@ -103,15 +107,14 @@ const RecipeDetail = ({ params }) => {
</li>
</ul>


<div className="mt-6">
{activeTab === "ingredients" ? (
<div>
<h2 className="text-2xl font-semibold mb-4">Ingredients</h2>
<ul className="list-disc pl-6 mt-2 text-gray-700">
{recipe.ingredients.map((ingredient, index) => (
{/* {recipe.ingredients.map((ingredient, index) => (
<li key={index}>{ingredient}</li>
))}
))} */}
</ul>
</div>
) : (
Expand All @@ -131,4 +134,4 @@ const RecipeDetail = ({ params }) => {
);
};

export default RecipeDetail;
export default RecipeDetail;
Loading

0 comments on commit 6ba9c9d

Please sign in to comment.