diff --git a/backend/models/feedback.js b/backend/models/feedback.js
new file mode 100644
index 0000000..ae8b18b
--- /dev/null
+++ b/backend/models/feedback.js
@@ -0,0 +1,13 @@
+import mongoose from "mongoose";
+
+const feedbackSchema = new mongoose.Schema({
+ userId: { type: String, default: null }, // null if anonymous
+ rating: { type: Number, required: true, min: 1, max: 5 },
+ comment: { type: String, required: true },
+ category: { type: String, default: "General" },
+ date: { type: Date, default: Date.now },
+});
+
+const Feedback = mongoose.model("Feedback", feedbackSchema);
+
+export default Feedback;
diff --git a/backend/routes/feedback.route.js b/backend/routes/feedback.route.js
new file mode 100644
index 0000000..4014d6d
--- /dev/null
+++ b/backend/routes/feedback.route.js
@@ -0,0 +1,30 @@
+import express from "express";
+import Feedback from "../models/feedback.js";
+
+const router = express.Router();
+
+// POST /api/feedback
+router.post("/", async (req, res) => {
+ try {
+ const { userId, rating, comment, category } = req.body;
+
+ if (!rating || !comment) {
+ return res.status(400).json({ error: "Rating and comment required" });
+ }
+
+ const feedback = await Feedback.create({
+ userId,
+ rating,
+ comment,
+ category,
+ date: new Date()
+ });
+
+ res.status(201).json({ message: "Feedback saved", feedback });
+ } catch (err) {
+ console.error("Feedback error:", err);
+ res.status(500).json({ error: "Server error" });
+ }
+});
+
+export default router;
diff --git a/backend/server.js b/backend/server.js
index d4938dc..0c44e3b 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -61,6 +61,13 @@ app.get("/", (req, res) => {
});
const PORT = process.env.PORT || 5000;
-app.listen(PORT, () => {
- console.log(`Server is up and running at http://localhost:${PORT} 🚀`);
-});
\ No newline at end of file
+dbconnection()
+ .then(() => {
+ app.listen(PORT, () => {
+ console.log(`Server is up and running at http://localhost:${PORT} 🚀`);
+ });
+ })
+ .catch((err) => {
+ console.error("❌ Failed to connect to MongoDB:", err.message);
+ process.exit(1); // stop server if DB fails
+ });
\ No newline at end of file
diff --git a/frontend/src/Components/Navbar/Navbar.jsx b/frontend/src/Components/Navbar/Navbar.jsx
index fb6221b..78c3f32 100644
--- a/frontend/src/Components/Navbar/Navbar.jsx
+++ b/frontend/src/Components/Navbar/Navbar.jsx
@@ -1,8 +1,8 @@
import React, { useEffect, useState } from "react";
-import { Github, Home, Info, Sparkle, LogIn, UserPlus, UserCircle } from "lucide-react";
+import { Github, Home, Info, Sparkle, LogIn, UserPlus, UserCircle, Phone } from "lucide-react";
import { FloatingNav } from "../ui/floating-navbar";
-import { Phone } from "lucide-react";
import { Link } from "react-router-dom";
+import FeedbackModal from "../ui/FeedbackModal";
const navItems = [
{
@@ -44,21 +44,20 @@ const Navbar = () => {
return () => window.removeEventListener("scroll", handleScroll);
}, []);
- const isAuthenticated = localStorage.getItem('token') !== null;
+ const isAuthenticated = localStorage.getItem("token") !== null;
+ const userId = localStorage.getItem("userId");
return (
{!showFloating && (
- {/* Logo */}
DevSync
- {/* Desktop Navigation */}
- {/* Mobile Menu Button */}
- {/* Mobile Navigation */}
{menuOpen && (
{navItems.map((item) => (
@@ -158,6 +155,8 @@ const Navbar = () => {
)}
{showFloating && }
+
+
);
};
diff --git a/frontend/src/Components/ui/FeedbackModal.jsx b/frontend/src/Components/ui/FeedbackModal.jsx
new file mode 100644
index 0000000..6fa03e4
--- /dev/null
+++ b/frontend/src/Components/ui/FeedbackModal.jsx
@@ -0,0 +1,204 @@
+import React, { useEffect, useState } from "react";
+import { motion } from "framer-motion"; // npm i framer-motion
+
+const STORAGE_KEY = "devsync-feedback-lastshown";
+
+export default function FeedbackModal({ userId = null, justLoggedIn = false, daysInterval = 5 }) {
+ const [open, setOpen] = useState(false);
+ const [rating, setRating] = useState(0);
+ const [comment, setComment] = useState("");
+ const [category, setCategory] = useState("");
+ const [allowAnonymous, setAllowAnonymous] = useState(false);
+ const [submitting, setSubmitting] = useState(false);
+ const [error, setError] = useState(null);
+ const [success, setSuccess] = useState(null);
+
+ useEffect(() => {
+ try {
+ const raw = localStorage.getItem(STORAGE_KEY);
+ const last = raw ? new Date(raw) : null;
+ const now = new Date();
+ const msInterval = daysInterval * 24 * 60 * 60 * 1000;
+
+ if (justLoggedIn) {
+ if (!last || now.getTime() - last.getTime() > 60 * 1000) {
+ setTimeout(() => setOpen(true), 800);
+ }
+ return;
+ }
+
+ if (!last || now.getTime() - last.getTime() >= msInterval) {
+ const t = setTimeout(() => setOpen(true), 2500);
+ return () => clearTimeout(t);
+ }
+ } catch (e) {
+ console.warn("FeedbackModal: localStorage issue", e);
+ }
+ }, [justLoggedIn, daysInterval]);
+
+ const handleClose = () => {
+ setOpen(false);
+ try {
+ localStorage.setItem(STORAGE_KEY, new Date().toISOString());
+ } catch {}
+ };
+
+ const validate = () => {
+ if (rating < 1 || rating > 5) {
+ setError("Please give a rating between 1 and 5 stars.");
+ return false;
+ }
+ if (!comment || comment.trim().length < 5) {
+ setError("Comment should be at least 5 characters.");
+ return false;
+ }
+ setError(null);
+ return true;
+ };
+
+ const submit = async () => {
+ if (!validate()) return;
+ setSubmitting(true);
+ setError(null);
+ setSuccess(null);
+
+ const payload = {
+ userId: allowAnonymous ? null : userId,
+ rating,
+ comment: comment.trim(),
+ category: category || null,
+ date: new Date().toISOString(),
+ };
+
+ try {
+ const res = await fetch("/api/feedback/submit", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(payload),
+ });
+
+ if (!res.ok) throw new Error("Failed to submit");
+
+ setSuccess("Thanks! Your feedback was recorded.");
+ setTimeout(() => handleClose(), 900);
+ } catch (err) {
+ setError(err.message || "Error submitting feedback");
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
+ const Star = ({ filled, onClick, index }) => (
+
+ );
+
+ if (!open) return null;
+
+ return (
+
+
+
+
+
Share your feedback
+
+
+
+
+
+
+
+ {[0, 1, 2, 3, 4].map((i) => (
+ setRating(i + 1)} />
+ ))}
+ {rating ? `${rating}/5` : "No rating"}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setAllowAnonymous(e.target.checked)}
+ />
+
+
+
+ {error &&
{error}
}
+ {success &&
{success}
}
+
+
+
+
+
+
+
+
+ );
+}