+
+
+
@@ -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.
+
+
+
-
-
-
-
-
-
-
- View
-
-
-
-
Catan
-
- Settle the island of Catan in this classic resource management game.
-
+
+
+
+
+
+ View
+
+
+
+
Catan
+
+ Settle the island of Catan in this classic resource management game.
+
+
-
-
-
- View
-
-
-
-
Ticket to Ride
-
- Connect cities across a map and complete your railway routes.
-
+
+
+ View
+
+
+
+
Ticket to Ride
+
+ Connect cities across a map and complete your railway routes.
+
+
-
-
-
- View
-
-
-
-
Pandemic
-
- Work together to contain the spread of deadly diseases across the globe.
-
+
+
+ View
+
+
+
+
Pandemic
+
+ Work together to contain the spread of deadly diseases across the globe.
+
+
-
-
-
- View
-
-
-
-
Codenames
-
- Compete to make word associations and guess your teams secret code words.
-
+
+
+ View
+
+
+
+
Codenames
+
+ Compete to make word associations and guess your teams secret code words.
+
+
-
-
-
- View
-
-
-
-
Azul
-
- Collect and place beautiful tiles to decorate the walls of a palace.
-
+
+
+ View
+
+
+
+
Azul
+
+ Collect and place beautiful tiles to decorate the walls of a palace.
+
+
-
-
-
- View
-
-
-
-
Azul
-
- Collect and place beautiful tiles to decorate the walls of a palace.
-
+
+
+ View
+
+
+
+
Azul
+
+ Collect and place beautiful tiles to decorate the walls of a palace.
+
+
-
-
-
- View
-
-
-
-
Azul
-
- Collect and place beautiful tiles to decorate the walls of a palace.
-
+
+
+ View
+
+
+
+
Azul
+
+ Collect and place beautiful tiles to decorate the walls of a palace.
+
+
-
-
-
- View
-
-
-
-
Azul
-
- Collect and place beautiful tiles to decorate the walls of a palace.
-
+
+
+ View
+
+
+
+
Azul
+
+ Collect and place beautiful tiles to decorate the walls of a palace.
+
+
-
-
-
- View
-
-
-
-
Wingspan
-
- Attract and collect birds in this relaxing engine-building game.
-
+
+
+ View
+
+
+
+
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 (
{
-
+
@@ -75,7 +108,7 @@ const Navbar = () => {
))}
{isAuthenticated ? (
@@ -83,7 +116,7 @@ const Navbar = () => {
) : (
@@ -96,9 +129,14 @@ const Navbar = () => {
{/* Mobile Menu Button */}
+ {isMenuOpen ?
+
+
+ :
+ }
@@ -120,7 +158,7 @@ const Navbar = () => {
))}
{isAuthenticated ? (
@@ -128,7 +166,7 @@ const Navbar = () => {
) : (
diff --git a/frontend/src/components/Shared/footer/Content.jsx b/frontend/src/components/Shared/footer/Content.jsx
index dae56000..648116ca 100644
--- a/frontend/src/components/Shared/footer/Content.jsx
+++ b/frontend/src/components/Shared/footer/Content.jsx
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from "react";
-import Logo from "../../../assets/Logo/logo.png";
+import Logo from "../../../assets/Logo/playcafe.png";
import { FaFacebook, FaInstagram, FaTiktok } from "react-icons/fa";
export default function Content() {
@@ -28,7 +28,7 @@ const Section2 = () => {
{!isWide && (
diff --git a/frontend/src/components/ui/FeedbackForm.jsx b/frontend/src/components/ui/FeedbackForm.jsx
index 5f02be21..620724f8 100644
--- a/frontend/src/components/ui/FeedbackForm.jsx
+++ b/frontend/src/components/ui/FeedbackForm.jsx
@@ -13,22 +13,48 @@ const FeedbackForm = () => {
hidden: { opacity: 0, y: 50 },
visible: { opacity: 1, y: 0, transition: { duration: 0.5 } },
};
-
+ const API_URL = import.meta.env.VITE_BACKEND_URI || "http://localhost:3000/";
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [feedback, setFeedback] = useState("");
const [submitted, setSubmitted] = useState(false);
+ const [error, setError] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
- const handleSubmit = (e) => {
+ const handleSubmit = async (e) => {
e.preventDefault();
- console.log(`Name: ${name}, Email: ${email}, Feedback: ${feedback}`);
- setSubmitted(true);
- setTimeout(() => {
- setName("");
- setEmail("");
- setFeedback("");
- setSubmitted(false);
- }, 3000);
+ setIsLoading(true);
+ try {
+ const response = await fetch(`${API_URL}/feedback/create`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({ name, email, feedback }),
+ });
+ const data = await response.json();
+ if (!response.ok) {
+ const errorMessage =
+ data.message || "An error occurred while submitting feedback.";
+ setError(errorMessage);
+ console.error("Feedback submission failed:", errorMessage);
+ return;
+ }
+
+ setSubmitted(true);
+ setError(null);
+ setTimeout(() => {
+ setName("");
+ setEmail("");
+ setFeedback("");
+ setSubmitted(false);
+ }, 3000);
+ } catch (error) {
+ setError("An error occurred while submitting feedback.");
+ console.error("Feedback submission failed:", error);
+ } finally {
+ setIsLoading(false);
+ }
};
return (
@@ -113,7 +139,7 @@ const FeedbackForm = () => {
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-[#004D43] hover:bg-[#003d35] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#004D43]"
>
- Submit Feedback
+ {isLoading ? "Submitting..." : "Submit Feedback"}
@@ -126,6 +152,15 @@ const FeedbackForm = () => {
Thank you for your feedback!
)}
+ {error && (
+
+ {error}
+
+ )}
diff --git a/frontend/src/components/ui/Landing.jsx b/frontend/src/components/ui/Landing.jsx
index b070cf90..aa50f3ef 100644
--- a/frontend/src/components/ui/Landing.jsx
+++ b/frontend/src/components/ui/Landing.jsx
@@ -3,16 +3,17 @@ import coffecup from "../../assets/landing/coffecup.png";
import { useEffect, useRef } from "react";
import SplitType from "split-type";
import gsap from "gsap";
+import { LazyLoadImage, trackWindowScroll } from 'react-lazy-load-image-component';
+import 'react-lazy-load-image-component/src/effects/blur.css';
-
-export default function Landing() {
+function Landing() {
const textRef = useRef(null);
useEffect(() => {
const splitText = new SplitType(textRef.current, { types: "chars, words" })
const tl = gsap.timeline({ repeat: -1, repeatDelay: 1 });
-
+
// Set initial opacity to 0 for all characters
gsap.set(splitText.chars, { opacity: 0 });
@@ -22,13 +23,13 @@ export default function Landing() {
stagger: 0.1,
ease: 'power1.inOut',
})
- .to(splitText.chars, {
- opacity: 0,
- duration: 0.1,
- stagger: 0.1,
- ease: 'power1.inOut',
- delay: 1
- });
+ .to(splitText.chars, {
+ opacity: 0,
+ duration: 0.1,
+ stagger: 0.1,
+ ease: 'power1.inOut',
+ delay: 1
+ });
});
return (
@@ -43,43 +44,30 @@ export default function Landing() {
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
- {/*
-
- Our name says it all!
-
-
- Founder, Jonathan Li, shares a passion for board games, boba, and
- delicious food, so he combined them all to become Sip & Play, Park
- Slope’s first board game cafe. It is a straightforward concept: come
- in with your friends and family to play any board game from our
- library of 300+ games! We hope when you visit, you also enjoy our
- coffee, espresso, boba, sandwiches, and snacks!
-
-
- New opening hours:
- Sunday: 10am-11pm
- Mon-Thurs: 11am-11pm
- Fri: 11am-midnight
- Sat: 10am-midnight
-
-
Learn more↗️
-
*/}
-
+
-
+
@@ -90,3 +78,5 @@ export default function Landing() {
);
}
+
+export default trackWindowScroll(Landing)
\ No newline at end of file
diff --git a/frontend/src/components/ui/ReviewCarousel.jsx b/frontend/src/components/ui/ReviewCarousel.jsx
index 38094e46..c6356e39 100644
--- a/frontend/src/components/ui/ReviewCarousel.jsx
+++ b/frontend/src/components/ui/ReviewCarousel.jsx
@@ -1,5 +1,7 @@
-import { useState,useEffect } from "react";
+import { useState, useEffect } from "react";
import { MdStars, MdArrowBackIos, MdArrowForwardIos } from "react-icons/md";
+import { LazyLoadImage } from 'react-lazy-load-image-component';
+import 'react-lazy-load-image-component/src/effects/blur.css';
const ReviewCarousel = () => {
const reviews = [
@@ -55,7 +57,7 @@ const ReviewCarousel = () => {
const [currentIndex, setCurrentIndex] = useState(0);
const [showMoreStates, setShowMoreStates] = useState(
- reviews.map(() => false)
+ reviews.map(() => false)
);
const toggleShowMore = (index) => {
@@ -75,18 +77,18 @@ const ReviewCarousel = () => {
prevIndex === 0 ? reviews.length - 4 : prevIndex - 1
);
};
- const [cardsToShow, setCardsToShow] = useState(1);
+ const [cardsToShow, setCardsToShow] = useState(1);
const updateCardsToShow = () => {
if (window.innerWidth >= 768) {
- setCardsToShow(4);
+ setCardsToShow(4);
} else {
- setCardsToShow(1);
+ setCardsToShow(1);
}
};
useEffect(() => {
- updateCardsToShow();
+ updateCardsToShow();
window.addEventListener("resize", updateCardsToShow);
return () => {
@@ -118,11 +120,19 @@ const ReviewCarousel = () => {
>
-
+ /> */}
+
{review.name}
{Array(review.rating)