From e13624f7dbb852f4dcc48f6244513b7830f8d1d4 Mon Sep 17 00:00:00 2001
From: "kruthik.soundapppan@gmail.com" <kruthik.soundapppan@gmail.com>
Date: Mon, 21 Apr 2025 17:50:43 +0100
Subject: [PATCH] Added likes tracking feature in MyPosts

---
 Startup-app/backend/models/idea.js            |  19 +-
 Startup-app/backend/routes/ideaRoutes.js      |  61 +++-
 Startup-app/backend/routes/likeRoutes.js      |  31 +-
 Startup-app/frontend/package-lock.json        | 331 ++++++++++++++++++
 Startup-app/frontend/package.json             |   1 +
 .../frontend/src/components/IdeaOfTheDay.jsx  |   7 +-
 .../frontend/src/components/LikesChart.jsx    |  85 +++++
 Startup-app/frontend/src/pages/MyPosts.js     | 131 ++++---
 .../frontend/src/styles/LikesChart.css        |  35 ++
 9 files changed, 618 insertions(+), 83 deletions(-)
 create mode 100644 Startup-app/frontend/src/components/LikesChart.jsx
 create mode 100644 Startup-app/frontend/src/styles/LikesChart.css

diff --git a/Startup-app/backend/models/idea.js b/Startup-app/backend/models/idea.js
index 83da5ac2..42e937ef 100644
--- a/Startup-app/backend/models/idea.js
+++ b/Startup-app/backend/models/idea.js
@@ -1,7 +1,7 @@
 const mongoose = require('mongoose');
 
 const IdeaSchema = new mongoose.Schema({
-    email:{type:String, required: true},
+    email: { type: String, required: true },
     userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
     name: { type: String, required: true },
     title: { type: String, required: true },
@@ -10,12 +10,10 @@ const IdeaSchema = new mongoose.Schema({
     budget: { type: Number, required: true },
     projectStage: { type: String, required: true },
     location: { type: String, required: true },
-    file: { name: String, type: String, data: String, size:Number, required: true },
+    file: { name: String, type: String, data: String, size: Number, required: true },
     fileId: mongoose.Schema.Types.ObjectId,
 
-    
-
-    // 👍 New fields for like feature
+    // 👍 Like feature
     likes: {
         type: Number,
         default: 0,
@@ -25,13 +23,20 @@ const IdeaSchema = new mongoose.Schema({
         ref: 'User',
         default: [],
     }],
-    // List of collaborators
+
+    // ✅ Like timestamps for chart
+    likeTimestamps: {
+        type: Map,
+        of: [Date],
+        default: {}
+    },
+
+    // Collaborators
     collaborators: [{
         type: mongoose.Schema.Types.ObjectId,
         ref: 'User',
         default: [],
     }],
-
 }, { timestamps: true });
 
 const Idea = mongoose.models.Idea || mongoose.model('Idea', IdeaSchema);
diff --git a/Startup-app/backend/routes/ideaRoutes.js b/Startup-app/backend/routes/ideaRoutes.js
index a8e44b3e..1a722c98 100644
--- a/Startup-app/backend/routes/ideaRoutes.js
+++ b/Startup-app/backend/routes/ideaRoutes.js
@@ -38,19 +38,16 @@ router.get('/count', async (req, res) => {
 
 /**
  * ✅ Get posts created by a specific user
- * URL: /api/ideas/posts-by-user/:userId
  */
 router.get('/posts-by-user/:userId', getUserPosts);
 
 /**
  * ✅ Get posts liked by a user
- * URL: /api/ideas/liked-by-user/:userId
  */
 router.get('/liked-by-user/:userId', getLikedPosts);
 
 /**
  * ✅ Get posts created by the currently logged-in user
- * URL: /api/ideas/my-posts
  */
 router.get('/my-posts', auth, async (req, res) => {
   try {
@@ -66,18 +63,16 @@ router.get('/my-posts', auth, async (req, res) => {
 
 /**
  * ✅ Submit an idea with file upload
- * URL: /api/ideas/submit
  */
 router.post('/submit', auth, upload.single('file'), submitIdea);
 
 /**
- * ✅ Get all ideas (optional usage)
+ * ✅ Get all ideas
  */
 router.get('/all', auth, getIdeas);
 
 /**
- * ✅ Get all ideas for the feed with follow status
- * URL: /api/feed
+ * ✅ Get all ideas for the feed
  */
 router.get('/feed', auth, async (req, res) => {
   try {
@@ -103,7 +98,6 @@ router.get('/feed', auth, async (req, res) => {
 
 /**
  * ✅ Get the idea with the highest likes (Idea of the Day)
- * URL: /api/ideas/idea-of-the-day
  */
 router.get('/idea-of-the-day', async (req, res) => {
   try {
@@ -122,6 +116,56 @@ router.get('/idea-of-the-day', async (req, res) => {
   }
 });
 
+/**
+ * ✅ Get like counts per day for the past 30 days
+ * URL: /api/ideas/:ideaId/likes-per-day
+ */
+router.get('/:ideaId/likes-per-day', auth, async (req, res) => {
+  try {
+    const { ideaId } = req.params;
+    const startDate = new Date();
+    startDate.setDate(startDate.getDate() - 29);
+    startDate.setHours(0, 0, 0, 0);
+
+    const result = await Idea.aggregate([
+      { $match: { _id: new mongoose.Types.ObjectId(ideaId) } },
+      { $unwind: "$likeTimestamps" },
+      { $match: { "likeTimestamps.likedAt": { $gte: startDate } } },
+      {
+        $group: {
+          _id: {
+            $dateToString: { format: "%Y-%m-%d", date: "$likeTimestamps.likedAt" }
+          },
+          count: { $sum: 1 }
+        }
+      },
+      { $sort: { _id: 1 } }
+    ]);
+
+    const today = new Date();
+    const days = Array.from({ length: 30 }, (_, i) => {
+      const d = new Date(startDate);
+      d.setDate(d.getDate() + i);
+      return d.toISOString().split('T')[0];
+    });
+
+    const resultMap = result.reduce((acc, entry) => {
+      acc[entry._id] = entry.count;
+      return acc;
+    }, {});
+
+    const final = days.map(date => ({ date, count: resultMap[date] || 0 }));
+
+    res.json(final);
+  } catch (err) {
+    console.error('Error fetching like stats:', err);
+    res.status(500).json({ msg: 'Failed to fetch like stats' });
+  }
+});
+
+/**
+ * ✅ Collaboration routes
+ */
 router.post('/collaborate', auth, toggleCollaboration);
 router.get('/:ideaId/collaborators', auth, getCollaborators);
 
@@ -139,4 +183,3 @@ router.put('/:ideaId/revert-collab', async (req, res) => {
 });
 
 module.exports = router;
-
diff --git a/Startup-app/backend/routes/likeRoutes.js b/Startup-app/backend/routes/likeRoutes.js
index 34da26a2..c431cbc1 100644
--- a/Startup-app/backend/routes/likeRoutes.js
+++ b/Startup-app/backend/routes/likeRoutes.js
@@ -8,44 +8,53 @@ router.post('/like', auth, async (req, res) => {
   const { ideaId } = req.body;
   const currentUserId = req.user.id;
 
-  console.log("🔁 Like route hit");
-  console.log("🧠 ideaId:", ideaId);
-  console.log("👤 currentUserId:", currentUserId);
-
   try {
     const idea = await Idea.findById(ideaId);
-    console.log("📌 Fetched Idea:", idea);
 
     if (!idea) {
-      console.log("❌ Idea not found");
       return res.status(404).json({ msg: 'Idea not found' });
     }
 
+    // Initialize likedBy and likeTimestamps if not present
     if (!Array.isArray(idea.likedBy)) {
-      console.log("🔧 likedBy was not an array, initializing...");
       idea.likedBy = [];
     }
 
+    if (!idea.likeTimestamps) {
+      idea.likeTimestamps = new Map();
+    }
+
     const alreadyLiked = idea.likedBy.includes(currentUserId);
-    console.log("✅ Already liked?", alreadyLiked);
+    const today = new Date();
+    const dateKey = today.toISOString().slice(0, 10); // yyyy-mm-dd
 
     if (alreadyLiked) {
+      // 👎 Unlike
       idea.likedBy = idea.likedBy.filter(id => id.toString() !== currentUserId.toString());
       idea.likes = Math.max((idea.likes || 1) - 1, 0);
+
+      const timestamps = idea.likeTimestamps.get(dateKey) || [];
+      const updated = timestamps.filter(
+        ts => new Date(ts).toISOString() !== today.toISOString()
+      );
+      idea.likeTimestamps.set(dateKey, updated);
     } else {
-      idea.likedBy.push(currentUserId.toString());
+      // 👍 Like
+      idea.likedBy.push(currentUserId);
       idea.likes = (idea.likes || 0) + 1;
+
+      const timestamps = idea.likeTimestamps.get(dateKey) || [];
+      timestamps.push(today);
+      idea.likeTimestamps.set(dateKey, timestamps);
     }
 
     await idea.save();
-    console.log("💾 Idea saved");
 
     res.status(200).json({
       msg: alreadyLiked ? 'Idea unliked successfully' : 'Idea liked successfully',
       likes: idea.likes,
       likedBy: idea.likedBy
     });
-
   } catch (error) {
     console.error('🔥 Error toggling like:', error);
     return res.status(500).json({ msg: 'Server error' });
diff --git a/Startup-app/frontend/package-lock.json b/Startup-app/frontend/package-lock.json
index a292113e..26d887a2 100644
--- a/Startup-app/frontend/package-lock.json
+++ b/Startup-app/frontend/package-lock.json
@@ -20,6 +20,7 @@
         "react-icons": "^5.5.0",
         "react-router-dom": "^7.4.0",
         "react-scripts": "^5.0.1",
+        "recharts": "^2.15.3",
         "web-vitals": "^2.1.4"
       }
     },
@@ -3653,6 +3654,69 @@
       "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
       "license": "MIT"
     },
+    "node_modules/@types/d3-array": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
+      "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-color": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+      "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-ease": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+      "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-interpolate": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+      "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-color": "*"
+      }
+    },
+    "node_modules/@types/d3-path": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+      "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-scale": {
+      "version": "4.0.9",
+      "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+      "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-time": "*"
+      }
+    },
+    "node_modules/@types/d3-shape": {
+      "version": "3.1.7",
+      "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
+      "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-path": "*"
+      }
+    },
+    "node_modules/@types/d3-time": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
+      "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-timer": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+      "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
+      "license": "MIT"
+    },
     "node_modules/@types/eslint": {
       "version": "8.56.12",
       "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz",
@@ -5636,6 +5700,15 @@
         "wrap-ansi": "^7.0.0"
       }
     },
+    "node_modules/clsx": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+      "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/co": {
       "version": "4.6.0",
       "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -6372,6 +6445,133 @@
       "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
       "license": "MIT"
     },
+    "node_modules/csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+      "license": "MIT"
+    },
+    "node_modules/d3-array": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+      "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+      "license": "ISC",
+      "dependencies": {
+        "internmap": "1 - 2"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-color": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+      "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-ease": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+      "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-format": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+      "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-interpolate": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+      "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-color": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-path": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+      "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-scale": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+      "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-array": "2.10.0 - 3",
+        "d3-format": "1 - 3",
+        "d3-interpolate": "1.2.0 - 3",
+        "d3-time": "2.1.1 - 3",
+        "d3-time-format": "2 - 4"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-shape": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+      "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-path": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-time": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+      "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-array": "2 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-time-format": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+      "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-time": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-timer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+      "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/damerau-levenshtein": {
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@@ -6466,6 +6666,12 @@
       "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==",
       "license": "MIT"
     },
+    "node_modules/decimal.js-light": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
+      "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
+      "license": "MIT"
+    },
     "node_modules/dedent": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
@@ -6698,6 +6904,16 @@
         "utila": "~0.4"
       }
     },
+    "node_modules/dom-helpers": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+      "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.8.7",
+        "csstype": "^3.0.2"
+      }
+    },
     "node_modules/dom-serializer": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@@ -7938,6 +8154,15 @@
       "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
       "license": "MIT"
     },
+    "node_modules/fast-equals": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
+      "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
     "node_modules/fast-glob": {
       "version": "3.3.3",
       "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@@ -9276,6 +9501,15 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/internmap": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+      "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/ipaddr.js": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
@@ -14110,6 +14344,37 @@
         }
       }
     },
+    "node_modules/react-smooth": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
+      "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
+      "license": "MIT",
+      "dependencies": {
+        "fast-equals": "^5.0.1",
+        "prop-types": "^15.8.1",
+        "react-transition-group": "^4.4.5"
+      },
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+        "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      }
+    },
+    "node_modules/react-transition-group": {
+      "version": "4.4.5",
+      "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+      "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@babel/runtime": "^7.5.5",
+        "dom-helpers": "^5.0.1",
+        "loose-envify": "^1.4.0",
+        "prop-types": "^15.6.2"
+      },
+      "peerDependencies": {
+        "react": ">=16.6.0",
+        "react-dom": ">=16.6.0"
+      }
+    },
     "node_modules/read-cache": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -14145,6 +14410,44 @@
         "node": ">=8.10.0"
       }
     },
+    "node_modules/recharts": {
+      "version": "2.15.3",
+      "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.3.tgz",
+      "integrity": "sha512-EdOPzTwcFSuqtvkDoaM5ws/Km1+WTAO2eizL7rqiG0V2UVhTnz0m7J2i0CjVPUCdEkZImaWvXLbZDS2H5t6GFQ==",
+      "license": "MIT",
+      "dependencies": {
+        "clsx": "^2.0.0",
+        "eventemitter3": "^4.0.1",
+        "lodash": "^4.17.21",
+        "react-is": "^18.3.1",
+        "react-smooth": "^4.0.4",
+        "recharts-scale": "^0.4.4",
+        "tiny-invariant": "^1.3.1",
+        "victory-vendor": "^36.6.8"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+        "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      }
+    },
+    "node_modules/recharts-scale": {
+      "version": "0.4.5",
+      "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
+      "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
+      "license": "MIT",
+      "dependencies": {
+        "decimal.js-light": "^2.4.1"
+      }
+    },
+    "node_modules/recharts/node_modules/react-is": {
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+      "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+      "license": "MIT"
+    },
     "node_modules/recursive-readdir": {
       "version": "2.2.3",
       "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz",
@@ -16198,6 +16501,12 @@
       "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
       "license": "MIT"
     },
+    "node_modules/tiny-invariant": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+      "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
+      "license": "MIT"
+    },
     "node_modules/tmpl": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -16721,6 +17030,28 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/victory-vendor": {
+      "version": "36.9.2",
+      "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
+      "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
+      "license": "MIT AND ISC",
+      "dependencies": {
+        "@types/d3-array": "^3.0.3",
+        "@types/d3-ease": "^3.0.0",
+        "@types/d3-interpolate": "^3.0.1",
+        "@types/d3-scale": "^4.0.2",
+        "@types/d3-shape": "^3.1.0",
+        "@types/d3-time": "^3.0.0",
+        "@types/d3-timer": "^3.0.0",
+        "d3-array": "^3.1.6",
+        "d3-ease": "^3.0.1",
+        "d3-interpolate": "^3.0.1",
+        "d3-scale": "^4.0.2",
+        "d3-shape": "^3.1.0",
+        "d3-time": "^3.0.0",
+        "d3-timer": "^3.0.1"
+      }
+    },
     "node_modules/w3c-hr-time": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
diff --git a/Startup-app/frontend/package.json b/Startup-app/frontend/package.json
index 89860847..cdf16b78 100644
--- a/Startup-app/frontend/package.json
+++ b/Startup-app/frontend/package.json
@@ -15,6 +15,7 @@
     "react-icons": "^5.5.0",
     "react-router-dom": "^7.4.0",
     "react-scripts": "^5.0.1",
+    "recharts": "^2.15.3",
     "web-vitals": "^2.1.4"
   },
   "scripts": {
diff --git a/Startup-app/frontend/src/components/IdeaOfTheDay.jsx b/Startup-app/frontend/src/components/IdeaOfTheDay.jsx
index 23b8abdc..fa0de36b 100644
--- a/Startup-app/frontend/src/components/IdeaOfTheDay.jsx
+++ b/Startup-app/frontend/src/components/IdeaOfTheDay.jsx
@@ -24,7 +24,6 @@ const IdeaOfTheDay = () => {
     fetchIdea();
   }, []);
 
-  // 🎉 Confetti only once per login session
   useEffect(() => {
     const alreadyShown = localStorage.getItem("confettiShown");
 
@@ -46,7 +45,6 @@ const IdeaOfTheDay = () => {
         });
       }, 300);
 
-      // ✅ Mark as shown
       localStorage.setItem("confettiShown", "true");
     }
   }, [idea]);
@@ -66,7 +64,10 @@ const IdeaOfTheDay = () => {
           </div>
 
           <div className="text-purple-600 font-semibold text-sm mt-2">
-            Likes: {idea.likes ?? 0}
+            👍 Likes: {idea.likes ?? 0}
+          </div>
+          <div className="text-purple-600 font-semibold text-sm mt-1">
+            🤝 Collaborators: {idea.collaborators?.length ?? 0}
           </div>
         </div>
 
diff --git a/Startup-app/frontend/src/components/LikesChart.jsx b/Startup-app/frontend/src/components/LikesChart.jsx
new file mode 100644
index 00000000..7fdb0d57
--- /dev/null
+++ b/Startup-app/frontend/src/components/LikesChart.jsx
@@ -0,0 +1,85 @@
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+import {
+  LineChart,
+  Line,
+  XAxis,
+  YAxis,
+  CartesianGrid,
+  Tooltip,
+  ResponsiveContainer,
+} from "recharts";
+import "../styles/LikesChart.css";
+
+const LikesChart = ({ ideaId }) => {
+  const [data, setData] = useState([]);
+  const [loading, setLoading] = useState(true);
+
+  useEffect(() => {
+    const fetchLikes = async () => {
+      try {
+        const token = localStorage.getItem("token");
+        const res = await axios.get(
+          `http://localhost:5001/api/ideas/my-posts`,
+          { headers: { "x-auth-token": token } }
+        );
+
+        const idea = res.data.find((post) => post._id === ideaId);
+        if (!idea || typeof idea.likeTimestamps !== "object") {
+          console.warn("❌ likeTimestamps missing or invalid for idea:", idea);
+          setLoading(false);
+          return;
+        }
+
+        // Flatten all like timestamps from object
+        const timestampsArray = Object.values(idea.likeTimestamps).flat();
+
+        const likeCounts = {};
+        timestampsArray.forEach((timestamp) => {
+          const dateKey = new Date(timestamp).toISOString().split("T")[0];
+          likeCounts[dateKey] = (likeCounts[dateKey] || 0) + 1;
+        });
+
+        const today = new Date();
+        const last30Days = Array.from({ length: 31 }, (_, i) => {
+          const date = new Date();
+          date.setDate(today.getDate() - i);
+          return date.toISOString().split("T")[0];
+        }).reverse();
+
+        const chartData = last30Days.map((date) => ({
+          date,
+          likes: likeCounts[date] || 0,
+        }));
+
+        setData(chartData);
+      } catch (err) {
+        console.error("Error fetching like chart data:", err);
+      } finally {
+        setLoading(false);
+      }
+    };
+
+    fetchLikes();
+  }, [ideaId]);
+
+  if (loading) return <div>Loading chart...</div>;
+
+  return (
+    <div className="likes-chart-popup">
+      <button className="close-btn" onClick={() => window.location.reload()}>×</button>
+      <h3>📊 Likes Over the Last 30 Days</h3>
+      <ResponsiveContainer width="100%" height={300}>
+        <LineChart data={data} margin={{ top: 20, right: 30, left: 0, bottom: 5 }}>
+          <CartesianGrid strokeDasharray="3 3" />
+          <XAxis dataKey="date" />
+          <YAxis allowDecimals={false} />
+          <Tooltip />
+          <Line type="monotone" dataKey="likes" stroke="#8884d8" strokeWidth={2} />
+        </LineChart>
+      </ResponsiveContainer>
+    </div>
+  );
+};
+
+export default LikesChart;
diff --git a/Startup-app/frontend/src/pages/MyPosts.js b/Startup-app/frontend/src/pages/MyPosts.js
index 118bceed..820d281b 100644
--- a/Startup-app/frontend/src/pages/MyPosts.js
+++ b/Startup-app/frontend/src/pages/MyPosts.js
@@ -2,34 +2,16 @@ import React, { useEffect, useState } from 'react';
 import axios from 'axios';
 import { useNavigate } from 'react-router-dom';
 import '../styles/Feed.css';
+import LikesChart from '../components/LikesChart';
 import PostPopup from '../components/PostPopup';
 
 const MyPosts = () => {
   const [posts, setPosts] = useState([]);
   const [selectedPost, setSelectedPost] = useState(null);
+  const [chartPost, setChartPost] = useState(null);
   const [loading, setLoading] = useState(true);
   const [error, setError] = useState('');
   const navigate = useNavigate();
-  const [collaborators, setCollaborators] = useState([]);
-
-  const fetchCollaborators = async (ideaId) => {
-    try {
-        const token = localStorage.getItem('token');
-        const response = await axios.get(
-            `http://localhost:5001/api/ideas/${ideaId}/collaborators`,
-            { headers: { Authorization: `Bearer ${token}` } }
-        );
-        // Update the specific post with its collaborators
-        setPosts((prevPosts) =>
-          prevPosts.map((post) =>
-            post._id === ideaId ? { ...post, collaborators: response.data } : post
-          )
-        );
-        //setCollaborators(response.data);
-    } catch (error) {
-        console.error('Error fetching collaborators:', error.response?.data || error.message);
-    }
-  };
 
   useEffect(() => {
     const fetchMyPosts = async () => {
@@ -41,18 +23,17 @@ const MyPosts = () => {
           }
         });
 
-        // Initialize collaborators as an empty array for each post
         const postsWithCollaborators = await Promise.all(
-          response.data.map(async(post) => {
+          response.data.map(async (post) => {
             const collaboratorsRes = await axios.get(
               `http://localhost:5001/api/ideas/${post._id}/collaborators`,
               { headers: { Authorization: `Bearer ${token}` } }
             );
             return { ...post, collaborators: collaboratorsRes.data };
-        }));
+          })
+        );
 
         setPosts(postsWithCollaborators);
-        //setPosts(response.data);
       } catch (error) {
         setError(error.response?.data?.msg || 'Failed to fetch your posts');
         console.error("Error fetching your posts:", error);
@@ -60,17 +41,32 @@ const MyPosts = () => {
         setLoading(false);
       }
     };
+
     fetchMyPosts();
   }, []);
 
+  const fetchCollaborators = async (ideaId) => {
+    try {
+      const token = localStorage.getItem('token');
+      const response = await axios.get(
+        `http://localhost:5001/api/ideas/${ideaId}/collaborators`,
+        { headers: { Authorization: `Bearer ${token}` } }
+      );
+      setPosts((prevPosts) =>
+        prevPosts.map((post) =>
+          post._id === ideaId ? { ...post, collaborators: response.data } : post
+        )
+      );
+    } catch (error) {
+      console.error('Error fetching collaborators:', error.response?.data || error.message);
+    }
+  };
+
   return (
     <div className="feed-container">
       <div className="feed-header-row">
         <h1>My Posts</h1>
-        <button
-          onClick={() => navigate('/post-idea')}
-          className="small-button"
-        >
+        <button onClick={() => navigate('/post-idea')} className="small-button">
           + Add More
         </button>
       </div>
@@ -82,21 +78,14 @@ const MyPosts = () => {
       ) : posts.length === 0 ? (
         <div className="no-ideas">
           <p>You haven't created any posts yet.</p>
-          <button
-            onClick={() => navigate('/post-idea')}
-            className="small-button"
-          >
+          <button onClick={() => navigate('/post-idea')} className="small-button">
             Create your first post!
           </button>
         </div>
       ) : (
         <div className="feed-grid">
           {posts.map((post) => (
-            <div
-              key={post._id}
-              className="idea-card"
-              onClick={() => setSelectedPost(post)}
-            >
+            <div key={post._id} className="idea-card" onClick={() => setSelectedPost(post)}>
               <div className="idea-header">
                 <h2 className="idea-title">{post.title}</h2>
                 <p className="idea-author">Posted by you</p>
@@ -125,7 +114,6 @@ const MyPosts = () => {
                 <span>Location: {post.location}</span>
               </div>
 
-              {/* ✅ Styled list of users who liked the post */}
               {post.likedBy?.length > 0 && (
                 <div className="idea-meta liked-by-meta">
                   <span>Liked by:</span>
@@ -138,21 +126,47 @@ const MyPosts = () => {
               )}
 
               <button
-                  className="collaborators-btn"
-                  onClick={(e) => {
-                      e.stopPropagation();
-                      fetchCollaborators(post._id);
-                  }}
+                className="collaborators-btn"
+                onClick={(e) => {
+                  e.stopPropagation();
+                  fetchCollaborators(post._id);
+                }}
               >
-                  Collaborators ({post.collaborators?.length || 0}) 
+                Collaborators ({post.collaborators?.length || 0})
               </button>
-              {post.collaborators?.length > 0 ? (
-                  <ul>
-                      {post.collaborators.map((collaborator) => (
-                          <li key={collaborator._id}>{collaborator.name || collaborator.email}</li>
-                      ))}
-                  </ul>
-              ) : null}
+
+              {post.collaborators?.length > 0 && (
+                <ul>
+                  {post.collaborators.map((collaborator) => (
+                    <li key={collaborator._id}>
+                      {collaborator.name || collaborator.email}
+                    </li>
+                  ))}
+                </ul>
+              )}
+
+              {/* ✅ Track Likes Button */}
+              <button
+  className="track-likes-btn"
+  style={{
+    marginTop: '10px',
+    backgroundColor: '#6c63ff',          // Deep purple background
+    color: 'white',                      // White text
+    fontWeight: 'bold',
+    padding: '8px 12px',
+    borderRadius: '8px',
+    border: 'none',
+    cursor: 'pointer',
+    boxShadow: '0 2px 5px rgba(0,0,0,0.1)'
+  }}
+  onClick={(e) => {
+    e.stopPropagation();
+    setChartPost(post);
+  }}
+>
+  📈 Track Likes
+</button>
+
             </div>
           ))}
         </div>
@@ -162,11 +176,22 @@ const MyPosts = () => {
         <PostPopup
           post={selectedPost}
           onClose={() => setSelectedPost(null)}
-          onAuthorClick={() => {}} // no navigation needed for own posts
+          onAuthorClick={() => {}}
         />
       )}
+
+      {chartPost && (
+        <div className="custom-popup">
+          <div className="custom-popup-inner" style={{ width: '80%', maxWidth: '900px' }}>
+            <button className="popup-close" onClick={() => setChartPost(null)}>
+              ×
+            </button>
+            <LikesChart ideaId={chartPost._id} />
+          </div>
+        </div>
+      )}
     </div>
   );
 };
 
-export default MyPosts;
\ No newline at end of file
+export default MyPosts;
diff --git a/Startup-app/frontend/src/styles/LikesChart.css b/Startup-app/frontend/src/styles/LikesChart.css
new file mode 100644
index 00000000..39e05ca0
--- /dev/null
+++ b/Startup-app/frontend/src/styles/LikesChart.css
@@ -0,0 +1,35 @@
+.likes-chart-popup {
+    position: fixed;
+    top: 10%;
+    left: 50%;
+    transform: translateX(-50%);
+    background: white;
+    padding: 20px;
+    border-radius: 12px;
+    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
+    z-index: 1000;
+    width: 90%;
+    max-width: 600px;
+  }
+  
+  .likes-chart-popup h3 {
+    text-align: center;
+    margin-bottom: 16px;
+  }
+  
+  .likes-chart-popup .close-btn {
+    background: #ff5c5c;
+    border: none;
+    color: white;
+    padding: 6px 10px;
+    border-radius: 6px;
+    cursor: pointer;
+    position: absolute;
+    right: 12px;
+    top: 12px;
+  }
+  
+  .likes-chart-popup .close-btn:hover {
+    background: #e74c3c;
+  }
+  
\ No newline at end of file
-- 
GitLab