diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml new file mode 100644 index 00000000..e313cf88 --- /dev/null +++ b/.github/workflows/contributors.yml @@ -0,0 +1,18 @@ +on: + push: + branches: + - main + - "*" + +jobs: + contrib-readme-job: + runs-on: ubuntu-latest + name: A job to automate contrib in readme + permissions: + contents: write + pull-requests: write + steps: + - name: Contribute List + uses: akhilmhdh/contributors-readme-action@v2.3.10 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 09f3d462..00000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -frontend/.env diff --git a/README.md b/README.md index b05b7187..74557f70 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,118 @@ Special thanks to our amazing mentors who are guiding this project! 🙌 - Make sure you show some love by giving ⭐ to our repository
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + RamakrushnaBiswal +
+ Ramakrushna Biswal +
+
+ + samar12-rad +
+ Samarth Vaidya +
+
+ + 17arindam +
+ Arindam +
+
+ + tejasbenibagde +
+ Tejas Benibagde +
+
+ + Suhas-Koheda +
+ Suhas Koheda +
+
+ + sajalbatra +
+ Sajal Batra +
+
+ + itznayan +
+ Mahera Nayan +
+
+ + Nvntdad +
+ Navneet Dadhich +
+
+ + mishradev1 +
+ Dev Mishra +
+
+ + MutiatBash +
+ Bashua Mutiat +
+
+ + Sapna127 +
+ Sapna Kul +
+
+ + Syed-Farazuddin +
+ Syed Faraz +
+
+ + Vaibhav-Kumar-K-R +
+ Vaibhav-Kumar-K-R +
+
+ + vishnuprasad2004 +
+ Vishnu Prasad Korada +
+
+ @@ -92,6 +204,7 @@ Special thanks to our amazing mentors who are guiding this project! 🙌 + diff --git a/backend/config/api.info.js b/backend/config/api.info.js new file mode 100644 index 00000000..8a827573 --- /dev/null +++ b/backend/config/api.info.js @@ -0,0 +1,16 @@ +module.exports = { + message: "Welcome to the feedback API!", + version: "1.0.0", + endpoints: { + createFeedback: { + path: "/create", + method: "POST", + parameters: { + name: { type: "string", required: true }, + email: { type: "string", required: true }, + message: { type: "string", required: true }, + }, + }, + }, + documentation: "https://api-docs-url.com", +}; diff --git a/backend/config/logger.js b/backend/config/logger.js new file mode 100644 index 00000000..da6a00ba --- /dev/null +++ b/backend/config/logger.js @@ -0,0 +1,21 @@ +// config/logger.js +const winston = require("winston"); + +const logger = winston.createLogger({ + level: "info", // Set the default logging level + format: winston.format.combine( + winston.format.timestamp(), // Include a timestamp + winston.format.colorize(), // Colorize the output + winston.format.printf(({ timestamp, level, message, stack, errors }) => { + return `${timestamp} [${level}] ${message}${ + stack ? `\nStack: ${stack}` : "" + }${errors ? `\nErrors: ${JSON.stringify(errors)}` : ""}`; // Custom format + }) + ), + transports: [ + new winston.transports.Console(), // Log to console + new winston.transports.File({ filename: "error.log", level: "error" }), // Log errors to a file + ], +}); + +module.exports = logger; diff --git a/backend/controller/feedback.controller.js b/backend/controller/feedback.controller.js new file mode 100644 index 00000000..5cdb36ec --- /dev/null +++ b/backend/controller/feedback.controller.js @@ -0,0 +1,53 @@ +const { z } = require("zod"); +const { Feedback } = require("../models/feedback.model"); +const logger = require("../config/logger"); // Import your logger + +// Define the Zod schema for feedback validation +const feedbackSchema = z.object({ + name: z.string().min(2).max(100), + email: z.string().email(), + feedback: z.string().min(10), +}); + +async function createFeedback(req, res) { + try { + const validationResult = feedbackSchema.safeParse(req.body); + + if (!validationResult.success) { + logger.error("Validation error:", { + errors: validationResult.error.errors, // Log the detailed validation errors + body: req.body, // Optionally log the request body for context + }); // Use logger for validation errors + return res.status(400).json({ + success: false, + message: "Validation failed", + errors: validationResult.error.errors, + }); + } + + const feedback = await Feedback.create(validationResult.data); + + res.status(201).json({ + success: true, + message: "Feedback created successfully", + data: feedback, + }); + } catch (error) { + logger.error("Error creating feedback:", error); // Log the error using Winston + res.status(500).json({ + success: false, + message: "An error occurred while creating the feedback", + }); + } +} + +module.exports = { + createFeedback, +}; + +// Dummy API call for feedback +// { +// "name": "John Doe", +// "email": "john@1212.com", +// "feedback": "This is a dummy feedback" +// } diff --git a/backend/controller/reservation.controller.js b/backend/controller/reservation.controller.js index f5631326..4ee00658 100644 --- a/backend/controller/reservation.controller.js +++ b/backend/controller/reservation.controller.js @@ -1,5 +1,6 @@ const { z } = require("zod"); const Reservation = require("../models/reservation.model"); +const logger = require("../config/logger"); // Import your logger // Define the Zod schema for reservation validation const reservationSchema = z.object({ @@ -13,7 +14,10 @@ async function createReservation(req, res) { const validationResult = reservationSchema.safeParse(req.body); if (!validationResult.success) { - console.error("Validation error:", validationResult.error.errors); + logger.error("Validation error:", { + errors: validationResult.error.errors, + body: req.body, + }); return res.status(400).json({ success: false, message: "Validation failed", @@ -29,7 +33,12 @@ async function createReservation(req, res) { data: reservation, }); } catch (error) { - console.error("Error creating reservation:", error); + logger.error("Error creating reservation:", { + message: error.message, + stack: error.stack, + body: req.body, + }); + res.status(500).json({ success: false, message: "An error occurred while creating the reservation", diff --git a/backend/index.js b/backend/index.js index 30c9ee2c..89240258 100644 --- a/backend/index.js +++ b/backend/index.js @@ -1,10 +1,15 @@ const express = require("express"); const cors = require("cors"); -const app = express(); -const port = 3000; -require("dotenv").config(); const mongoose = require("mongoose"); +const dotenv = require("dotenv"); +const logger = require("./config/logger"); // Import your logger +const errorMiddleware = require("./middlewares/errrorMiddleware"); + +dotenv.config(); +const app = express(); +const port = process.env.PORT || 3000; +// CORS configuration app.use( cors({ origin: ["http://localhost:5173", "https://play-cafe.vercel.app"], @@ -13,22 +18,31 @@ app.use( app.use(express.json()); +// MongoDB connection mongoose .connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true, }) .then(() => { - console.log("Connected to MongoDB"); + logger.info("Connected to MongoDB"); // Log successful connection }) .catch((error) => { - console.error("Database connection failed:", error.message); - console.error(error.stack); + logger.error("Database connection failed:", error.message); // Use logger for connection error process.exit(1); }); +// API routes app.use("/api", require("./routes/index")); -app.listen(port, () => console.log(`Server is running on port ${port}!`)); +// Health Check Endpoint +app.get("/health", (req, res) => { + res.status(200).json({ status: "OK" }); +}); + +app.use(errorMiddleware); + +// Start server +app.listen(port, () => logger.info(`Server is running on port ${port}!`)); // Log server start module.exports = app; diff --git a/backend/middlewares/errrorMiddleware.js b/backend/middlewares/errrorMiddleware.js new file mode 100644 index 00000000..4c3afd5a --- /dev/null +++ b/backend/middlewares/errrorMiddleware.js @@ -0,0 +1,20 @@ +// middlewares/errorMiddleware.js +const logger = require("../config/logger"); // Assuming you have a logger set up + +function errorMiddleware(err, req, res, next) { + // Log the error details using logger + logger.error(err.message, { + stack: req.body, // Include stack trace + url: req.originalUrl, // Request URL + method: req.method, // HTTP method + body: req.body, // Request body + }); + + // Send the response + res.status(err.status || 500).json({ + success: false, + message: err.message || "An unexpected error occurred.", + }); +} + +module.exports = errorMiddleware; diff --git a/backend/models/feedback.model.js b/backend/models/feedback.model.js new file mode 100644 index 00000000..c3b1a6bf --- /dev/null +++ b/backend/models/feedback.model.js @@ -0,0 +1,51 @@ +const mongoose = require("mongoose"); +const Schema = mongoose.Schema; +const validator = require("validator"); + +const feedbackSchema = new Schema( + { + name: { + type: String, + required: true, + trim: true, + maxlength: [100, "Name cannot be more than 100 characters"], + }, + email: { + type: String, + required: true, + trim: true, + lowercase: true, + maxlength: [255, "Email cannot be more than 255 characters"], + match: [ + /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/, + "Please fill a valid email address", + ], + }, + feedback: { + type: String, + required: true, + trim: true, + maxlength: [1000, "Feedback cannot be more than 1000 characters"], + }, + createdAt: { + type: Date, + default: Date.now, + }, + }, + { + timestamps: true, + } +); + +feedbackSchema.pre("save", function (next) { + const feedback = this; + feedback.name = validator.escape(feedback.name); + feedback.feedback = validator.escape(feedback.feedback); + next(); +}); + +const Feedback = mongoose.model("Feedback", feedbackSchema); + +module.exports = { + Feedback, +}; diff --git a/backend/package.json b/backend/package.json index 883ceacc..2275182a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -16,6 +16,8 @@ "dotenv": "^16.4.5", "express": "^4.21.0", "mongoose": "^8.7.0", + "validator": "^13.12.0", + "winston": "^3.14.2", "zod": "^3.23.8" }, "devDependencies": { diff --git a/backend/routes/feedbackRouter.js b/backend/routes/feedbackRouter.js new file mode 100644 index 00000000..e02c3bab --- /dev/null +++ b/backend/routes/feedbackRouter.js @@ -0,0 +1,21 @@ +const express = require("express"); +const { createFeedback } = require("../controller/feedback.controller"); +const router = express.Router(); +const apiInfo = require("../config/api.info"); +const logger = require("../config/logger"); // Import your logger + +router.post("/create", createFeedback); + +router.get("/", (req, res) => { + try { + res.json(apiInfo); + } catch (error) { + logger.error("Error fetching API info:", { + message: error.message, + stack: error.stack, + }); + res.status(500).json({ error: "Internal server error" }); + } +}); + +module.exports = router; diff --git a/backend/routes/index.js b/backend/routes/index.js index 8503b4c3..bbe44865 100644 --- a/backend/routes/index.js +++ b/backend/routes/index.js @@ -1,15 +1,31 @@ const express = require("express"); const Reservation = require("../models/reservation.model"); +const logger = require("../config/logger"); // Import your Winston logger const router = express.Router(); +let feedbackRouter; +try { + feedbackRouter = require("./feedbackRouter"); +} catch (error) { + logger.error("Error loading feedbackRouter:", error); // Log the error with Winston + feedbackRouter = (req, res) => { + res + .status(500) + .json({ error: "Feedback functionality is currently unavailable" }); + }; +} + +router.use("/feedback", feedbackRouter); router.use("/reservation", require("./reservationRouter")); + router.get("/", (req, res) => { res.json({ message: "Welcome to the restaurant API!", version: "1.0.0", endpoints: { Reservation: "/reservation", + Feedback: "/feedback", // Added feedback endpoint documentation }, documentation: "https://api-docs-url.com", }); diff --git a/frontend/.gitignore b/frontend/.gitignore index 1035c9af..3146fc91 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -22,3 +22,10 @@ dist-ssr *.njsproj *.sln *.sw? + +# Frontend build +.env.local +.env.development.local +.env.test.local +.env.production.local +.env diff --git a/frontend/package.json b/frontend/package.json index 02011c37..01c1e1b7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,7 @@ "@kinde-oss/kinde-auth-react": "^4.0.4", "@splidejs/splide": "^4.1.4", "@splidejs/splide-extension-auto-scroll": "^0.5.3", + "antd": "^5.21.2", "autoprefixer": "^10.4.19", "axios": "^1.7.7", "clsx": "^2.1.1", @@ -23,6 +24,7 @@ "react-dom": "^18.3.1", "react-icons": "^5.2.1", "react-intersection-observer": "^9.13.0", + "react-lazy-load-image-component": "^1.6.2", "react-pageflip": "^2.0.3", "react-router-dom": "^6.24.1", "split-type": "^0.3.4", diff --git a/frontend/src/assets/Logo/playcafe.png b/frontend/src/assets/Logo/playcafe.png new file mode 100644 index 00000000..718ba34b Binary files /dev/null and b/frontend/src/assets/Logo/playcafe.png differ diff --git a/frontend/src/components/Pages/Boardgame.jsx b/frontend/src/components/Pages/Boardgame.jsx index ede290f0..e31ee1bc 100644 --- a/frontend/src/components/Pages/Boardgame.jsx +++ b/frontend/src/components/Pages/Boardgame.jsx @@ -9,232 +9,235 @@ import board8 from "../../assets/Boardgames/board8.png"; import board10 from "../../assets/Boardgames/board10.png"; import bg from "../../assets/Boardgames/bg.jpg"; +import { LazyLoadImage } from 'react-lazy-load-image-component'; +import 'react-lazy-load-image-component/src/effects/blur.css'; export default function Boardgame() { return ( <> -
-
-
-
-
-

- Discover Our Boardgame Collection -

-

- Explore our diverse selection of captivating boardgames, perfect for game nights, family gatherings, and strategic adventures. -

+
+
+
+
+
+

+ Discover Our Boardgame Collection +

+

+ Explore our diverse selection of captivating boardgames, perfect for game nights, family gatherings, and strategic adventures. +

+
+
- Boardgame Hero -
-
-
-
-
- - View - - Catan -
-

Catan

-

- Settle the island of Catan in this classic resource management game. -

+
+
+
+
+ + View + + Catan +
+

Catan

+

+ Settle the island of Catan in this classic resource management game. +

+
-
-
- - View - - Ticket to Ride -
-

Ticket to Ride

-

- Connect cities across a map and complete your railway routes. -

+
+ + View + + Ticket to Ride +
+

Ticket to Ride

+

+ Connect cities across a map and complete your railway routes. +

+
-
-
- - View - - Pandemic -
-

Pandemic

-

- Work together to contain the spread of deadly diseases across the globe. -

+
+ + View + + Pandemic +
+

Pandemic

+

+ Work together to contain the spread of deadly diseases across the globe. +

+
-
-
- - View - - Codenames -
-

Codenames

-

- Compete to make word associations and guess your teams secret code words. -

+
+ + View + + Codenames +
+

Codenames

+

+ Compete to make word associations and guess your teams secret code words. +

+
-
-
- - View - - Azul -
-

Azul

-

- Collect and place beautiful tiles to decorate the walls of a palace. -

+
+ + View + + Azul +
+

Azul

+

+ Collect and place beautiful tiles to decorate the walls of a palace. +

+
-
-
- - View - - Azul -
-

Azul

-

- Collect and place beautiful tiles to decorate the walls of a palace. -

+
+ + View + + Azul +
+

Azul

+

+ Collect and place beautiful tiles to decorate the walls of a palace. +

+
-
-
- - View - - Azul -
-

Azul

-

- Collect and place beautiful tiles to decorate the walls of a palace. -

+
+ + View + + Azul +
+

Azul

+

+ Collect and place beautiful tiles to decorate the walls of a palace. +

+
-
-
- - View - - Azul -
-

Azul

-

- Collect and place beautiful tiles to decorate the walls of a palace. -

+
+ + View + + Azul +
+

Azul

+

+ Collect and place beautiful tiles to decorate the walls of a palace. +

+
-
-
- - View - - Wingspan -
-

Wingspan

-

- Attract and collect birds in this relaxing engine-building game. -

+
+ + View + + Wingspan +
+

Wingspan

+

+ Attract and collect birds in this relaxing engine-building game. +

+
-
-
-
-
-

- Subscribe to our Newsletter -

-

- Stay updated with our latest boardgame collections, special offers, and events. Subscribe now and never miss out! -

-
- - -
-
-
-
+ +
+
+

+ Subscribe to our Newsletter +

+

+ Stay updated with our latest boardgame collections, special offers, and events. Subscribe now and never miss out! +

+
+ + +
+
+
+
); } diff --git a/frontend/src/components/Shared/Navbar.jsx b/frontend/src/components/Shared/Navbar.jsx index 9a283ba7..13785685 100644 --- a/frontend/src/components/Shared/Navbar.jsx +++ b/frontend/src/components/Shared/Navbar.jsx @@ -1,14 +1,15 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import Logo from "../../assets/Logo/logo.png"; import { Link, useLocation } from "react-router-dom"; import { useKindeAuth } from "@kinde-oss/kinde-auth-react"; +import { message } from "antd"; const Navbar = () => { const { login, logout, isAuthenticated } = useKindeAuth(); const [isScrolled, setIsScrolled] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false); const location = useLocation(); - + const wasAuthenticated = useRef(false); const menuItems = [ { name: "Home", path: "/" }, { name: "Events", path: "/events" }, @@ -30,6 +31,17 @@ const Navbar = () => { }; }, []); + useEffect(() => { + // Check when user goes from not authenticated to authenticated + if (wasAuthenticated.current && !isAuthenticated) { + message.success("Logout successful!"); + } + if (!wasAuthenticated.current && isAuthenticated) { + message.success("Login successful!"); + } + wasAuthenticated.current = isAuthenticated; + }, [isAuthenticated]); + const toggleMenu = () => { setIsMenuOpen(!isMenuOpen); }; @@ -47,6 +59,27 @@ const Navbar = () => { const hoverTextColorClass = isScrolled ? "hover:text-gray-900" : "hover:text-gray-800"; const baseTextColorClass = isScrolled ? "text-gray-800" : "text-gray-900"; const mobileMenuBaseTextColorClass = isScrolled ? "text-gray-900" : "text-gray-800"; + + // Handle login + const handleLogin = async () => { + try { + await login(); + + } catch (error) { + message.error("Login failed. Please try again."); + } + }; + + + const handleLogout = async () => { + try { + await logout(); + + } catch (error) { + message.error("Logout failed. Please try again."); + } + }; + return (