Skip to content

Commit

Permalink
Merge pull request #70 from Cheplusplus/PWA-Testing
Browse files Browse the repository at this point in the history
Pwa testing
  • Loading branch information
kutlwano10 authored Nov 25, 2024
2 parents b54b09f + 7561ea4 commit 1e63a33
Show file tree
Hide file tree
Showing 99 changed files with 11,902 additions and 1,008 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs

name: Node.js CI

env:
MONGODB_URI: ${{ vars.MONGODB_URI }}
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:

runs-on: ubuntu-latest
environment: build
env:
MONGODB_URI: ${{ secrets.MONGODB_URI }}
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
strategy:
matrix:
node-version: [18.x, 20.x, 22.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm i
- run: npm run build
60 changes: 60 additions & 0 deletions app/about.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Image from 'next/image';

const AboutUs = () =>{
return (
<section className="py-24 bg-white">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="text-center max-w-3xl mx-auto">
<h1 className="text-4xl font-bold text-gray-900 mb-4">Meet Our Team</h1>
<p className="text-lg text-gray-600">
We’re the passionate developers behind Recipe Rush, dedicated to making culinary exploration easy, fun, and accessible for everyone. Our team brings together diverse skills and a shared love for cooking to build a platform that inspires people to try new recipes, master classics, and find joy in the kitchen.
</p>
</div>
<div className="flex flex-wrap mt-12 justify-center gap-y-14 max-w-3xl mx-auto lg:max-w-full">
{[
{ name: "Nomusa Mtshali", role: "add role", imgSrc: "https://via.placeholder.com/150", github: "https://github.com/nomusamtshali", linkin: "https://www.linkedin.com/in/nomusa-mtshali/" },
{ name: "Phillip Bogopane", role: "add role", imgSrc: "https://via.placeholder.com/150", github: "https://github.com/Phillip-tech", linkin: "https://linkedin.com/in/username" },
{ name: "Kutlwano Ramotebele", role: "add role", imgSrc: "https://via.placeholder.com/150", github: "https://github.com/kutlwano10", linkin: "https://www.linkedin.com/in/kutlwano-ramotebele-769461296/" },
{ name: "Kealeboga A Ntheledi", role: "add role", imgSrc: "https://via.placeholder.com/150", github: "https://github.com/Kea-Angel-Ntheledi", linkin: "https://linkedin.com/in/username" },
{ name: "Koketso Moilwe", role: "add role", imgSrc: "https://via.placeholder.com/150", github: "https://github.com/KoketsoMoilwe20", linkin: "https://www.linkedin.com/in/koketsomoilwe/" },
{ name: "Karabo M. Radebe", role: "add role", imgSrc: "https://via.placeholder.com/150", github: "https://github.com/Karabo-M-Radebe", linkin: "https://www.linkedin.com/in/karabo-m-radebe/" },
{ name: "Kitso Mogale", role: "add role", imgSrc: "https://via.placeholder.com/150", github: "https://github.com/KitsoMogale", linkin: "https://www.linkedin.com/in/kitso-mogale-200663321/" },
{ name: "Mmakgosana Makgaka", role: "add role", imgSrc: "https://via.placeholder.com/150", github: "https://github.com/Mmakgosana", linkin: "https://www.linkedin.com/in/mmakgosana-makgaka-b32478313/" },
{ name: "Mateo Benzien", role: "add role", imgSrc: "https://via.placeholder.com/150", github: "https://github.com/Mateo-Benzien", linkin: "https://www.linkedin.com/in/mateo-benzien-857864302/" },
{ name: "Sabelo Mawela", role: "add role", imgSrc: "https://via.placeholder.com/150", github: "https://github.com/SABELOMAWELA", linkin: "https://www.linkedin.com/in/sabelo-mawela-480793296/" },
].map((member, index) => (
<div key={index} className="group block text-center lg:w-1/5 sm:w-1/3 min-[450px]:w-1/2 w-full">
<div className="relative mb-5">
<Image
src={member.imgSrc}
alt={`${member.name} image`}
width={112} // 112px for width
height={112} // 112px for height
className="rounded-2xl object-cover transition-all duration-500 border-2 border-solid border-transparent group-hover:border-indigo-600"
/>
</div>
<h4 className="text-xl text-gray-900 font-semibold text-center mb-2 transition-all duration-500 group-hover:text-indigo-600">{member.name}</h4>
<span className="text-gray-500 text-center block transition-all duration-500 group-hover:text-gray-900">{member.role}</span>
<div className="space-x-4 mt-4">
<a href={member.github} target="_blank" rel="noopener noreferrer" className="w-7 h-7 inline-flex items-center justify-center rounded-full bg-gray-100 hover:bg-gray-200">
{/* GitHub Icon */}
<svg xmlns="http://www.w3.org/2000/svg" width="12px" fill="#333" viewBox="0 0 24 24">
<path d="M12 .5C5.371.5 0 5.872 0 12.5c0 5.25 3.438 9.75 8.205 11.388.6.111.793-.261.793-.577v-2.273c-3.338.724-4.043-1.607-4.043-1.607-.546-1.383-1.333-1.752-1.333-1.752-1.086-.742.083-.727.083-.727 1.204.085 1.84 1.237 1.84 1.237 1.067 1.843 2.8 1.309 3.487.999.107-.772.418-1.309.76-1.607-2.665-.3-5.465-1.334-5.465-5.935 0-1.314.47-2.387 1.236-3.224-.124-.303-.536-1.53.116-3.184 0 0 1.007-.322 3.298 1.228a11.364 11.364 0 0 1 3.005-.405c1.018.005 2.043.138 3.005.405 2.29-1.55 3.298-1.228 3.298-1.228.653 1.654.241 2.88.118 3.184.766.837 1.236 1.91 1.236 3.224 0 4.616-2.804 5.633-5.475 5.93.429.37.813 1.099.813 2.221v3.287c0 .318.191.694.797.577C20.563 22.25 24 17.75 24 12.5 24 5.872 18.629.5 12 .5z"/>
</svg>
</a>
<a href={member.linkin} target="_blank" rel="noopener noreferrer" className="w-7 h-7 inline-flex items-center justify-center rounded-full bg-gray-100 hover:bg-gray-200">
{/* LinkedIn Icon */}
<svg xmlns="http://www.w3.org/2000/svg" width="14px" fill="#333" viewBox="0 0 24 24">
<path d="M23.994 24v-.001H24v-8.802c0-4.306-.927-7.623-5.961-7.623-2.42 0-4.044 1.328-4.707 2.587h-.07V7.976H8.489v16.023h4.97v-7.934c0-2.089.396-4.109 2.983-4.109 2.549 0 2.587 2.384 2.587 4.243V24zM.396 7.977h4.976V24H.396zM2.882 0C1.291 0 0 1.291 0 2.882 0 4.47 1.291 5.765 2.882 5.765S5.765 4.47 5.765 2.882C5.765 1.291 4.47 0 2.882 0z"/>
</svg>
</a>
</div>
</div>
))}
</div>
</div>
</section>
);
};

export default AboutUs;
14 changes: 14 additions & 0 deletions app/all/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react'
import RecipeGrid from '../components/RecipeGrid'

const AllRecipes = ({searchParams}) => {
return (
<div className="relative pt-16">
<section className="w-full">
<RecipeGrid searchParams={searchParams} />
</section>
</div>
)
}

export default AllRecipes
39 changes: 39 additions & 0 deletions app/api/10Recipes/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import connectToDatabase from "@/app/lib/connectMongoose";
import Recipe from "@/app/models/Recipe";
import { NextResponse } from "next/server";

export async function GET(req) {
try {
await connectToDatabase();

const recipes = await Recipe.aggregate([
{
$lookup: {
from: "reviews",
localField: "_id",
foreignField: "recipeId",
as: "reviews",
},
},
{
$addFields: {
averageRating: { $avg: "$reviews.rating" },
},
},
{
$sort: { averageRating: -1 },
},
{
$limit: 10,
},
]);

return NextResponse.json({ success: true, recipes });
} catch (error) {
console.error("Error fetching recipes:", error);
return NextResponse.json(
{ success: false, message: "Failed to fetch recipes." },
{ status: 500 }
);
}
}
34 changes: 34 additions & 0 deletions app/api/addReview/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import connectToDatabase from "@/app/lib/connectMongoose";
import Review from "@/app/models/reviews";
import { NextResponse } from "next/server";

export async function POST(req) {
await connectToDatabase();

try {
const { recipeId, comment, rating, reviewerName } = await req.json();

// Validate request data
if ( !comment || !rating ) {
return NextResponse.json(
{ error: "All fields are required" },
{ status: 400 }
);
}

// Create a new review document
const newReview = new Review({ recipeId, comment, rating, reviewerName });
const savedReview = await newReview.save();

// Return success response
return NextResponse.json(savedReview, { status: 201 });
} catch (error) {
console.error("Error saving review:", error);

// Return error response
return NextResponse.json(
{ error: "Failed to add review" },
{ status: 500 }
);
}
}
58 changes: 58 additions & 0 deletions app/api/auth/[...nextauth]/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import CredentialsProvider from "next-auth/providers/credentials";
import { MongoDBAdapter } from "@auth/mongodb-adapter";
import { clientPromise } from "@/app/lib/connectMongoose";
import User from "@/app/models/user";
import bcrypt from "bcrypt";

const authOptions = {
adapter: MongoDBAdapter(clientPromise, {
databaseName: "devdb", // Explicitly specify your database
collections: {
users: "users",
accounts: "accounts",
sessions: "sessions",
},
}),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
CredentialsProvider({
name: "Credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
const { email, password } = credentials;
const user = await User.findOne({ email });
if (!user) throw new Error("No user found with this email");
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) throw new Error("Invalid password");
return { id: user._id.toString(), email: user.email, name: user.name };
},
}),
],
secret: process.env.NEXTAUTH_SECRET,
session: { strategy: "jwt" },
callbacks: {
async session({ session, token }) {
session.user.id = token.sub;
session.user.email = token.email;
session.user.provider = token.provider;
return session;
},
async jwt({ token, account }) {
if (account) {
token.provider = account.provider;
}
return token;
},
},
};

export const GET = NextAuth(authOptions);
export const POST = NextAuth(authOptions);
39 changes: 39 additions & 0 deletions app/api/auth/register/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// pages/api/auth/register.js
import bcrypt from "bcrypt";
import User from "@/app/models/user"; // Assuming your User model is here
import connectToDatabase from "@/app/lib/connectMongoose";
import { NextResponse } from "next/server";

export async function POST(req) {
await connectToDatabase();

// Log to confirm request received
console.log("signup123");

// Parse JSON from the request body
const { email, password, name } = await req.json();
console.log(email, password, name);

// Check if the email is already in use
const existingUser = await User.findOne({ email });
if (existingUser) {
// console.log(existingUser)
return NextResponse.json({ error: "Email already in use" }, { status: 400 });
}
// Hash the password
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds);
//console.log(User.schema.paths);

// Create and save the new user
const newUser = new User({
email,
password: hashedPassword,
name,
});

await newUser.validate(); // Check validation errors
await newUser.save();
// Return success response
return NextResponse.json({ message: "User registered successfully" }, { status: 201 });
}
36 changes: 36 additions & 0 deletions app/api/categories/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import connectToDatabase from '@/app/lib/connectMongoose';
// pages/api/categories.js
import { NextResponse } from 'next/server';
import Categories from "@/app/models/categories";

export async function GET() {
// Connect to MongoDB
await connectToDatabase();
try {
// Fetch the categories document
const categoryDoc = await Categories.findOne({});
if (!categoryDoc) {
return NextResponse.json({ message: "Categories not found" }, { status: 404 },{
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
});
}
// Send back the categories array
return NextResponse.json({ categories: categoryDoc.categories }, {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
});
} catch (error) {
console.error("Error fetching categories:", error);
return NextResponse.json({ message: "Internal server error" }, { status: 500 },{
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
});
}
}
27 changes: 27 additions & 0 deletions app/api/deleteReview/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import connectToDatabase from "@/app/lib/connectMongoose";
import Review from "@/app/models/reviews";
import { NextResponse } from "next/server";

export async function DELETE(req) {
await connectToDatabase();

try {
const { id } = await req.json();

// Validate
if (!id) {
return NextResponse.json(
{ error: "Review ID is required" },
{ status: 400 }
);
}

// Delete the review
await Review.findByIdAndDelete(id);

return NextResponse.json({ message: "Review deleted successfully" }, { status: 200 });
} catch (error) {
console.error("Error deleting review:", error);
return NextResponse.json({ error: "Failed to delete review" }, { status: 500 });
}
}
31 changes: 31 additions & 0 deletions app/api/editReview/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import connectToDatabase from "@/app/lib/connectMongoose";
import Review from "@/app/models/reviews";
import { NextResponse } from "next/server";

export async function PATCH(req) {
await connectToDatabase();

try {
const { id, comment, rating } = await req.json();

// Validate
if (!id || !comment || !rating) {
return NextResponse.json(
{ error: "All fields are required" },
{ status: 400 }
);
}

// Update the review
const updatedReview = await Review.findByIdAndUpdate(
id,
{ comment, rating },
{ new: true }
);

return NextResponse.json(updatedReview, { status: 200 });
} catch (error) {
console.error("Error editing review:", error);
return NextResponse.json({ error: "Failed to edit review" }, { status: 500 });
}
}
Loading

0 comments on commit 1e63a33

Please sign in to comment.